",{"class":"ganttview-grid-row"});for(var o in t)for(var n in t[o])for(var i in t[o][n]){var r=jQuery("
",{"class":"ganttview-grid-row-cell"});this.options.showWeekends&&this.isWeekend(t[o][n][i])&&r.addClass("ganttview-weekend"),a.append(r)}var s=jQuery("div.ganttview-grid-row-cell",a).length*this.options.cellWidth;a.css("width",s+"px"),e.css("width",s+"px");for(var d=0;d
",{"class":"ganttview-blocks"}),e=0;e",{"class":"ganttview-block-container"}));return t},Kanboard.Gantt.prototype.addBlocks=function(t,e){for(var a=jQuery("div.ganttview-blocks div.ganttview-block-container",t),o=0,n=0;n",{"class":"ganttview-block-text"}),l=jQuery("",{"class":"ganttview-block tooltip"+(this.options.allowMoves?" ganttview-block-movable":""),title:this.getBarTooltip(i),css:{width:r*this.options.cellWidth-9+"px","margin-left":s*this.options.cellWidth+"px"}}).append(d);r>=2&&d.append(i.progress),l.data("record",i),this.setBarColor(l,i),jQuery(a[o]).append(l),o+=1}},Kanboard.Gantt.prototype.getVerticalHeaderTooltip=function(t){var e="";if("task"==t.type)e="
"+t.column_title+" ("+t.progress+")
"+t.title;else{var a=["project-manager","project-member"];for(var o in a){var n=a[o];if(!jQuery.isEmptyObject(t.users[n])){var i=jQuery("
',data:function(){return{content:""}},ready:function(){var t=this;$.ajax({cache:!1,url:this.url,success:function(e){t.content=e}})}}),Vue.component("submit-cancel",{props:["labelButton","labelOr","labelCancel","callback"],template:'',data:function(){return{loading:!1}},computed:{isLoading:function(){return this.loading}},methods:{onSubmit:function(){this.loading=!0,this.callback()},onCancel:function(){_KB.get("Popover").close()}},events:{submitCancelled:function(){this.loading=!1}}}),Vue.component("task-move-position",{props:["board","saveUrl"],template:"#template-task-move-position",data:function(){return{swimlaneId:0,columnId:0,position:1,columns:[],tasks:[],positionChoice:"before",errorMessage:""}},ready:function(){this.columns=this.board[0].columns,this.columnId=this.columns[0].id,this.tasks=this.columns[0].tasks,this.errorMessage=""},methods:{onChangeSwimlane:function(){var t=this;this.columnId=0,this.position=1,this.columns=[],this.tasks=[],this.positionChoice="before",this.board.forEach(function(e){e.id===t.swimlaneId&&(t.columns=e.columns,t.tasks=t.columns[0].tasks,t.columnId=t.columns[0].id)})},onChangeColumn:function(){var t=this;this.position=1,this.tasks=[],this.positionChoice="before",this.columns.forEach(function(e){e.id==t.columnId&&(t.tasks=e.tasks,t.tasks.length>0&&(t.position=parseInt(t.tasks[0].position)))})},onSubmit:function(){var t=this;"after"==this.positionChoice&&this.position++,$.ajax({cache:!1,url:this.saveUrl,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:this.columnId,swimlane_id:this.swimlaneId,position:this.position}),statusCode:{200:function(){window.location.reload(!0)},403:function(e){var a=JSON.parse(e.responseText);t.errorMessage=a.message,t.$broadcast("submitCancelled")}}})}}}),KB.component("text-editor",function(t,e){function a(){var t=KB.el("div").attr("class","text-editor-toolbar")["for"]("a",[{href:"#",html:' '+e.labelWrite,click:function(){n()}}]).build();return m=KB.el("div").attr("class","text-editor-preview-area markdown").build(),KB.el("div").attr("class","text-editor-view-mode").add(t).add(m).hide().build()}function o(){var t=KB.el("div").attr("class","text-editor-toolbar")["for"]("a",[{href:"#",html:' '+e.labelPreview,click:function(){n()}},{href:"#",html:'',click:function(){s("**")}},{href:"#",html:'',click:function(){s("_")}},{href:"#",html:'',click:function(){s("~~")}},{href:"#",html:'',click:function(){l("> ")}},{href:"#",html:'',click:function(){l("* ")}},{href:"#",html:'',click:function(){d("```")}}]).build();return u=KB.el("textarea").attr("name",e.name).attr("tabindex",e.tabindex||"-1").attr("required",e.required||!1).attr("autofocus",e.autofocus||null).attr("placeholder",e.placeholder||"").text(e.text).build(),KB.el("div").attr("class","text-editor-write-mode").add(t).add(u).build()}function n(){KB.el(m).html(marked(u.value,{sanitize:!0})),KB.el(h).toggle(),KB.el(f).toggle()}function i(){return u.value.substring(u.selectionStart,u.selectionEnd)}function r(t,e,a,o){return t.substring(0,e)+o+t.substring(a)}function s(t){var e=i();c(t+e+t),p(t)}function d(t){var e=i();c("\n"+t+"\n"+e+"\n"+t),p(t,2)}function l(t){var e=i();if(e.indexOf("\n")===-1)c("\n"+t+e);else{for(var a=e.split("\n"),o=0;o' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '
' +
- '' +
- '' +
- '
' +
- '{{{ renderedText }}}
' +
- ''
- ,
- data: function() {
- return {
- id: null,
- preview: false,
- renderedText: '',
- textarea: null,
- selectionStart: 0,
- selectionEnd: 0
- };
- },
- ready: function() {
- this.textarea = document.getElementById(this.id);
- },
- computed: {
- hasAutofocus: function() {
- return this.autofocus === '1';
- },
- isPreview: function() {
- return this.preview;
- },
- getId: function() {
- if (! this.id) {
- var i = 0;
- var uniqueId;
+KB.component('text-editor', function (containerElement, options) {
+ var textarea, viewModeElement, writeModeElement, previewElement, selectionStart, selectionEnd;
- while (true) {
- i++;
- uniqueId = 'text-editor-textarea-' + i;
+ this.render = function() {
+ writeModeElement = buildWriteMode();
+ viewModeElement = buildViewMode();
- if (! document.getElementById(uniqueId)) {
- break;
- }
+ containerElement.appendChild(KB.el('div')
+ .attr('class', 'text-editor')
+ .add(viewModeElement)
+ .add(writeModeElement)
+ .build());
+ };
+
+ function buildViewMode() {
+ var toolbarElement = KB.el('div')
+ .attr('class', 'text-editor-toolbar')
+ .for('a', [
+ {href: '#', html: '
' + options.labelWrite, click: function() { toggleViewMode(); }}
+ ])
+ .build();
+
+ previewElement = KB.el('div')
+ .attr('class', 'text-editor-preview-area markdown')
+ .build();
+
+ return KB.el('div')
+ .attr('class', 'text-editor-view-mode')
+ .add(toolbarElement)
+ .add(previewElement)
+ .hide()
+ .build();
+ }
+
+ function buildWriteMode() {
+ var toolbarElement = KB.el('div')
+ .attr('class', 'text-editor-toolbar')
+ .for('a', [
+ {href: '#', html: '
' + options.labelPreview, click: function() { toggleViewMode(); }},
+ {href: '#', html: '
', click: function() { insertEnclosedTag('**'); }},
+ {href: '#', html: '
', click: function() { insertEnclosedTag('_'); }},
+ {href: '#', html: '
', click: function() { insertEnclosedTag('~~'); }},
+ {href: '#', html: '
', click: function() { insertPrependTag('> '); }},
+ {href: '#', html: '
', click: function() { insertPrependTag('* '); }},
+ {href: '#', html: '
', click: function() { insertBlockTag('```'); }}
+ ])
+ .build();
+
+ textarea = KB.el('textarea')
+ .attr('name', options.name)
+ .attr('tabindex', options.tabindex || '-1')
+ .attr('required', options.required || false)
+ .attr('autofocus', options.autofocus || null)
+ .attr('placeholder', options.placeholder || '')
+ .text(options.text)
+ .build();
+
+ return KB.el('div')
+ .attr('class', 'text-editor-write-mode')
+ .add(toolbarElement)
+ .add(textarea)
+ .build();
+ }
+
+ function toggleViewMode() {
+ KB.el(previewElement).html(marked(textarea.value, {sanitize: true}));
+ KB.el(viewModeElement).toggle();
+ KB.el(writeModeElement).toggle();
+ }
+
+ function getSelectedText() {
+ return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
+ }
+
+ function replaceTextRange(s, start, end, substitute) {
+ return s.substring(0, start) + substitute + s.substring(end);
+ }
+
+ function insertEnclosedTag(tag) {
+ var selectedText = getSelectedText();
+
+ insertText(tag + selectedText + tag);
+ setCursorBeforeClosingTag(tag);
+ }
+
+ function insertBlockTag(tag) {
+ var selectedText = getSelectedText();
+
+ insertText('\n' + tag + '\n' + selectedText + '\n' + tag);
+ setCursorBeforeClosingTag(tag, 2);
+ }
+
+ function insertPrependTag(tag) {
+ var selectedText = getSelectedText();
+
+ if (selectedText.indexOf('\n') === -1) {
+ insertText('\n' + tag + selectedText);
+ } else {
+ var lines = selectedText.split('\n');
+
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].indexOf(tag) === -1) {
+ lines[i] = tag + lines[i];
}
-
- this.id = uniqueId;
}
- return this.id;
- }
- },
- methods: {
- toggleEditor: function() {
- this.preview = false;
- },
- togglePreview: function() {
- this.preview = true;
- this.renderedText = marked(this.text, {sanitize: true});
- },
- insertBoldTag: function() {
- this.insertEnclosedTag('**');
- },
- insertItalicTag: function() {
- this.insertEnclosedTag('_');
- },
- insertStrikethroughTag: function() {
- this.insertEnclosedTag('~~');
- },
- insertQuoteTag: function() {
- this.insertPrependTag('> ');
- },
- insertBulletListTag: function() {
- this.insertPrependTag('* ');
- },
- insertCodeTag: function() {
- this.insertBlockTag('```');
- },
- replaceTextRange: function(s, start, end, substitute) {
- return s.substring(0, start) + substitute + s.substring(end);
- },
- getSelectedText: function() {
- return this.text.substring(this.textarea.selectionStart, this.textarea.selectionEnd);
- },
- insertEnclosedTag: function(tag) {
- var selectedText = this.getSelectedText();
-
- this.insertText(tag + selectedText + tag);
- this.setCursorBeforeClosingTag(tag);
- },
- insertPrependTag: function(tag) {
- var selectedText = this.getSelectedText();
-
- if (selectedText.indexOf('\n') === -1) {
- this.insertText('\n' + tag + selectedText);
- } else {
- var lines = selectedText.split('\n');
-
- for (var i = 0; i < lines.length; i++) {
- if (lines[i].indexOf(tag) === -1) {
- lines[i] = tag + lines[i];
- }
- }
-
- this.insertText(lines.join('\n'));
- }
- },
- insertBlockTag: function(tag) {
- var selectedText = this.getSelectedText();
-
- this.insertText('\n' + tag + '\n' + selectedText + '\n' + tag);
- this.setCursorBeforeClosingTag(tag, 2);
- },
- insertText: function(replacedText) {
- var result = false;
-
- this.selectionStart = this.textarea.selectionStart;
- this.selectionEnd = this.textarea.selectionEnd;
- this.textarea.focus();
-
- if (document.queryCommandSupported('insertText')) {
- result = document.execCommand('insertText', false, replacedText);
- }
-
- if (! result) {
- try {
- document.execCommand("ms-beginUndoUnit");
- } catch (error) {}
-
- this.textarea.value = this.replaceTextRange(this.text, this.textarea.selectionStart, this.textarea.selectionEnd, replacedText);
-
- try {
- document.execCommand("ms-endUndoUnit");
- } catch (error) {}
- }
- },
- setCursorBeforeClosingTag: function(tag, offset) {
- var position = this.selectionEnd + tag.length + offset;
- this.textarea.setSelectionRange(position, position);
+ insertText(lines.join('\n'));
}
}
+
+ function insertText(replacedText) {
+ var result = false;
+
+ selectionStart = textarea.selectionStart;
+ selectionEnd = textarea.selectionEnd;
+ textarea.focus();
+
+ if (document.queryCommandSupported('insertText')) {
+ result = document.execCommand('insertText', false, replacedText);
+ }
+
+ if (! result) {
+ try {
+ document.execCommand('ms-beginUndoUnit');
+ } catch (error) {}
+
+ textarea.value = replaceTextRange(text, textarea.selectionStart, textarea.selectionEnd, replacedText);
+
+ try {
+ document.execCommand('ms-endUndoUnit');
+ } catch (error) {}
+ }
+ }
+
+ function setCursorBeforeClosingTag(tag, offset) {
+ offset = offset || 0;
+ var position = selectionEnd + tag.length + offset;
+ textarea.setSelectionRange(position, position);
+ }
});
diff --git a/assets/js/src/Bootstrap.js b/assets/js/src/Bootstrap.js
index 94b1f2ca5..7f722b04a 100644
--- a/assets/js/src/Bootstrap.js
+++ b/assets/js/src/Bootstrap.js
@@ -3,4 +3,6 @@ var _KB = null;
jQuery(document).ready(function() {
_KB = new Kanboard.App();
_KB.execute();
+
+ KB.render();
});
diff --git a/assets/js/src/Namespace.js b/assets/js/src/Namespace.js
index 6b4b05430..e6ca1ed3a 100644
--- a/assets/js/src/Namespace.js
+++ b/assets/js/src/Namespace.js
@@ -1,3 +1,111 @@
'use strict';
var Kanboard = {};
+
+var KB = {
+ components: {}
+};
+
+KB.component = function (name, object) {
+ this.components[name] = object;
+};
+
+KB.render = function () {
+ for (var name in this.components) {
+ var elementList = document.querySelectorAll('.js-' + name);
+
+ for (var i = 0; i < elementList.length; i++) {
+ var object = this.components[name];
+ var component = new object(elementList[i], JSON.parse(elementList[i].dataset.params));
+ component.render();
+ elementList[i].className = elementList[i].className + '-rendered';
+ }
+ }
+};
+
+KB.el = function (tag) {
+
+ function DOMBuilder(tag) {
+ var element = typeof tag === 'string' ? document.createElement(tag) : tag;
+
+ this.attr = function (attribute, value) {
+ if (value !== null) {
+ element.setAttribute(attribute, value);
+ }
+ return this;
+ };
+
+ this.hide = function () {
+ element.style.display = 'none';
+ return this;
+ };
+
+ this.show = function () {
+ element.style.display = 'block';
+ return this;
+ };
+
+ this.toggle = function () {
+ if (element.style.display === 'none') {
+ this.show();
+ } else{
+ this.hide();
+ }
+
+ return this;
+ };
+
+ this.click = function (callback) {
+ element.onclick = function (e) {
+ e.preventDefault();
+ callback();
+ };
+ return this;
+ };
+
+ this.add = function (node) {
+ element.appendChild(node);
+ return this;
+ };
+
+ this.html = function (html) {
+ element.innerHTML = html;
+ return this;
+ };
+
+ this.text = function (text) {
+ element.appendChild(document.createTextNode(text));
+ return this;
+ };
+
+ this.for = function (tag, list) {
+ for (var i = 0; i < list.length; i++) {
+ var dict = list[i];
+
+ if (typeof dict !== 'object') {
+ element.appendChild(KB.el(tag).text(dict).build());
+ } else {
+ var node = KB.el(tag);
+
+ for (var attribute in dict) {
+ if (attribute in this && typeof this[attribute] === 'function') {
+ node[attribute](dict[attribute]);
+ } else {
+ node.attr(attribute, dict[attribute]);
+ }
+ }
+
+ element.appendChild(node.build());
+ }
+ }
+
+ return this;
+ };
+
+ this.build = function () {
+ return element;
+ };
+ }
+
+ return new DOMBuilder(tag);
+};
diff --git a/assets/js/src/Popover.js b/assets/js/src/Popover.js
index 910c54b03..6e487efa1 100644
--- a/assets/js/src/Popover.js
+++ b/assets/js/src/Popover.js
@@ -160,4 +160,6 @@ Kanboard.Popover.prototype.afterOpen = function() {
new Vue({
el: '#popover-container'
});
+
+ KB.render();
};
diff --git a/assets/sass/_markdown_editor.sass b/assets/sass/_markdown_editor.sass
index 4041a1253..b9279be92 100644
--- a/assets/sass/_markdown_editor.sass
+++ b/assets/sass/_markdown_editor.sass
@@ -1,19 +1,16 @@
@import variables
.text-editor
- button
+ a
font-size: size('normal')
- border: none
color: color('light')
- background: transparent
+ text-decoration: none
+ margin-right: 10px
&:hover
color: link-color('primary')
- cursor: pointer
.text-editor-preview-area
border: 1px solid color('lighter')
width: 400px
height: 200px
overflow: auto
- .text-editor-toolbar
- button:first-child
- padding-left: 0
+ padding: 2px
diff --git a/gulpfile.js b/gulpfile.js
index 75a3e61e9..0ff5b6510 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -7,10 +7,10 @@ var strip = require('gulp-strip-comments');
var src = {
js: [
- 'assets/js/components/*.js',
'assets/js/src/Namespace.js',
'assets/js/src/!(Namespace|Bootstrap|BoardDragAndDrop)*.js',
'assets/js/src/BoardDragAndDrop.js',
+ 'assets/js/components/*.js',
'assets/js/src/Bootstrap.js'
]
};