mirror of
https://github.com/sismics/docs.git
synced 2024-11-25 15:17:57 +01:00
Closes #190: lightweight text editor on description field
This commit is contained in:
parent
abde9b7897
commit
e2548ef6b1
@ -25,7 +25,7 @@ module.exports = function(grunt) {
|
|||||||
options: {
|
options: {
|
||||||
separator: ';'
|
separator: ';'
|
||||||
},
|
},
|
||||||
src: ['src/lib/jquery.js','src/lib/jquery.ui.js','src/lib/underscore.js','src/lib/colorpicker.js', 'src/lib/angular.js', 'src/lib/angular.*.js',
|
src: ['src/lib/jquery.js','src/lib/jquery.ui.js','src/lib/underscore.js','src/lib/colorpicker.js', 'src/lib/pell.js', 'src/lib/angular.js', 'src/lib/angular.*.js',
|
||||||
'dist/app/docs/app.js', 'dist/app/docs/controller/**/*.js', 'dist/app/docs/directive/*.js', 'dist/app/docs/filter/*.js', 'dist/app/docs/service/*.js'],
|
'dist/app/docs/app.js', 'dist/app/docs/controller/**/*.js', 'dist/app/docs/directive/*.js', 'dist/app/docs/filter/*.js', 'dist/app/docs/service/*.js'],
|
||||||
dest: 'dist/docs.js'
|
dest: 'dist/docs.js'
|
||||||
},
|
},
|
||||||
@ -33,7 +33,7 @@ module.exports = function(grunt) {
|
|||||||
options: {
|
options: {
|
||||||
separator: ';'
|
separator: ';'
|
||||||
},
|
},
|
||||||
src: ['src/lib/jquery.js','src/lib/jquery.ui.js','src/lib/underscore.js','src/lib/colorpicker.js', 'src/lib/angular.js', 'src/lib/angular.*.js',
|
src: ['src/lib/jquery.js','src/lib/jquery.ui.js','src/lib/underscore.js','src/lib/colorpicker.js', 'src/lib/pell.js', 'src/lib/angular.js', 'src/lib/angular.*.js',
|
||||||
'dist/app/share/app.js', 'dist/app/share/controller/*.js', 'dist/app/share/directive/*.js', 'dist/app/share/filter/*.js', 'dist/app/share/service/*.js'],
|
'dist/app/share/app.js', 'dist/app/share/controller/*.js', 'dist/app/share/directive/*.js', 'dist/app/share/filter/*.js', 'dist/app/share/service/*.js'],
|
||||||
dest: 'dist/share.js'
|
dest: 'dist/share.js'
|
||||||
},
|
},
|
||||||
|
28
docs-web/src/main/webapp/src/app/docs/directive/Pell.js
Normal file
28
docs-web/src/main/webapp/src/app/docs/directive/Pell.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pell directive.
|
||||||
|
*/
|
||||||
|
angular.module('docs').directive('pellEditor', function ($timeout) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
template: '<div class="pell"></div>',
|
||||||
|
require: 'ngModel',
|
||||||
|
replace: true,
|
||||||
|
link: function (scope, element, attrs, ngModelCtrl) {
|
||||||
|
var editor = pell.init({
|
||||||
|
element: element[0],
|
||||||
|
defaultParagraphSeparator: 'p',
|
||||||
|
onChange: function (html) {
|
||||||
|
$timeout(function () {
|
||||||
|
ngModelCtrl.$setViewValue(html);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ngModelCtrl.$render = function() {
|
||||||
|
editor.content.innerHTML = ngModelCtrl.$viewValue || '';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
@ -1,13 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter converting new lines in <br />.
|
|
||||||
*/
|
|
||||||
angular.module('docs').filter('newline', function() {
|
|
||||||
return function(text) {
|
|
||||||
if (!text) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return text.replace(/\n/g, '<br/>');
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,13 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter converting new lines in <br />.
|
|
||||||
*/
|
|
||||||
angular.module('share').filter('newline', function() {
|
|
||||||
return function(text) {
|
|
||||||
if (!text) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return text.replace(/\n/g, '<br/>');
|
|
||||||
}
|
|
||||||
});
|
|
@ -12,6 +12,7 @@
|
|||||||
<link rel="stylesheet" href="style/bootstrap.css" type="text/css" />
|
<link rel="stylesheet" href="style/bootstrap.css" type="text/css" />
|
||||||
<link rel="stylesheet" href="style/fontawesome.css" type="text/css" />
|
<link rel="stylesheet" href="style/fontawesome.css" type="text/css" />
|
||||||
<link rel="stylesheet" href="style/colorpicker.css" type="text/css" />
|
<link rel="stylesheet" href="style/colorpicker.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="style/pell.css" type="text/css" />
|
||||||
<link rel="stylesheet/less" href="style/main.less" type="text/css" />
|
<link rel="stylesheet/less" href="style/main.less" type="text/css" />
|
||||||
<!-- endref -->
|
<!-- endref -->
|
||||||
<link rel="stylesheet" href="../api/theme/stylesheet" type="text/css" id="theme-stylesheet" />
|
<link rel="stylesheet" href="../api/theme/stylesheet" type="text/css" id="theme-stylesheet" />
|
||||||
@ -32,6 +33,7 @@
|
|||||||
<script src="lib/less.js" type="text/javascript"></script>
|
<script src="lib/less.js" type="text/javascript"></script>
|
||||||
<script src="lib/underscore.js" type="text/javascript"></script>
|
<script src="lib/underscore.js" type="text/javascript"></script>
|
||||||
<script src="lib/colorpicker.js" type="text/javascript"></script>
|
<script src="lib/colorpicker.js" type="text/javascript"></script>
|
||||||
|
<script src="lib/pell.js" type="text/javascript"></script>
|
||||||
<script src="lib/angular.js" type="text/javascript"></script>
|
<script src="lib/angular.js" type="text/javascript"></script>
|
||||||
<script src="lib/angular.translate.js" type="text/javascript"></script>
|
<script src="lib/angular.translate.js" type="text/javascript"></script>
|
||||||
<script src="lib/angular.sanitize.js" type="text/javascript"></script>
|
<script src="lib/angular.sanitize.js" type="text/javascript"></script>
|
||||||
@ -92,7 +94,6 @@
|
|||||||
<script src="app/docs/controller/usergroup/UserProfile.js" type="text/javascript"></script>
|
<script src="app/docs/controller/usergroup/UserProfile.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/usergroup/GroupProfile.js" type="text/javascript"></script>
|
<script src="app/docs/controller/usergroup/GroupProfile.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/service/User.js" type="text/javascript"></script>
|
<script src="app/docs/service/User.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/filter/Newline.js" type="text/javascript"></script>
|
|
||||||
<script src="app/docs/filter/Filesize.js" type="text/javascript"></script>
|
<script src="app/docs/filter/Filesize.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/directive/SelectTag.js" type="text/javascript"></script>
|
<script src="app/docs/directive/SelectTag.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/directive/SelectRelation.js" type="text/javascript"></script>
|
<script src="app/docs/directive/SelectRelation.js" type="text/javascript"></script>
|
||||||
@ -100,6 +101,7 @@
|
|||||||
<script src="app/docs/directive/ImgError.js" type="text/javascript"></script>
|
<script src="app/docs/directive/ImgError.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/directive/Acl.js" type="text/javascript"></script>
|
<script src="app/docs/directive/Acl.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/directive/AclEdit.js" type="text/javascript"></script>
|
<script src="app/docs/directive/AclEdit.js" type="text/javascript"></script>
|
||||||
|
<script src="app/docs/directive/Pell.js" type="text/javascript"></script>
|
||||||
<!-- endref -->
|
<!-- endref -->
|
||||||
</head>
|
</head>
|
||||||
<body translate-cloak ng-cloak>
|
<body translate-cloak ng-cloak>
|
||||||
|
226
docs-web/src/main/webapp/src/lib/pell.js
Normal file
226
docs-web/src/main/webapp/src/lib/pell.js
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||||
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||||
|
(factory((global.pell = {})));
|
||||||
|
}(this, (function (exports) { 'use strict';
|
||||||
|
|
||||||
|
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||||
|
|
||||||
|
var defaultParagraphSeparatorString = 'defaultParagraphSeparator';
|
||||||
|
var formatBlock = 'formatBlock';
|
||||||
|
var addEventListener = function addEventListener(parent, type, listener) {
|
||||||
|
return parent.addEventListener(type, listener);
|
||||||
|
};
|
||||||
|
var appendChild = function appendChild(parent, child) {
|
||||||
|
return parent.appendChild(child);
|
||||||
|
};
|
||||||
|
var createElement = function createElement(tag) {
|
||||||
|
return document.createElement(tag);
|
||||||
|
};
|
||||||
|
var queryCommandState = function queryCommandState(command) {
|
||||||
|
return document.queryCommandState(command);
|
||||||
|
};
|
||||||
|
var queryCommandValue = function queryCommandValue(command) {
|
||||||
|
return document.queryCommandValue(command);
|
||||||
|
};
|
||||||
|
|
||||||
|
var exec = function exec(command) {
|
||||||
|
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
||||||
|
return document.execCommand(command, false, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
var defaultActions = {
|
||||||
|
bold: {
|
||||||
|
icon: '<b>B</b>',
|
||||||
|
title: 'Bold',
|
||||||
|
state: function state() {
|
||||||
|
return queryCommandState('bold');
|
||||||
|
},
|
||||||
|
result: function result() {
|
||||||
|
return exec('bold');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
italic: {
|
||||||
|
icon: '<i>I</i>',
|
||||||
|
title: 'Italic',
|
||||||
|
state: function state() {
|
||||||
|
return queryCommandState('italic');
|
||||||
|
},
|
||||||
|
result: function result() {
|
||||||
|
return exec('italic');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
underline: {
|
||||||
|
icon: '<u>U</u>',
|
||||||
|
title: 'Underline',
|
||||||
|
state: function state() {
|
||||||
|
return queryCommandState('underline');
|
||||||
|
},
|
||||||
|
result: function result() {
|
||||||
|
return exec('underline');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
strikethrough: {
|
||||||
|
icon: '<strike>S</strike>',
|
||||||
|
title: 'Strike-through',
|
||||||
|
state: function state() {
|
||||||
|
return queryCommandState('strikeThrough');
|
||||||
|
},
|
||||||
|
result: function result() {
|
||||||
|
return exec('strikeThrough');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
heading1: {
|
||||||
|
icon: '<b>H<sub>1</sub></b>',
|
||||||
|
title: 'Heading 1',
|
||||||
|
result: function result() {
|
||||||
|
return exec(formatBlock, '<h1>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
heading2: {
|
||||||
|
icon: '<b>H<sub>2</sub></b>',
|
||||||
|
title: 'Heading 2',
|
||||||
|
result: function result() {
|
||||||
|
return exec(formatBlock, '<h2>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
icon: '¶',
|
||||||
|
title: 'Paragraph',
|
||||||
|
result: function result() {
|
||||||
|
return exec(formatBlock, '<p>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
quote: {
|
||||||
|
icon: '“ ”',
|
||||||
|
title: 'Quote',
|
||||||
|
result: function result() {
|
||||||
|
return exec(formatBlock, '<blockquote>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
olist: {
|
||||||
|
icon: '#',
|
||||||
|
title: 'Ordered List',
|
||||||
|
result: function result() {
|
||||||
|
return exec('insertOrderedList');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ulist: {
|
||||||
|
icon: '•',
|
||||||
|
title: 'Unordered List',
|
||||||
|
result: function result() {
|
||||||
|
return exec('insertUnorderedList');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
icon: '</>',
|
||||||
|
title: 'Code',
|
||||||
|
result: function result() {
|
||||||
|
return exec(formatBlock, '<pre>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
line: {
|
||||||
|
icon: '―',
|
||||||
|
title: 'Horizontal Line',
|
||||||
|
result: function result() {
|
||||||
|
return exec('insertHorizontalRule');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
icon: '🔗',
|
||||||
|
title: 'Link',
|
||||||
|
result: function result() {
|
||||||
|
var url = window.prompt('Enter the link URL');
|
||||||
|
if (url) exec('createLink', url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
icon: '📷',
|
||||||
|
title: 'Image',
|
||||||
|
result: function result() {
|
||||||
|
var url = window.prompt('Enter the image URL');
|
||||||
|
if (url) exec('insertImage', url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var defaultClasses = {
|
||||||
|
actionbar: 'pell-actionbar',
|
||||||
|
button: 'pell-button',
|
||||||
|
content: 'pell-content',
|
||||||
|
selected: 'pell-button-selected'
|
||||||
|
};
|
||||||
|
|
||||||
|
var init = function init(settings) {
|
||||||
|
var actions = settings.actions ? settings.actions.map(function (action) {
|
||||||
|
if (typeof action === 'string') return defaultActions[action];else if (defaultActions[action.name]) return _extends({}, defaultActions[action.name], action);
|
||||||
|
return action;
|
||||||
|
}) : Object.keys(defaultActions).map(function (action) {
|
||||||
|
return defaultActions[action];
|
||||||
|
});
|
||||||
|
|
||||||
|
var classes = _extends({}, defaultClasses, settings.classes);
|
||||||
|
|
||||||
|
var defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div';
|
||||||
|
|
||||||
|
var actionbar = createElement('div');
|
||||||
|
actionbar.className = classes.actionbar;
|
||||||
|
appendChild(settings.element, actionbar);
|
||||||
|
|
||||||
|
var content = settings.element.content = createElement('div');
|
||||||
|
content.contentEditable = true;
|
||||||
|
content.className = classes.content;
|
||||||
|
content.oninput = function (_ref) {
|
||||||
|
var firstChild = _ref.target.firstChild;
|
||||||
|
|
||||||
|
if (firstChild && firstChild.nodeType === 3) exec(formatBlock, '<' + defaultParagraphSeparator + '>');else if (content.innerHTML === '<br>') content.innerHTML = '';
|
||||||
|
settings.onChange(content.innerHTML);
|
||||||
|
};
|
||||||
|
content.onkeydown = function (event) {
|
||||||
|
if (event.key === 'Tab') {
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') {
|
||||||
|
setTimeout(function () {
|
||||||
|
return exec(formatBlock, '<' + defaultParagraphSeparator + '>');
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
appendChild(settings.element, content);
|
||||||
|
|
||||||
|
actions.forEach(function (action) {
|
||||||
|
var button = createElement('button');
|
||||||
|
button.className = classes.button;
|
||||||
|
button.innerHTML = action.icon;
|
||||||
|
button.title = action.title;
|
||||||
|
button.setAttribute('type', 'button');
|
||||||
|
button.onclick = function () {
|
||||||
|
return action.result() && content.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (action.state) {
|
||||||
|
var handler = function handler() {
|
||||||
|
return button.classList[action.state() ? 'add' : 'remove'](classes.selected);
|
||||||
|
};
|
||||||
|
addEventListener(content, 'keyup', handler);
|
||||||
|
addEventListener(content, 'mouseup', handler);
|
||||||
|
addEventListener(button, 'click', handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendChild(actionbar, button);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (settings.styleWithCSS) exec('styleWithCSS');
|
||||||
|
exec(defaultParagraphSeparatorString, defaultParagraphSeparator);
|
||||||
|
|
||||||
|
return settings.element;
|
||||||
|
};
|
||||||
|
|
||||||
|
var pell = { exec: exec, init: init };
|
||||||
|
|
||||||
|
exports.exec = exec;
|
||||||
|
exports.init = init;
|
||||||
|
exports['default'] = pell;
|
||||||
|
|
||||||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
|
||||||
|
})));
|
@ -31,8 +31,9 @@
|
|||||||
<div class="form-group" ng-class="{ 'has-error': !documentForm.description.$valid }">
|
<div class="form-group" ng-class="{ 'has-error': !documentForm.description.$valid }">
|
||||||
<label class="col-sm-2 control-label" for="inputDescription">{{ 'document.description' | translate }}</label>
|
<label class="col-sm-2 control-label" for="inputDescription">{{ 'document.description' | translate }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea ng-maxlength="4000" class="form-control" rows="5" id="inputDescription" ng-attr-placeholder="{{ 'document.edit.description_placeholder' | translate }}"
|
<pell-editor name="description" id="inputDescription"
|
||||||
name="description" ng-model="document.description" ng-disabled="fileIsUploading"></textarea>
|
ng-maxlength="4000" ng-model="document.description" ng-disabled="fileIsUploading"></pell-editor>
|
||||||
|
<p class="help-block" ng-show="documentForm.description.$error.maxlength && documentForm.$dirty">{{ 'validation.too_long' | translate }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<p class="well-sm" ng-bind-html="document.description | newline"></p>
|
<p class="well-sm" ng-bind-html="document.description"></p>
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
<dt ng-if="document.subject">{{ 'document.subject' | translate }}</dt>
|
<dt ng-if="document.subject">{{ 'document.subject' | translate }}</dt>
|
||||||
<dd ng-if="document.subject">{{ document.subject }}</dd>
|
<dd ng-if="document.subject">{{ document.subject }}</dd>
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p ng-bind-html="document.description | newline"></p>
|
<p ng-bind-html="document.description"></p>
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
<dt ng-if="document.subject">{{ 'document.subject' | translate }}</dt>
|
<dt ng-if="document.subject">{{ 'document.subject' | translate }}</dt>
|
||||||
<dd ng-if="document.subject">{{ document.subject }}</dd>
|
<dd ng-if="document.subject">{{ document.subject }}</dd>
|
||||||
|
@ -43,7 +43,6 @@
|
|||||||
<script src="app/share/controller/FileView.js" type="text/javascript"></script>
|
<script src="app/share/controller/FileView.js" type="text/javascript"></script>
|
||||||
<script src="app/share/controller/FileModalView.js" type="text/javascript"></script>
|
<script src="app/share/controller/FileModalView.js" type="text/javascript"></script>
|
||||||
<script src="app/share/controller/Footer.js" type="text/javascript"></script>
|
<script src="app/share/controller/Footer.js" type="text/javascript"></script>
|
||||||
<script src="app/share/filter/Newline.js" type="text/javascript"></script>
|
|
||||||
<script src="app/share/filter/Filesize.js" type="text/javascript"></script>
|
<script src="app/share/filter/Filesize.js" type="text/javascript"></script>
|
||||||
<!-- endref -->
|
<!-- endref -->
|
||||||
</head>
|
</head>
|
||||||
|
26
docs-web/src/main/webapp/src/style/pell.css
Normal file
26
docs-web/src/main/webapp/src/style/pell.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
.pell {
|
||||||
|
border: 1px solid rgba(10, 10, 10, 0.1);
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
.pell-content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 300px;
|
||||||
|
outline: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px; }
|
||||||
|
|
||||||
|
.pell-actionbar {
|
||||||
|
background-color: #FFF;
|
||||||
|
border-bottom: 1px solid rgba(10, 10, 10, 0.1); }
|
||||||
|
|
||||||
|
.pell-button {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 30px;
|
||||||
|
outline: 0;
|
||||||
|
width: 30px;
|
||||||
|
vertical-align: bottom; }
|
||||||
|
|
||||||
|
.pell-button-selected {
|
||||||
|
background-color: #F0F0F0; }
|
Loading…
Reference in New Issue
Block a user