From 8df1dc19f2ac90f899b49518faed1eaf271865f9 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 31 Jul 2018 19:27:13 +0200 Subject: [PATCH 01/16] Add Color picker for board title and let it have less space between boards. (Default colors are still working) --- www/kanban/app-kanban.less | 22 +- www/kanban/inner.js | 35 +- www/kanban/jkanban.js | 64 +- www/kanban/jscolor.js | 1855 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1939 insertions(+), 37 deletions(-) create mode 100644 www/kanban/jscolor.js diff --git a/www/kanban/app-kanban.less b/www/kanban/app-kanban.less index ab49cfda5..2985f2fe7 100644 --- a/www/kanban/app-kanban.less +++ b/www/kanban/app-kanban.less @@ -3,8 +3,8 @@ @import (once) "../../customize/src/less2/include/tools.less"; .framework_main( @bg-color: @colortheme_kanban-bg, -@warn-color: @colortheme_kanban-warn, -@color: @colortheme_kanban-color); + @warn-color: @colortheme_kanban-warn, + @color: @colortheme_kanban-color); // body &.cp-app-kanban { @@ -115,39 +115,39 @@ } .kanban-header-yellow { - background: #FC3; + background: #FC3 !important; } .kanban-header-orange { - background: #F91; + background: #F91 !important; } .kanban-header-blue { - background: #0AC; + background: #0AC !important; } .kanban-header-red { - background: #E43; + background: #E43 !important; } .kanban-header-green { - background: #8C4; + background: #8C4 !important; } .kanban-header-purple { - background: #c851ff; + background: #c851ff !important; } .kanban-header-cyan { - background: #00ffff; + background: #00ffff !important; } .kanban-header-lightgreen { - background: #c3ff5b; + background: #c3ff5b !important; } .kanban-header-lightblue { - background: #adeeff; + background: #adeeff !important; } @media (max-width: @browser_media-medium-screen) { diff --git a/www/kanban/inner.js b/www/kanban/inner.js index 647032548..ad1a3e64f 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -10,6 +10,7 @@ define([ '/common/modes.js', '/customize/messages.js', '/kanban/jkanban.js', + '/kanban/jscolor.js', 'css!/kanban/jkanban.css', ], function ( $, @@ -105,7 +106,7 @@ define([ var kanban = new window.jKanban({ element: '#cp-app-kanban-content', - gutter: '15px', + gutter: '5px', widthBoard: '300px', buttonContent: '❌', colors: COLORS, @@ -209,22 +210,26 @@ define([ verbose("in color click"); var board = $(el.parentNode).attr("data-id"); var boardJSON = kanban.getBoardJSON(board); + var onchange = function (colorL) { + var elL = el; + var boardL = $(elL.parentNode).attr("data-id"); + var boardJSONL = kanban.getBoardJSON(boardL); + var currentColor = boardJSONL.color; + verbose("Current color " + currentColor); + if (currentColor !== colorL.toString()) { + $(elL).removeClass("kanban-header-" + currentColor); + boardJSONL.color = colorL.toString(); + kanban.onChange(); + } + }; + var jscolorL; + el._jscLinkedInstance = undefined; + jscolorL = new jscolor(el,{onFineChange: onchange, valueElement:undefined}); + jscolorL.show(); var currentColor = boardJSON.color; - verbose("Current color " + currentColor); - var index = kanban.options.colors.findIndex(function (element) { - return (element === currentColor); - }) + 1; - verbose("Next index " + index); - if (index >= kanban.options.colors.length) { index = 0; } - var nextColor = kanban.options.colors[index]; - verbose("Next color " + nextColor); - boardJSON.color = nextColor; - $(el).removeClass("kanban-header-" + currentColor); - $(el).addClass("kanban-header-" + nextColor); - kanban.onChange(); + jscolorL.fromString(currentColor); }, - buttonClick: function (el, boardId, e) { - e.stopPropagation(); + buttonClick: function (el, boardId) { if (framework.isReadOnly() || framework.isLocked()) { return; } UI.confirm(Messages.kanban_deleteBoard, function (yes) { if (!yes) { return; } diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index 02e395635..e70a5d92e 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -52,6 +52,7 @@ widthBoard: '250px', responsive: '700', colors: ["yellow", "green", "blue", "red", "orange"], + responsivePercentage: false, boards: [], dragBoards: true, addItemButton: false, @@ -85,12 +86,12 @@ //Init Drag Board self.drakeBoard = self.dragula([self.container], { moves: function (el, source, handle, sibling) { - if (self.options.readOnly) { return false; } + if (self.options.readOnly) { return false; } if (!self.options.dragBoards) return false; return (handle.classList.contains('kanban-board-header') || handle.classList.contains('kanban-title-board')); }, accepts: function (el, target, source, sibling) { - if (self.options.readOnly) { return false; } + if (self.options.readOnly) { return false; } return target.classList.contains('kanban-container'); }, revertOnSpill: true, @@ -112,7 +113,7 @@ el.classList.remove('is-moving'); self.options.dropBoard(el, target, source, sibling); if (typeof (el.dropfn) === 'function') - el.dropfn(el, target, source, sibling); + el.dropfn(el, target, source, sibling); el.dropfn(el, target, source, sibling); // TODO: update board object board order console.log("Drop " + $(el).attr("data-id") + " just before " + (sibling ? $(sibling).attr("data-id") : " end ")); @@ -145,15 +146,18 @@ //Init Drag Item self.drake = self.dragula(self.boardContainer, { moves: function (el, source, handle, sibling) { - if (self.options.readOnly) { return false; } + if (self.options.readOnly) { return false; } return handle.classList.contains('kanban-item'); }, accepts: function (el, target, source, sibling) { - if (self.options.readOnly) { return false; } + if (self.options.readOnly) { return false; } return true; }, revertOnSpill: true }) + .on('cancel', function(el, container, source) { + self.enableAllBoards(); + }) .on('drag', function (el, source) { // we need to calculate the position before starting to drag self.dragItemPos = self.findElementPosition(el); @@ -184,7 +188,9 @@ var boardId = source.parentNode.dataset.id; self.options.dragcancelEl(el, boardId); }) - .on('drop', function (el, target, source, sibling) { + .on('drop', function(el, target, source, sibling) { + self.enableAllBoards(); + console.log("In drop"); // TODO: update board object board order @@ -229,7 +235,7 @@ // if (board1==board2 && pos2 0 && allB !== undefined) { + for (var i = 0; i < allB.length; i++) { + allB[i].classList.remove('disabled-board'); + } + } + }; + this.addElement = function (boardID, element) { - // add Element to JSON + // add Element to JSON var boardJSON = __findBoardJSON(boardID); boardJSON.item.push({ title: element.title @@ -272,8 +287,19 @@ return self; }; - this.addBoards = function (boards) { - var boardWidth = self.options.widthBoard; + + this.addBoards = function(boards) { + if (self.options.responsivePercentage) { + self.container.style.width = '100%'; + self.options.gutter = '1%'; + if (window.innerWidth > self.options.responsive) { + var boardWidth = (100 - boards.length * 2) / boards.length; + } else { + var boardWidth = 100 - (boards.length * 2); + } + } else { + var boardWidth = self.options.widthBoard; + } var addButton = self.options.addItemButton; var buttonContent = self.options.buttonContent; @@ -285,12 +311,24 @@ if (self.options.boards !== boards) self.options.boards.push(board); + /*if (!self.options.responsivePercentage) { + //add width to container + if (self.container.style.width === '') { + self.container.style.width = parseInt(boardWidth) + (parseInt(self.options.gutter) * 2) + 'px'; + } else { + self.container.style.width = parseInt(self.container.style.width) + parseInt(boardWidth) + (parseInt(self.options.gutter) * 2) + 'px'; + } + }*/ //create node var boardNode = document.createElement('div'); boardNode.dataset.id = board.id; boardNode.classList.add('kanban-board'); //set style - boardNode.style.width = boardWidth; + if (self.options.responsivePercentage) { + boardNode.style.width = boardWidth + '%'; + } else { + boardNode.style.width = boardWidth; + } boardNode.style.marginLeft = self.options.gutter; boardNode.style.marginRight = self.options.gutter; // header board @@ -303,6 +341,10 @@ headerBoard.classList.add(value); }); if (board.color !== '' && board.color !== undefined) { + headerBoard._jscLinkedInstance = undefined; + jscolorL = new jscolor(headerBoard,{valueElement:undefined}); + jscolorL.fromString(board.color); + headerBoard._jscLinkedInstance = undefined; headerBoard.classList.add("kanban-header-" + board.color); } titleBoard = document.createElement('div'); diff --git a/www/kanban/jscolor.js b/www/kanban/jscolor.js new file mode 100644 index 000000000..c9bfa795b --- /dev/null +++ b/www/kanban/jscolor.js @@ -0,0 +1,1855 @@ +/** + * jscolor - JavaScript Color Picker + * + * @link http://jscolor.com + * @license For open source use: GPLv3 + * For commercial use: JSColor Commercial License + * @author Jan Odvarko + * @version 2.0.5 + * + * See usage examples at http://jscolor.com/examples/ + */ + + +"use strict"; + + +if (!window.jscolor) { window.jscolor = (function () { + + +var jsc = { + + + register : function () { + jsc.attachDOMReadyEvent(jsc.init); + jsc.attachEvent(document, 'mousedown', jsc.onDocumentMouseDown); + jsc.attachEvent(document, 'touchstart', jsc.onDocumentTouchStart); + jsc.attachEvent(window, 'resize', jsc.onWindowResize); + }, + + + init : function () { + if (jsc.jscolor.lookupClass) { + jsc.jscolor.installByClassName(jsc.jscolor.lookupClass); + } + }, + + + tryInstallOnElements : function (elms, className) { + var matchClass = new RegExp('(^|\\s)(' + className + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i'); + + for (var i = 0; i < elms.length; i += 1) { + if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color') { + if (jsc.isColorAttrSupported) { + // skip inputs of type 'color' if supported by the browser + continue; + } + } + var m; + if (!elms[i].jscolor && elms[i].className && (m = elms[i].className.match(matchClass))) { + var targetElm = elms[i]; + var optsStr = null; + + var dataOptions = jsc.getDataAttr(targetElm, 'jscolor'); + if (dataOptions !== null) { + optsStr = dataOptions; + } else if (m[4]) { + optsStr = m[4]; + } + + var opts = {}; + if (optsStr) { + try { + opts = (new Function ('return (' + optsStr + ')'))(); + } catch(eParseError) { + jsc.warn('Error parsing jscolor options: ' + eParseError + ':\n' + optsStr); + } + } + targetElm.jscolor = new jsc.jscolor(targetElm, opts); + } + } + }, + + + isColorAttrSupported : (function () { + var elm = document.createElement('input'); + if (elm.setAttribute) { + elm.setAttribute('type', 'color'); + if (elm.type.toLowerCase() == 'color') { + return true; + } + } + return false; + })(), + + + isCanvasSupported : (function () { + var elm = document.createElement('canvas'); + return !!(elm.getContext && elm.getContext('2d')); + })(), + + + fetchElement : function (mixed) { + return typeof mixed === 'string' ? document.getElementById(mixed) : mixed; + }, + + + isElementType : function (elm, type) { + return elm.nodeName.toLowerCase() === type.toLowerCase(); + }, + + + getDataAttr : function (el, name) { + var attrName = 'data-' + name; + var attrValue = el.getAttribute(attrName); + if (attrValue !== null) { + return attrValue; + } + return null; + }, + + + attachEvent : function (el, evnt, func) { + if (el.addEventListener) { + el.addEventListener(evnt, func, false); + } else if (el.attachEvent) { + el.attachEvent('on' + evnt, func); + } + }, + + + detachEvent : function (el, evnt, func) { + if (el.removeEventListener) { + el.removeEventListener(evnt, func, false); + } else if (el.detachEvent) { + el.detachEvent('on' + evnt, func); + } + }, + + + _attachedGroupEvents : {}, + + + attachGroupEvent : function (groupName, el, evnt, func) { + if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) { + jsc._attachedGroupEvents[groupName] = []; + } + jsc._attachedGroupEvents[groupName].push([el, evnt, func]); + jsc.attachEvent(el, evnt, func); + }, + + + detachGroupEvents : function (groupName) { + if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) { + for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) { + var evt = jsc._attachedGroupEvents[groupName][i]; + jsc.detachEvent(evt[0], evt[1], evt[2]); + } + delete jsc._attachedGroupEvents[groupName]; + } + }, + + + attachDOMReadyEvent : function (func) { + var fired = false; + var fireOnce = function () { + if (!fired) { + fired = true; + func(); + } + }; + + if (document.readyState === 'complete') { + setTimeout(fireOnce, 1); // async + return; + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireOnce, false); + + // Fallback + window.addEventListener('load', fireOnce, false); + + } else if (document.attachEvent) { + // IE + document.attachEvent('onreadystatechange', function () { + if (document.readyState === 'complete') { + document.detachEvent('onreadystatechange', arguments.callee); + fireOnce(); + } + }) + + // Fallback + window.attachEvent('onload', fireOnce); + + // IE7/8 + if (document.documentElement.doScroll && window == window.top) { + var tryScroll = function () { + if (!document.body) { return; } + try { + document.documentElement.doScroll('left'); + fireOnce(); + } catch (e) { + setTimeout(tryScroll, 1); + } + }; + tryScroll(); + } + } + }, + + + warn : function (msg) { + if (window.console && window.console.warn) { + window.console.warn(msg); + } + }, + + + preventDefault : function (e) { + if (e.preventDefault) { e.preventDefault(); } + e.returnValue = false; + }, + + + captureTarget : function (target) { + // IE + if (target.setCapture) { + jsc._capturedTarget = target; + jsc._capturedTarget.setCapture(); + } + }, + + + releaseTarget : function () { + // IE + if (jsc._capturedTarget) { + jsc._capturedTarget.releaseCapture(); + jsc._capturedTarget = null; + } + }, + + + fireEvent : function (el, evnt) { + if (!el) { + return; + } + if (document.createEvent) { + var ev = document.createEvent('HTMLEvents'); + ev.initEvent(evnt, true, true); + el.dispatchEvent(ev); + } else if (document.createEventObject) { + var ev = document.createEventObject(); + el.fireEvent('on' + evnt, ev); + } else if (el['on' + evnt]) { // alternatively use the traditional event model + el['on' + evnt](); + } + }, + + + classNameToList : function (className) { + return className.replace(/^\s+|\s+$/g, '').split(/\s+/); + }, + + + // The className parameter (str) can only contain a single class name + hasClass : function (elm, className) { + if (!className) { + return false; + } + return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' '); + }, + + + // The className parameter (str) can contain multiple class names separated by whitespace + setClass : function (elm, className) { + var classList = jsc.classNameToList(className); + for (var i = 0; i < classList.length; i += 1) { + if (!jsc.hasClass(elm, classList[i])) { + elm.className += (elm.className ? ' ' : '') + classList[i]; + } + } + }, + + + // The className parameter (str) can contain multiple class names separated by whitespace + unsetClass : function (elm, className) { + var classList = jsc.classNameToList(className); + for (var i = 0; i < classList.length; i += 1) { + var repl = new RegExp( + '^\\s*' + classList[i] + '\\s*|' + + '\\s*' + classList[i] + '\\s*$|' + + '\\s+' + classList[i] + '(\\s+)', + 'g' + ); + elm.className = elm.className.replace(repl, '$1'); + } + }, + + + getStyle : function (elm) { + return window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle; + }, + + + setStyle : (function () { + var helper = document.createElement('div'); + var getSupportedProp = function (names) { + for (var i = 0; i < names.length; i += 1) { + if (names[i] in helper.style) { + return names[i]; + } + } + }; + var props = { + borderRadius: getSupportedProp(['borderRadius', 'MozBorderRadius', 'webkitBorderRadius']), + boxShadow: getSupportedProp(['boxShadow', 'MozBoxShadow', 'webkitBoxShadow']) + }; + return function (elm, prop, value) { + switch (prop.toLowerCase()) { + case 'opacity': + var alphaOpacity = Math.round(parseFloat(value) * 100); + elm.style.opacity = value; + elm.style.filter = 'alpha(opacity=' + alphaOpacity + ')'; + break; + default: + elm.style[props[prop]] = value; + break; + } + }; + })(), + + + setBorderRadius : function (elm, value) { + jsc.setStyle(elm, 'borderRadius', value || '0'); + }, + + + setBoxShadow : function (elm, value) { + jsc.setStyle(elm, 'boxShadow', value || 'none'); + }, + + + getElementPos : function (e, relativeToViewport) { + var x=0, y=0; + var rect = e.getBoundingClientRect(); + x = rect.left; + y = rect.top; + if (!relativeToViewport) { + var viewPos = jsc.getViewPos(); + x += viewPos[0]; + y += viewPos[1]; + } + return [x, y]; + }, + + + getElementSize : function (e) { + return [e.offsetWidth, e.offsetHeight]; + }, + + + // get pointer's X/Y coordinates relative to viewport + getAbsPointerPos : function (e) { + if (!e) { e = window.event; } + var x = 0, y = 0; + if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { + // touch devices + x = e.changedTouches[0].clientX; + y = e.changedTouches[0].clientY; + } else if (typeof e.clientX === 'number') { + x = e.clientX; + y = e.clientY; + } + return { x: x, y: y }; + }, + + + // get pointer's X/Y coordinates relative to target element + getRelPointerPos : function (e) { + if (!e) { e = window.event; } + var target = e.target || e.srcElement; + var targetRect = target.getBoundingClientRect(); + + var x = 0, y = 0; + + var clientX = 0, clientY = 0; + if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { + // touch devices + clientX = e.changedTouches[0].clientX; + clientY = e.changedTouches[0].clientY; + } else if (typeof e.clientX === 'number') { + clientX = e.clientX; + clientY = e.clientY; + } + + x = clientX - targetRect.left; + y = clientY - targetRect.top; + return { x: x, y: y }; + }, + + + getViewPos : function () { + var doc = document.documentElement; + return [ + (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), + (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) + ]; + }, + + + getViewSize : function () { + var doc = document.documentElement; + return [ + (window.innerWidth || doc.clientWidth), + (window.innerHeight || doc.clientHeight), + ]; + }, + + + redrawPosition : function () { + + if (jsc.picker && jsc.picker.owner) { + var thisObj = jsc.picker.owner; + + var tp, vp; + + if (thisObj.fixed) { + // Fixed elements are positioned relative to viewport, + // therefore we can ignore the scroll offset + tp = jsc.getElementPos(thisObj.targetElement, true); // target pos + vp = [0, 0]; // view pos + } else { + tp = jsc.getElementPos(thisObj.targetElement); // target pos + vp = jsc.getViewPos(); // view pos + } + + var ts = jsc.getElementSize(thisObj.targetElement); // target size + var vs = jsc.getViewSize(); // view size + var ps = jsc.getPickerOuterDims(thisObj); // picker size + var a, b, c; + switch (thisObj.position.toLowerCase()) { + case 'left': a=1; b=0; c=-1; break; + case 'right':a=1; b=0; c=1; break; + case 'top': a=0; b=1; c=-1; break; + default: a=0; b=1; c=1; break; + } + var l = (ts[b]+ps[b])/2; + + // compute picker position + if (!thisObj.smartPosition) { + var pp = [ + tp[a], + tp[b]+ts[b]-l+l*c + ]; + } else { + var pp = [ + -vp[a]+tp[a]+ps[a] > vs[a] ? + (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : + tp[a], + -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? + (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : + (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) + ]; + } + + var x = pp[a]; + var y = pp[b]; + var positionValue = thisObj.fixed ? 'fixed' : 'absolute'; + var contractShadow = + (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && + (pp[1] + ps[1] < tp[1] + ts[1]); + + jsc._drawPosition(thisObj, x, y, positionValue, contractShadow); + } + }, + + + _drawPosition : function (thisObj, x, y, positionValue, contractShadow) { + var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px + + jsc.picker.wrap.style.position = positionValue; + jsc.picker.wrap.style.left = x + 'px'; + jsc.picker.wrap.style.top = y + 'px'; + + jsc.setBoxShadow( + jsc.picker.boxS, + thisObj.shadow ? + new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : + null); + }, + + + getPickerDims : function (thisObj) { + var displaySlider = !!jsc.getSliderComponent(thisObj); + var dims = [ + 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.width + + (displaySlider ? 2 * thisObj.insetWidth + jsc.getPadToSliderPadding(thisObj) + thisObj.sliderSize : 0), + 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.height + + (thisObj.closable ? 2 * thisObj.insetWidth + thisObj.padding + thisObj.buttonHeight : 0) + ]; + return dims; + }, + + + getPickerOuterDims : function (thisObj) { + var dims = jsc.getPickerDims(thisObj); + return [ + dims[0] + 2 * thisObj.borderWidth, + dims[1] + 2 * thisObj.borderWidth + ]; + }, + + + getPadToSliderPadding : function (thisObj) { + return Math.max(thisObj.padding, 1.5 * (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness)); + }, + + + getPadYComponent : function (thisObj) { + switch (thisObj.mode.charAt(1).toLowerCase()) { + case 'v': return 'v'; break; + } + return 's'; + }, + + + getSliderComponent : function (thisObj) { + if (thisObj.mode.length > 2) { + switch (thisObj.mode.charAt(2).toLowerCase()) { + case 's': return 's'; break; + case 'v': return 'v'; break; + } + } + return null; + }, + + + onDocumentMouseDown : function (e) { + if (!e) { e = window.event; } + var target = e.target || e.srcElement; + + if (target._jscLinkedInstance) { + if (target._jscLinkedInstance.showOnClick) { + target._jscLinkedInstance.show(); + } + } else if (target._jscControlName) { + jsc.onControlPointerStart(e, target, target._jscControlName, 'mouse'); + } else { + // Mouse is outside the picker controls -> hide the color picker! + if (jsc.picker && jsc.picker.owner) { + jsc.picker.owner.hide(); + } + } + }, + + + onDocumentTouchStart : function (e) { + if (!e) { e = window.event; } + var target = e.target || e.srcElement; + + if (target._jscLinkedInstance) { + if (target._jscLinkedInstance.showOnClick) { + target._jscLinkedInstance.show(); + } + } else if (target._jscControlName) { + jsc.onControlPointerStart(e, target, target._jscControlName, 'touch'); + } else { + if (jsc.picker && jsc.picker.owner) { + jsc.picker.owner.hide(); + } + } + }, + + + onWindowResize : function (e) { + jsc.redrawPosition(); + }, + + + onParentScroll : function (e) { + // hide the picker when one of the parent elements is scrolled + if (jsc.picker && jsc.picker.owner) { + jsc.picker.owner.hide(); + } + }, + + + _pointerMoveEvent : { + mouse: 'mousemove', + touch: 'touchmove' + }, + _pointerEndEvent : { + mouse: 'mouseup', + touch: 'touchend' + }, + + + _pointerOrigin : null, + _capturedTarget : null, + + + onControlPointerStart : function (e, target, controlName, pointerType) { + var thisObj = target._jscInstance; + + jsc.preventDefault(e); + jsc.captureTarget(target); + + var registerDragEvents = function (doc, offset) { + jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType], + jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); + jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType], + jsc.onDocumentPointerEnd(e, target, controlName, pointerType)); + }; + + registerDragEvents(document, [0, 0]); + + if (window.parent && window.frameElement) { + var rect = window.frameElement.getBoundingClientRect(); + var ofs = [-rect.left, -rect.top]; + registerDragEvents(window.parent.window.document, ofs); + } + + var abs = jsc.getAbsPointerPos(e); + var rel = jsc.getRelPointerPos(e); + jsc._pointerOrigin = { + x: abs.x - rel.x, + y: abs.y - rel.y + }; + + switch (controlName) { + case 'pad': + // if the slider is at the bottom, move it up + switch (jsc.getSliderComponent(thisObj)) { + case 's': if (thisObj.hsv[1] === 0) { thisObj.fromHSV(null, 100, null); }; break; + case 'v': if (thisObj.hsv[2] === 0) { thisObj.fromHSV(null, null, 100); }; break; + } + jsc.setPad(thisObj, e, 0, 0); + break; + + case 'sld': + jsc.setSld(thisObj, e, 0); + break; + } + + jsc.dispatchFineChange(thisObj); + }, + + + onDocumentPointerMove : function (e, target, controlName, pointerType, offset) { + return function (e) { + var thisObj = target._jscInstance; + switch (controlName) { + case 'pad': + if (!e) { e = window.event; } + jsc.setPad(thisObj, e, offset[0], offset[1]); + jsc.dispatchFineChange(thisObj); + break; + + case 'sld': + if (!e) { e = window.event; } + jsc.setSld(thisObj, e, offset[1]); + jsc.dispatchFineChange(thisObj); + break; + } + } + }, + + + onDocumentPointerEnd : function (e, target, controlName, pointerType) { + return function (e) { + var thisObj = target._jscInstance; + jsc.detachGroupEvents('drag'); + jsc.releaseTarget(); + // Always dispatch changes after detaching outstanding mouse handlers, + // in case some user interaction will occur in user's onchange callback + // that would intrude with current mouse events + jsc.dispatchChange(thisObj); + }; + }, + + + dispatchChange : function (thisObj) { + if (thisObj.valueElement) { + if (jsc.isElementType(thisObj.valueElement, 'input')) { + jsc.fireEvent(thisObj.valueElement, 'change'); + } + } + }, + + + dispatchFineChange : function (thisObj) { + if (thisObj.onFineChange) { + var callback; + if (typeof thisObj.onFineChange === 'string') { + callback = new Function(thisObj.onFineChange); + } else { + callback = thisObj.onFineChange; + } + callback(thisObj); + } + }, + + + setPad : function (thisObj, e, ofsX, ofsY) { + var pointerAbs = jsc.getAbsPointerPos(e); + var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.insetWidth; + var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; + + var xVal = x * (360 / (thisObj.width - 1)); + var yVal = 100 - (y * (100 / (thisObj.height - 1))); + + switch (jsc.getPadYComponent(thisObj)) { + case 's': thisObj.fromHSV(xVal, yVal, null, jsc.leaveSld); break; + case 'v': thisObj.fromHSV(xVal, null, yVal, jsc.leaveSld); break; + } + }, + + + setSld : function (thisObj, e, ofsY) { + var pointerAbs = jsc.getAbsPointerPos(e); + var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; + + var yVal = 100 - (y * (100 / (thisObj.height - 1))); + + switch (jsc.getSliderComponent(thisObj)) { + case 's': thisObj.fromHSV(null, yVal, null, jsc.leavePad); break; + case 'v': thisObj.fromHSV(null, null, yVal, jsc.leavePad); break; + } + }, + + + _vmlNS : 'jsc_vml_', + _vmlCSS : 'jsc_vml_css_', + _vmlReady : false, + + + initVML : function () { + if (!jsc._vmlReady) { + // init VML namespace + var doc = document; + if (!doc.namespaces[jsc._vmlNS]) { + doc.namespaces.add(jsc._vmlNS, 'urn:schemas-microsoft-com:vml'); + } + if (!doc.styleSheets[jsc._vmlCSS]) { + var tags = ['shape', 'shapetype', 'group', 'background', 'path', 'formulas', 'handles', 'fill', 'stroke', 'shadow', 'textbox', 'textpath', 'imagedata', 'line', 'polyline', 'curve', 'rect', 'roundrect', 'oval', 'arc', 'image']; + var ss = doc.createStyleSheet(); + ss.owningElement.id = jsc._vmlCSS; + for (var i = 0; i < tags.length; i += 1) { + ss.addRule(jsc._vmlNS + '\\:' + tags[i], 'behavior:url(#default#VML);'); + } + } + jsc._vmlReady = true; + } + }, + + + createPalette : function () { + + var paletteObj = { + elm: null, + draw: null + }; + + if (jsc.isCanvasSupported) { + // Canvas implementation for modern browsers + + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + var drawFunc = function (width, height, type) { + canvas.width = width; + canvas.height = height; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); + hGrad.addColorStop(0 / 6, '#F00'); + hGrad.addColorStop(1 / 6, '#FF0'); + hGrad.addColorStop(2 / 6, '#0F0'); + hGrad.addColorStop(3 / 6, '#0FF'); + hGrad.addColorStop(4 / 6, '#00F'); + hGrad.addColorStop(5 / 6, '#F0F'); + hGrad.addColorStop(6 / 6, '#F00'); + + ctx.fillStyle = hGrad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); + switch (type.toLowerCase()) { + case 's': + vGrad.addColorStop(0, 'rgba(255,255,255,0)'); + vGrad.addColorStop(1, 'rgba(255,255,255,1)'); + break; + case 'v': + vGrad.addColorStop(0, 'rgba(0,0,0,0)'); + vGrad.addColorStop(1, 'rgba(0,0,0,1)'); + break; + } + ctx.fillStyle = vGrad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }; + + paletteObj.elm = canvas; + paletteObj.draw = drawFunc; + + } else { + // VML fallback for IE 7 and 8 + + jsc.initVML(); + + var vmlContainer = document.createElement('div'); + vmlContainer.style.position = 'relative'; + vmlContainer.style.overflow = 'hidden'; + + var hGrad = document.createElement(jsc._vmlNS + ':fill'); + hGrad.type = 'gradient'; + hGrad.method = 'linear'; + hGrad.angle = '90'; + hGrad.colors = '16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0' + + var hRect = document.createElement(jsc._vmlNS + ':rect'); + hRect.style.position = 'absolute'; + hRect.style.left = -1 + 'px'; + hRect.style.top = -1 + 'px'; + hRect.stroked = false; + hRect.appendChild(hGrad); + vmlContainer.appendChild(hRect); + + var vGrad = document.createElement(jsc._vmlNS + ':fill'); + vGrad.type = 'gradient'; + vGrad.method = 'linear'; + vGrad.angle = '180'; + vGrad.opacity = '0'; + + var vRect = document.createElement(jsc._vmlNS + ':rect'); + vRect.style.position = 'absolute'; + vRect.style.left = -1 + 'px'; + vRect.style.top = -1 + 'px'; + vRect.stroked = false; + vRect.appendChild(vGrad); + vmlContainer.appendChild(vRect); + + var drawFunc = function (width, height, type) { + vmlContainer.style.width = width + 'px'; + vmlContainer.style.height = height + 'px'; + + hRect.style.width = + vRect.style.width = + (width + 1) + 'px'; + hRect.style.height = + vRect.style.height = + (height + 1) + 'px'; + + // Colors must be specified during every redraw, otherwise IE won't display + // a full gradient during a subsequential redraw + hGrad.color = '#F00'; + hGrad.color2 = '#F00'; + + switch (type.toLowerCase()) { + case 's': + vGrad.color = vGrad.color2 = '#FFF'; + break; + case 'v': + vGrad.color = vGrad.color2 = '#000'; + break; + } + }; + + paletteObj.elm = vmlContainer; + paletteObj.draw = drawFunc; + } + + return paletteObj; + }, + + + createSliderGradient : function () { + + var sliderObj = { + elm: null, + draw: null + }; + + if (jsc.isCanvasSupported) { + // Canvas implementation for modern browsers + + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + var drawFunc = function (width, height, color1, color2) { + canvas.width = width; + canvas.height = height; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); + grad.addColorStop(0, color1); + grad.addColorStop(1, color2); + + ctx.fillStyle = grad; + ctx.fillRect(0, 0, canvas.width, canvas.height); + }; + + sliderObj.elm = canvas; + sliderObj.draw = drawFunc; + + } else { + // VML fallback for IE 7 and 8 + + jsc.initVML(); + + var vmlContainer = document.createElement('div'); + vmlContainer.style.position = 'relative'; + vmlContainer.style.overflow = 'hidden'; + + var grad = document.createElement(jsc._vmlNS + ':fill'); + grad.type = 'gradient'; + grad.method = 'linear'; + grad.angle = '180'; + + var rect = document.createElement(jsc._vmlNS + ':rect'); + rect.style.position = 'absolute'; + rect.style.left = -1 + 'px'; + rect.style.top = -1 + 'px'; + rect.stroked = false; + rect.appendChild(grad); + vmlContainer.appendChild(rect); + + var drawFunc = function (width, height, color1, color2) { + vmlContainer.style.width = width + 'px'; + vmlContainer.style.height = height + 'px'; + + rect.style.width = (width + 1) + 'px'; + rect.style.height = (height + 1) + 'px'; + + grad.color = color1; + grad.color2 = color2; + }; + + sliderObj.elm = vmlContainer; + sliderObj.draw = drawFunc; + } + + return sliderObj; + }, + + + leaveValue : 1<<0, + leaveStyle : 1<<1, + leavePad : 1<<2, + leaveSld : 1<<3, + + + BoxShadow : (function () { + var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) { + this.hShadow = hShadow; + this.vShadow = vShadow; + this.blur = blur; + this.spread = spread; + this.color = color; + this.inset = !!inset; + }; + + BoxShadow.prototype.toString = function () { + var vals = [ + Math.round(this.hShadow) + 'px', + Math.round(this.vShadow) + 'px', + Math.round(this.blur) + 'px', + Math.round(this.spread) + 'px', + this.color + ]; + if (this.inset) { + vals.push('inset'); + } + return vals.join(' '); + }; + + return BoxShadow; + })(), + + + // + // Usage: + // var myColor = new jscolor( [, ]) + // + + jscolor : function (targetElement, options) { + + // General options + // + this.value = null; // initial HEX color. To change it later, use methods fromString(), fromHSV() and fromRGB() + this.valueElement = targetElement; // element that will be used to display and input the color code + this.styleElement = targetElement; // element that will preview the picked color using CSS backgroundColor + this.required = true; // whether the associated text can be left empty + this.refine = true; // whether to refine the entered color code (e.g. uppercase it and remove whitespace) + this.hash = false; // whether to prefix the HEX color code with # symbol + this.uppercase = true; // whether to show the color code in upper case + this.onFineChange = null; // called instantly every time the color changes (value can be either a function or a string with javascript code) + this.activeClass = 'jscolor-active'; // class to be set to the target element when a picker window is open on it + this.overwriteImportant = false; // whether to overwrite colors of styleElement using !important + this.minS = 0; // min allowed saturation (0 - 100) + this.maxS = 100; // max allowed saturation (0 - 100) + this.minV = 0; // min allowed value (brightness) (0 - 100) + this.maxV = 100; // max allowed value (brightness) (0 - 100) + + // Accessing the picked color + // + this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100] + this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255] + + // Color Picker options + // + this.width = 181; // width of color palette (in px) + this.height = 101; // height of color palette (in px) + this.showOnClick = true; // whether to display the color picker when user clicks on its target element + this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls + this.position = 'bottom'; // left | right | top | bottom - position relative to the target element + this.smartPosition = true; // automatically change picker position when there is not enough space for it + this.sliderSize = 16; // px + this.crossSize = 8; // px + this.closable = false; // whether to display the Close button + this.closeText = 'Close'; + this.buttonColor = '#000000'; // CSS color + this.buttonHeight = 18; // px + this.padding = 12; // px + this.backgroundColor = '#FFFFFF'; // CSS color + this.borderWidth = 1; // px + this.borderColor = '#BBBBBB'; // CSS color + this.borderRadius = 8; // px + this.insetWidth = 1; // px + this.insetColor = '#BBBBBB'; // CSS color + this.shadow = true; // whether to display shadow + this.shadowBlur = 15; // px + this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color + this.pointerColor = '#4C4C4C'; // px + this.pointerBorderColor = '#FFFFFF'; // px + this.pointerBorderWidth = 1; // px + this.pointerThickness = 2; // px + this.zIndex = 1000; + this.container = null; // where to append the color picker (BODY element by default) + + + for (var opt in options) { + if (options.hasOwnProperty(opt)) { + this[opt] = options[opt]; + } + } + + + this.hide = function () { + if (isPickerOwner()) { + detachPicker(); + } + }; + + + this.show = function () { + drawPicker(); + }; + + + this.redraw = function () { + if (isPickerOwner()) { + drawPicker(); + } + }; + + + this.importColor = function () { + if (!this.valueElement) { + this.exportColor(); + } else { + if (jsc.isElementType(this.valueElement, 'input')) { + if (!this.refine) { + if (!this.fromString(this.valueElement.value, jsc.leaveValue)) { + if (this.styleElement) { + this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; + this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; + this.styleElement.style.color = this.styleElement._jscOrigStyle.color; + } + this.exportColor(jsc.leaveValue | jsc.leaveStyle); + } + } else if (!this.required && /^\s*$/.test(this.valueElement.value)) { + this.valueElement.value = ''; + if (this.styleElement) { + this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; + this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; + this.styleElement.style.color = this.styleElement._jscOrigStyle.color; + } + this.exportColor(jsc.leaveValue | jsc.leaveStyle); + + } else if (this.fromString(this.valueElement.value)) { + // managed to import color successfully from the value -> OK, don't do anything + } else { + this.exportColor(); + } + } else { + // not an input element -> doesn't have any value + this.exportColor(); + } + } + }; + + + this.exportColor = function (flags) { + if (!(flags & jsc.leaveValue) && this.valueElement) { + var value = this.toString(); + if (this.uppercase) { value = value.toUpperCase(); } + if (this.hash) { value = '#' + value; } + + if (jsc.isElementType(this.valueElement, 'input')) { + this.valueElement.value = value; + } else { + this.valueElement.innerHTML = value; + } + } + if (!(flags & jsc.leaveStyle)) { + if (this.styleElement) { + var bgColor = '#' + this.toString(); + var fgColor = this.isLight() ? '#000' : '#FFF'; + + this.styleElement.style.backgroundImage = 'none'; + this.styleElement.style.backgroundColor = bgColor; + this.styleElement.style.color = fgColor; + + if (this.overwriteImportant) { + this.styleElement.setAttribute('style', + 'background: ' + bgColor + ' !important; ' + + 'color: ' + fgColor + ' !important;' + ); + } + } + } + if (!(flags & jsc.leavePad) && isPickerOwner()) { + redrawPad(); + } + if (!(flags & jsc.leaveSld) && isPickerOwner()) { + redrawSld(); + } + }; + + + // h: 0-360 + // s: 0-100 + // v: 0-100 + // + this.fromHSV = function (h, s, v, flags) { // null = don't change + if (h !== null) { + if (isNaN(h)) { return false; } + h = Math.max(0, Math.min(360, h)); + } + if (s !== null) { + if (isNaN(s)) { return false; } + s = Math.max(0, Math.min(100, this.maxS, s), this.minS); + } + if (v !== null) { + if (isNaN(v)) { return false; } + v = Math.max(0, Math.min(100, this.maxV, v), this.minV); + } + + this.rgb = HSV_RGB( + h===null ? this.hsv[0] : (this.hsv[0]=h), + s===null ? this.hsv[1] : (this.hsv[1]=s), + v===null ? this.hsv[2] : (this.hsv[2]=v) + ); + + this.exportColor(flags); + }; + + + // r: 0-255 + // g: 0-255 + // b: 0-255 + // + this.fromRGB = function (r, g, b, flags) { // null = don't change + if (r !== null) { + if (isNaN(r)) { return false; } + r = Math.max(0, Math.min(255, r)); + } + if (g !== null) { + if (isNaN(g)) { return false; } + g = Math.max(0, Math.min(255, g)); + } + if (b !== null) { + if (isNaN(b)) { return false; } + b = Math.max(0, Math.min(255, b)); + } + + var hsv = RGB_HSV( + r===null ? this.rgb[0] : r, + g===null ? this.rgb[1] : g, + b===null ? this.rgb[2] : b + ); + if (hsv[0] !== null) { + this.hsv[0] = Math.max(0, Math.min(360, hsv[0])); + } + if (hsv[2] !== 0) { + this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); + } + this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); + + // update RGB according to final HSV, as some values might be trimmed + var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); + this.rgb[0] = rgb[0]; + this.rgb[1] = rgb[1]; + this.rgb[2] = rgb[2]; + + this.exportColor(flags); + }; + + + this.fromString = function (str, flags) { + var m; + if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) { + // HEX notation + // + + if (m[1].length === 6) { + // 6-char notation + this.fromRGB( + parseInt(m[1].substr(0,2),16), + parseInt(m[1].substr(2,2),16), + parseInt(m[1].substr(4,2),16), + flags + ); + } else { + // 3-char notation + this.fromRGB( + parseInt(m[1].charAt(0) + m[1].charAt(0),16), + parseInt(m[1].charAt(1) + m[1].charAt(1),16), + parseInt(m[1].charAt(2) + m[1].charAt(2),16), + flags + ); + } + return true; + + } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { + var params = m[1].split(','); + var re = /^\s*(\d*)(\.\d+)?\s*$/; + var mR, mG, mB; + if ( + params.length >= 3 && + (mR = params[0].match(re)) && + (mG = params[1].match(re)) && + (mB = params[2].match(re)) + ) { + var r = parseFloat((mR[1] || '0') + (mR[2] || '')); + var g = parseFloat((mG[1] || '0') + (mG[2] || '')); + var b = parseFloat((mB[1] || '0') + (mB[2] || '')); + this.fromRGB(r, g, b, flags); + return true; + } + } + return false; + }; + + + this.toString = function () { + return ( + (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) + + (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) + + (0x100 | Math.round(this.rgb[2])).toString(16).substr(1) + ); + }; + + + this.toHEXString = function () { + return '#' + this.toString().toUpperCase(); + }; + + + this.toRGBString = function () { + return ('rgb(' + + Math.round(this.rgb[0]) + ',' + + Math.round(this.rgb[1]) + ',' + + Math.round(this.rgb[2]) + ')' + ); + }; + + + this.isLight = function () { + return ( + 0.213 * this.rgb[0] + + 0.715 * this.rgb[1] + + 0.072 * this.rgb[2] > + 255 / 2 + ); + }; + + + this._processParentElementsInDOM = function () { + if (this._linkedElementsProcessed) { return; } + this._linkedElementsProcessed = true; + + var elm = this.targetElement; + do { + // If the target element or one of its parent nodes has fixed position, + // then use fixed positioning instead + // + // Note: In Firefox, getComputedStyle returns null in a hidden iframe, + // that's why we need to check if the returned style object is non-empty + var currStyle = jsc.getStyle(elm); + if (currStyle && currStyle.position.toLowerCase() === 'fixed') { + this.fixed = true; + } + + if (elm !== this.targetElement) { + // Ensure to attach onParentScroll only once to each parent element + // (multiple targetElements can share the same parent nodes) + // + // Note: It's not just offsetParents that can be scrollable, + // that's why we loop through all parent nodes + if (!elm._jscEventsAttached) { + jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); + elm._jscEventsAttached = true; + } + } + } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body')); + }; + + + // r: 0-255 + // g: 0-255 + // b: 0-255 + // + // returns: [ 0-360, 0-100, 0-100 ] + // + function RGB_HSV (r, g, b) { + r /= 255; + g /= 255; + b /= 255; + var n = Math.min(Math.min(r,g),b); + var v = Math.max(Math.max(r,g),b); + var m = v - n; + if (m === 0) { return [ null, 0, 100 * v ]; } + var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); + return [ + 60 * (h===6?0:h), + 100 * (m/v), + 100 * v + ]; + } + + + // h: 0-360 + // s: 0-100 + // v: 0-100 + // + // returns: [ 0-255, 0-255, 0-255 ] + // + function HSV_RGB (h, s, v) { + var u = 255 * (v / 100); + + if (h === null) { + return [ u, u, u ]; + } + + h /= 60; + s /= 100; + + var i = Math.floor(h); + var f = i%2 ? h-i : 1-(h-i); + var m = u * (1 - s); + var n = u * (1 - s * f); + switch (i) { + case 6: + case 0: return [u,n,m]; + case 1: return [n,u,m]; + case 2: return [m,u,n]; + case 3: return [m,n,u]; + case 4: return [n,m,u]; + case 5: return [u,m,n]; + } + } + + + function detachPicker () { + jsc.unsetClass(THIS.targetElement, THIS.activeClass); + jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); + delete jsc.picker.owner; + } + + + function drawPicker () { + + // At this point, when drawing the picker, we know what the parent elements are + // and we can do all related DOM operations, such as registering events on them + // or checking their positioning + THIS._processParentElementsInDOM(); + + if (!jsc.picker) { + jsc.picker = { + owner: null, + wrap : document.createElement('div'), + box : document.createElement('div'), + boxS : document.createElement('div'), // shadow area + boxB : document.createElement('div'), // border + pad : document.createElement('div'), + padB : document.createElement('div'), // border + padM : document.createElement('div'), // mouse/touch area + padPal : jsc.createPalette(), + cross : document.createElement('div'), + crossBY : document.createElement('div'), // border Y + crossBX : document.createElement('div'), // border X + crossLY : document.createElement('div'), // line Y + crossLX : document.createElement('div'), // line X + sld : document.createElement('div'), + sldB : document.createElement('div'), // border + sldM : document.createElement('div'), // mouse/touch area + sldGrad : jsc.createSliderGradient(), + sldPtrS : document.createElement('div'), // slider pointer spacer + sldPtrIB : document.createElement('div'), // slider pointer inner border + sldPtrMB : document.createElement('div'), // slider pointer middle border + sldPtrOB : document.createElement('div'), // slider pointer outer border + btn : document.createElement('div'), + btnT : document.createElement('span') // text + }; + + jsc.picker.pad.appendChild(jsc.picker.padPal.elm); + jsc.picker.padB.appendChild(jsc.picker.pad); + jsc.picker.cross.appendChild(jsc.picker.crossBY); + jsc.picker.cross.appendChild(jsc.picker.crossBX); + jsc.picker.cross.appendChild(jsc.picker.crossLY); + jsc.picker.cross.appendChild(jsc.picker.crossLX); + jsc.picker.padB.appendChild(jsc.picker.cross); + jsc.picker.box.appendChild(jsc.picker.padB); + jsc.picker.box.appendChild(jsc.picker.padM); + + jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); + jsc.picker.sldB.appendChild(jsc.picker.sld); + jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); + jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); + jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); + jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); + jsc.picker.box.appendChild(jsc.picker.sldB); + jsc.picker.box.appendChild(jsc.picker.sldM); + + jsc.picker.btn.appendChild(jsc.picker.btnT); + jsc.picker.box.appendChild(jsc.picker.btn); + + jsc.picker.boxB.appendChild(jsc.picker.box); + jsc.picker.wrap.appendChild(jsc.picker.boxS); + jsc.picker.wrap.appendChild(jsc.picker.boxB); + } + + var p = jsc.picker; + + var displaySlider = !!jsc.getSliderComponent(THIS); + var dims = jsc.getPickerDims(THIS); + var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); + var padToSliderPadding = jsc.getPadToSliderPadding(THIS); + var borderRadius = Math.min( + THIS.borderRadius, + Math.round(THIS.padding * Math.PI)); // px + var padCursor = 'crosshair'; + + // wrap + p.wrap.style.clear = 'both'; + p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px'; + p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px'; + p.wrap.style.zIndex = THIS.zIndex; + + // picker + p.box.style.width = dims[0] + 'px'; + p.box.style.height = dims[1] + 'px'; + + p.boxS.style.position = 'absolute'; + p.boxS.style.left = '0'; + p.boxS.style.top = '0'; + p.boxS.style.width = '100%'; + p.boxS.style.height = '100%'; + jsc.setBorderRadius(p.boxS, borderRadius + 'px'); + + // picker border + p.boxB.style.position = 'relative'; + p.boxB.style.border = THIS.borderWidth + 'px solid'; + p.boxB.style.borderColor = THIS.borderColor; + p.boxB.style.background = THIS.backgroundColor; + jsc.setBorderRadius(p.boxB, borderRadius + 'px'); + + // IE hack: + // If the element is transparent, IE will trigger the event on the elements under it, + // e.g. on Canvas or on elements with border + p.padM.style.background = + p.sldM.style.background = + '#FFF'; + jsc.setStyle(p.padM, 'opacity', '0'); + jsc.setStyle(p.sldM, 'opacity', '0'); + + // pad + p.pad.style.position = 'relative'; + p.pad.style.width = THIS.width + 'px'; + p.pad.style.height = THIS.height + 'px'; + + // pad palettes (HSV and HVS) + p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS)); + + // pad border + p.padB.style.position = 'absolute'; + p.padB.style.left = THIS.padding + 'px'; + p.padB.style.top = THIS.padding + 'px'; + p.padB.style.border = THIS.insetWidth + 'px solid'; + p.padB.style.borderColor = THIS.insetColor; + + // pad mouse area + p.padM._jscInstance = THIS; + p.padM._jscControlName = 'pad'; + p.padM.style.position = 'absolute'; + p.padM.style.left = '0'; + p.padM.style.top = '0'; + p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px'; + p.padM.style.height = dims[1] + 'px'; + p.padM.style.cursor = padCursor; + + // pad cross + p.cross.style.position = 'absolute'; + p.cross.style.left = + p.cross.style.top = + '0'; + p.cross.style.width = + p.cross.style.height = + crossOuterSize + 'px'; + + // pad cross border Y and X + p.crossBY.style.position = + p.crossBX.style.position = + 'absolute'; + p.crossBY.style.background = + p.crossBX.style.background = + THIS.pointerBorderColor; + p.crossBY.style.width = + p.crossBX.style.height = + (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; + p.crossBY.style.height = + p.crossBX.style.width = + crossOuterSize + 'px'; + p.crossBY.style.left = + p.crossBX.style.top = + (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px'; + p.crossBY.style.top = + p.crossBX.style.left = + '0'; + + // pad cross line Y and X + p.crossLY.style.position = + p.crossLX.style.position = + 'absolute'; + p.crossLY.style.background = + p.crossLX.style.background = + THIS.pointerColor; + p.crossLY.style.height = + p.crossLX.style.width = + (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px'; + p.crossLY.style.width = + p.crossLX.style.height = + THIS.pointerThickness + 'px'; + p.crossLY.style.left = + p.crossLX.style.top = + (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px'; + p.crossLY.style.top = + p.crossLX.style.left = + THIS.pointerBorderWidth + 'px'; + + // slider + p.sld.style.overflow = 'hidden'; + p.sld.style.width = THIS.sliderSize + 'px'; + p.sld.style.height = THIS.height + 'px'; + + // slider gradient + p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000'); + + // slider border + p.sldB.style.display = displaySlider ? 'block' : 'none'; + p.sldB.style.position = 'absolute'; + p.sldB.style.right = THIS.padding + 'px'; + p.sldB.style.top = THIS.padding + 'px'; + p.sldB.style.border = THIS.insetWidth + 'px solid'; + p.sldB.style.borderColor = THIS.insetColor; + + // slider mouse area + p.sldM._jscInstance = THIS; + p.sldM._jscControlName = 'sld'; + p.sldM.style.display = displaySlider ? 'block' : 'none'; + p.sldM.style.position = 'absolute'; + p.sldM.style.right = '0'; + p.sldM.style.top = '0'; + p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px'; + p.sldM.style.height = dims[1] + 'px'; + p.sldM.style.cursor = 'default'; + + // slider pointer inner and outer border + p.sldPtrIB.style.border = + p.sldPtrOB.style.border = + THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; + + // slider pointer outer border + p.sldPtrOB.style.position = 'absolute'; + p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; + p.sldPtrOB.style.top = '0'; + + // slider pointer middle border + p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; + + // slider pointer spacer + p.sldPtrS.style.width = THIS.sliderSize + 'px'; + p.sldPtrS.style.height = sliderPtrSpace + 'px'; + + // the Close button + function setBtnBorder () { + var insetColors = THIS.insetColor.split(/\s+/); + var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; + p.btn.style.borderColor = outsetColor; + } + p.btn.style.display = THIS.closable ? 'block' : 'none'; + p.btn.style.position = 'absolute'; + p.btn.style.left = THIS.padding + 'px'; + p.btn.style.bottom = THIS.padding + 'px'; + p.btn.style.padding = '0 15px'; + p.btn.style.height = THIS.buttonHeight + 'px'; + p.btn.style.border = THIS.insetWidth + 'px solid'; + setBtnBorder(); + p.btn.style.color = THIS.buttonColor; + p.btn.style.font = '12px sans-serif'; + p.btn.style.textAlign = 'center'; + try { + p.btn.style.cursor = 'pointer'; + } catch(eOldIE) { + p.btn.style.cursor = 'hand'; + } + p.btn.onmousedown = function () { + THIS.hide(); + }; + p.btnT.style.lineHeight = THIS.buttonHeight + 'px'; + p.btnT.innerHTML = ''; + p.btnT.appendChild(document.createTextNode(THIS.closeText)); + + // place pointers + redrawPad(); + redrawSld(); + + // If we are changing the owner without first closing the picker, + // make sure to first deal with the old owner + if (jsc.picker.owner && jsc.picker.owner !== THIS) { + jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass); + } + + // Set the new picker owner + jsc.picker.owner = THIS; + + // The redrawPosition() method needs picker.owner to be set, that's why we call it here, + // after setting the owner + if (jsc.isElementType(container, 'body')) { + jsc.redrawPosition(); + } else { + jsc._drawPosition(THIS, 0, 0, 'relative', false); + } + + if (p.wrap.parentNode != container) { + container.appendChild(p.wrap); + } + + jsc.setClass(THIS.targetElement, THIS.activeClass); + } + + + function redrawPad () { + // redraw the pad pointer + switch (jsc.getPadYComponent(THIS)) { + case 's': var yComponent = 1; break; + case 'v': var yComponent = 2; break; + } + var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1)); + var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); + var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); + var ofs = -Math.floor(crossOuterSize / 2); + jsc.picker.cross.style.left = (x + ofs) + 'px'; + jsc.picker.cross.style.top = (y + ofs) + 'px'; + + // redraw the slider + switch (jsc.getSliderComponent(THIS)) { + case 's': + var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]); + var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]); + var color1 = 'rgb(' + + Math.round(rgb1[0]) + ',' + + Math.round(rgb1[1]) + ',' + + Math.round(rgb1[2]) + ')'; + var color2 = 'rgb(' + + Math.round(rgb2[0]) + ',' + + Math.round(rgb2[1]) + ',' + + Math.round(rgb2[2]) + ')'; + jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); + break; + case 'v': + var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100); + var color1 = 'rgb(' + + Math.round(rgb[0]) + ',' + + Math.round(rgb[1]) + ',' + + Math.round(rgb[2]) + ')'; + var color2 = '#000'; + jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); + break; + } + } + + + function redrawSld () { + var sldComponent = jsc.getSliderComponent(THIS); + if (sldComponent) { + // redraw the slider pointer + switch (sldComponent) { + case 's': var yComponent = 1; break; + case 'v': var yComponent = 2; break; + } + var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); + jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px'; + } + } + + + function isPickerOwner () { + return jsc.picker && jsc.picker.owner === THIS; + } + + + function blurValue () { + THIS.importColor(); + } + + + // Find the target element + if (typeof targetElement === 'string') { + var id = targetElement; + var elm = document.getElementById(id); + if (elm) { + this.targetElement = elm; + } else { + jsc.warn('Could not find target element with ID \'' + id + '\''); + } + } else if (targetElement) { + this.targetElement = targetElement; + } else { + jsc.warn('Invalid target element: \'' + targetElement + '\''); + } + + if (this.targetElement._jscLinkedInstance) { + jsc.warn('Cannot link jscolor twice to the same element. Skipping.'); + return; + } + this.targetElement._jscLinkedInstance = this; + + // Find the value element + this.valueElement = jsc.fetchElement(this.valueElement); + // Find the style element + this.styleElement = jsc.fetchElement(this.styleElement); + + var THIS = this; + var container = + this.container ? + jsc.fetchElement(this.container) : + document.getElementsByTagName('body')[0]; + var sliderPtrSpace = 3; // px + + // For BUTTON elements it's important to stop them from sending the form when clicked + // (e.g. in Safari) + if (jsc.isElementType(this.targetElement, 'button')) { + if (this.targetElement.onclick) { + var origCallback = this.targetElement.onclick; + this.targetElement.onclick = function (evt) { + origCallback.call(this, evt); + return false; + }; + } else { + this.targetElement.onclick = function () { return false; }; + } + } + + /* + var elm = this.targetElement; + do { + // If the target element or one of its offsetParents has fixed position, + // then use fixed positioning instead + // + // Note: In Firefox, getComputedStyle returns null in a hidden iframe, + // that's why we need to check if the returned style object is non-empty + var currStyle = jsc.getStyle(elm); + if (currStyle && currStyle.position.toLowerCase() === 'fixed') { + this.fixed = true; + } + + if (elm !== this.targetElement) { + // attach onParentScroll so that we can recompute the picker position + // when one of the offsetParents is scrolled + if (!elm._jscEventsAttached) { + jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); + elm._jscEventsAttached = true; + } + } + } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body')); + */ + + // valueElement + if (this.valueElement) { + if (jsc.isElementType(this.valueElement, 'input')) { + var updateField = function () { + THIS.fromString(THIS.valueElement.value, jsc.leaveValue); + jsc.dispatchFineChange(THIS); + }; + jsc.attachEvent(this.valueElement, 'keyup', updateField); + jsc.attachEvent(this.valueElement, 'input', updateField); + jsc.attachEvent(this.valueElement, 'blur', blurValue); + this.valueElement.setAttribute('autocomplete', 'off'); + } + } + + // styleElement + if (this.styleElement) { + this.styleElement._jscOrigStyle = { + backgroundImage : this.styleElement.style.backgroundImage, + backgroundColor : this.styleElement.style.backgroundColor, + color : this.styleElement.style.color + }; + } + + if (this.value) { + // Try to set the color from the .value option and if unsuccessful, + // export the current color + this.fromString(this.value) || this.exportColor(); + } else { + this.importColor(); + } + } + +}; + + +//================================ +// Public properties and methods +//================================ + + +// By default, search for all elements with class="jscolor" and install a color picker on them. +// +// You can change what class name will be looked for by setting the property jscolor.lookupClass +// anywhere in your HTML document. To completely disable the automatic lookup, set it to null. +// +jsc.jscolor.lookupClass = 'jscolor'; + + +jsc.jscolor.installByClassName = function (className) { + var inputElms = document.getElementsByTagName('input'); + var buttonElms = document.getElementsByTagName('button'); + + jsc.tryInstallOnElements(inputElms, className); + jsc.tryInstallOnElements(buttonElms, className); +}; + + +jsc.register(); + + +return jsc.jscolor; + + +})(); } From 1bf1253963da21a29d974068b05739a5d8557985 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 31 Jul 2018 20:08:06 +0200 Subject: [PATCH 02/16] Add color picker to items --- www/kanban/inner.js | 2 +- www/kanban/jkanban.js | 148 ++++++++++++++++++++++++------------------ 2 files changed, 87 insertions(+), 63 deletions(-) diff --git a/www/kanban/inner.js b/www/kanban/inner.js index ad1a3e64f..685331f0b 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -137,7 +137,7 @@ define([ // Remove the input $(el).text(name); // Save the value for the correct board - var board = $(el.parentNode.parentNode).attr("data-id"); + var board = $(el.parentNode.parentNode.parentNode).attr("data-id"); var pos = kanban.findElementPosition(el); kanban.getBoardJSON(board).item[pos].title = name; kanban.onChange(); diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index e70a5d92e..9c2a1cc5b 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -31,7 +31,7 @@ * @author: Riccardo Tartaglia */ - //Require dragula + //Require dragula var dragula = require('dragula'); (function () { @@ -85,18 +85,18 @@ //Init Drag Board self.drakeBoard = self.dragula([self.container], { - moves: function (el, source, handle, sibling) { - if (self.options.readOnly) { return false; } - if (!self.options.dragBoards) return false; - return (handle.classList.contains('kanban-board-header') || handle.classList.contains('kanban-title-board')); - }, - accepts: function (el, target, source, sibling) { - if (self.options.readOnly) { return false; } - return target.classList.contains('kanban-container'); - }, - revertOnSpill: true, - direction: 'horizontal', - }) + moves: function (el, source, handle, sibling) { + if (self.options.readOnly) { return false; } + if (!self.options.dragBoards) return false; + return (handle.classList.contains('kanban-board-header') || handle.classList.contains('kanban-title-board')); + }, + accepts: function (el, target, source, sibling) { + if (self.options.readOnly) { return false; } + return target.classList.contains('kanban-container'); + }, + revertOnSpill: true, + direction: 'horizontal', + }) .on('drag', function (el, source) { el.classList.add('is-moving'); self.options.dragBoard(el, source); @@ -145,16 +145,16 @@ //Init Drag Item self.drake = self.dragula(self.boardContainer, { - moves: function (el, source, handle, sibling) { - if (self.options.readOnly) { return false; } - return handle.classList.contains('kanban-item'); - }, - accepts: function (el, target, source, sibling) { - if (self.options.readOnly) { return false; } - return true; - }, - revertOnSpill: true - }) + moves: function (el, source, handle, sibling) { + if (self.options.readOnly) { return false; } + return handle.classList.contains('kanban-item'); + }, + accepts: function (el, target, source, sibling) { + if (self.options.readOnly) { return false; } + return true; + }, + revertOnSpill: true + }) .on('cancel', function(el, container, source) { self.enableAllBoards(); }) @@ -374,14 +374,38 @@ var nodeItem = document.createElement('div'); nodeItem.classList.add('kanban-item'); nodeItem.dataset.eid = itemKanban.id; - nodeItem.innerHTML = itemKanban.title; + var nodeItemText = document.createElement('div'); + nodeItemText.classList.add('kanban-item-text'); + nodeItemText.dataset.eid = itemKanban.id; + nodeItemText.innerHTML = itemKanban.title; + nodeItem.appendChild(nodeItemText); //add function - nodeItem.clickfn = itemKanban.click; - nodeItem.dragfn = itemKanban.drag; - nodeItem.dragendfn = itemKanban.dragend; - nodeItem.dropfn = itemKanban.drop; + nodeItemText.clickfn = itemKanban.click; + nodeItemText.dragfn = itemKanban.drag; + nodeItemText.dragendfn = itemKanban.dragend; + nodeItemText.dropfn = itemKanban.drop; //add click handler of item - __onclickHandler(nodeItem); + __onclickHandler(nodeItemText); + + var onchange = function (colorL) { + var currentColor = itemKanban.color; + if (currentColor !== colorL.toString()) { + itemKanban.color = colorL.toString(); + self.onChange(); + } + }; + + var jscolorL; + nodeItem._jscLinkedInstance = undefined; + jscolorL = new jscolor(nodeItem,{onFineChange: onchange, valueElement:undefined}); + var currentColor = itemKanban.color; + // If not defined dont have it undefined + if (currentColor == undefined) { + currentColor = '' + } + console.log(currentColor); + jscolorL.fromString(currentColor); + contentBoard.appendChild(nodeItem); } //footer board @@ -575,7 +599,7 @@ }()); -}, { + }, { "dragula": 9 }], 2: [function (require, module, exports) { @@ -583,7 +607,7 @@ return Array.prototype.slice.call(a, n); } -}, {}], + }, {}], 3: [function (require, module, exports) { 'use strict'; @@ -598,7 +622,7 @@ }); }; -}, { + }, { "ticky": 10 }], 4: [function (require, module, exports) { @@ -669,7 +693,7 @@ return thing; }; -}, { + }, { "./debounce": 3, "atoa": 2 }], @@ -786,7 +810,7 @@ } }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -}, { + }, { "./eventmap": 6, "custom-event": 7 }], @@ -807,7 +831,7 @@ module.exports = eventmap; }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -}, {}], + }, {}], 7: [function (require, module, exports) { (function (global) { @@ -837,33 +861,33 @@ // IE >= 9 'function' === typeof document.createEvent ? function CustomEvent(type, params) { - var e = document.createEvent('CustomEvent'); - if (params) { - e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail); - } else { - e.initCustomEvent(type, false, false, void 0); - } - return e; - } : - - // IE <= 8 - function CustomEvent(type, params) { - var e = document.createEventObject(); - e.type = type; - if (params) { - e.bubbles = Boolean(params.bubbles); - e.cancelable = Boolean(params.cancelable); - e.detail = params.detail; - } else { - e.bubbles = false; - e.cancelable = false; - e.detail = void 0; + var e = document.createEvent('CustomEvent'); + if (params) { + e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail); + } else { + e.initCustomEvent(type, false, false, void 0); + } + return e; + } : + + // IE <= 8 + function CustomEvent(type, params) { + var e = document.createEventObject(); + e.type = type; + if (params) { + e.bubbles = Boolean(params.bubbles); + e.cancelable = Boolean(params.cancelable); + e.detail = params.detail; + } else { + e.bubbles = false; + e.cancelable = false; + e.detail = void 0; + } + return e; } - return e; - } }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -}, {}], + }, {}], 8: [function (require, module, exports) { 'use strict'; @@ -899,7 +923,7 @@ rm: rmClass }; -}, {}], + }, {}], 9: [function (require, module, exports) { (function (global) { 'use strict'; @@ -1586,7 +1610,7 @@ module.exports = dragula; }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -}, { + }, { "./classes": 8, "contra/emitter": 4, "crossvent": 5 @@ -1605,5 +1629,5 @@ } module.exports = tick; -}, {}] + }, {}] }, {}, [1]); From 343e63f41b838419ce5a1f20b440a36d5ad553ce Mon Sep 17 00:00:00 2001 From: MTRNord Date: Tue, 31 Jul 2018 20:27:29 +0200 Subject: [PATCH 03/16] Fix drag bug --- www/kanban/jkanban.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index 9c2a1cc5b..0ecaf2a22 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -113,7 +113,7 @@ el.classList.remove('is-moving'); self.options.dropBoard(el, target, source, sibling); if (typeof (el.dropfn) === 'function') - el.dropfn(el, target, source, sibling); el.dropfn(el, target, source, sibling); + el.dropfn(el, target, source, sibling); // TODO: update board object board order console.log("Drop " + $(el).attr("data-id") + " just before " + (sibling ? $(sibling).attr("data-id") : " end ")); From d49cccd7401c0572f8e539c74520b815c8179b68 Mon Sep 17 00:00:00 2001 From: b3yond Date: Fri, 7 Sep 2018 00:37:56 +0200 Subject: [PATCH 04/16] Changed strings for automatic pad storage settings --- customize.dist/translations/messages.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index f88f211e3..efe898558 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -572,9 +572,9 @@ define(function () { out.settings_importDone = "Import completed"; out.settings_autostoreTitle = "Pad storage in CryptDrive"; - out.settings_autostoreHint = "Automatic pad storage results in all the pads you visit being stored in your CryptDrive.
" + - "Manual (always ask) results in the pads not being stored but a reminder will appear to ask you if you want to store them in CryptDrive.
" + - "Manual (never ask) results in the pads not being stored and option to store them will be available but in a hidden way."; + out.settings_autostoreHint = "Automatic All the pads you visit are stored in your CryptDrive.
" + + "Manual (always ask) If you have not stored a pad yet, you will be asked if you want to store them in your CryptDrive.
" + + "Manual (never ask) Pads are not stored automatically in your Cryptpad. The option to store them will be hidden."; out.settings_autostoreYes = "Automatic"; out.settings_autostoreNo = "Manual (never ask)"; out.settings_autostoreMaybe = "Manual (always ask)"; From 45097dfe6918ab907c84e70c8b3b597aba71deeb Mon Sep 17 00:00:00 2001 From: b3yond Date: Fri, 7 Sep 2018 00:58:33 +0200 Subject: [PATCH 05/16] German strings for automatic pad storage settings --- customize.dist/translations/messages.de.js | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/customize.dist/translations/messages.de.js b/customize.dist/translations/messages.de.js index 4f70ced16..01113de2d 100644 --- a/customize.dist/translations/messages.de.js +++ b/customize.dist/translations/messages.de.js @@ -567,6 +567,14 @@ define(function () { out.settings_importConfirm = "Bist Du sicher, dass Du die kürzlich besuchte Dokumente in Deinem Konto importieren möchtest??"; out.settings_importDone = "Import erledigt"; + out.settings_autostoreTitle = "Automatisches Speichern im CryptDrive"; + out.settings_autostoreHint = "Automatisch: Alle Pads werden in deinem CryptDrive gespeichert.
" + + "Manuell (immer nachfragen): Wenn du ein Pad noch nicht gespeichert hast, wirst du gefragt, ob du es im CryptDrive speichern willst.
" + + "Manuell (nie nachfragen): Pads werden nicht automatisch im CryptDrive gespeichert. Die Option, sie trotzdem zu speichern, ist versteckt.
"; + out.settings_autostoreYes = "Automatisch"; + out.settings_autostoreNo = "Manuell (nie nachfragen)"; + out.settings_autostoreMaybe = "Manual (immer nachfragen)"; + out.settings_userFeedbackTitle = "Rückmeldung"; out.settings_userFeedbackHint1 = "CryptPad gibt grundlegende Rückmeldungen zum Server, um die Benutzer-Erfahrung zu verbessern können."; out.settings_userFeedbackHint2 = "Der Inhalt deiner Dokumente wird nie mit dem Server geteilt."; @@ -1200,6 +1208,27 @@ define(function () { out.loading_drive_2 = "Aktualisiere Datenformat"; out.loading_drive_3 = "Verifiziere Datenintegrität"; + // Shared folders + out.sharedFolders_forget = "Dieses pad wird nur in einem geteilten Ordner gespeichert, du kannst es nicht in den Papierkorb verschieben. Du kannst es in deinem CryptDrive löschen."; + out.sharedFolders_duplicate = "Einige der pads, die du versucht hast zu verschieben, waren schon im Zielordner geteilt."; + out.sharedFolders_create = "Erstelle einen geteilten Ordner"; + out.sharedFolders_create_name = "Neuer Ordner"; + out.sharedFolders_create_owned = "Eigener Ordner"; + out.sharedFolders_create_password = "Ordnerpasswort"; + out.sharedFolders_share = "Teile diese URL mit anderen registrierten Benutzern, um ihnen Zugriff auf den geteilten Ordner zu geben. Sobald sie diese URL öffnen, wird der geteilte Ordner zu ihrem CryptDrive hinzugefügt."; + + out.chrome68 = "Anscheinend benutzt du Chrome oder Chromium version 68. Darin ist ein bug, der dafür sorgt, dass nach ein paar Sekunden die Seite komplett weiß ist oder nicht mehr auf Klicks reagiert. Um das Problem zu beheben, wechsle den Tab und komme wieder, oder versuche zu scrollen. Dieser Bug sollte in der nächsten Version deines Browsers gefixt sein."; + + // Manual pad storage popup + out.autostore_notstored = "Dieses Pad ist noch nicht in deinem CryptDrive. Willst du es jetzt speichern?"; + out.autostore_settings = "Du kannst automatisches Speichern im CryptDrive in deinen Einstellungen aktivieren!"; + out.autostore_store = "Speichern"; + out.autostore_hide = "Nicht speichern"; + out.autostore_error = "Unerwarteter Fehler: wir konnten das Pad nicht speichern, bitte versuche es nochmal."; + out.autostore_saved = "Das Pad wurde erfolgreich in deinem CryptDrive gespeichert!"; + out.autostore_forceSave = "Speicher die Datei in deinem CryptDrive"; // File upload modal + out.autostore_notAvailable = "Du musst dieses Pad in deinem CryptDrive speichern, bevor du dieses Feature benutzen kannst."; // Properties/tags/move to trash + return out; }); From 64a1f888aca5bf9b417715cc36893a8251a27719 Mon Sep 17 00:00:00 2001 From: Jens Diemer Date: Tue, 11 Sep 2018 17:41:35 +0200 Subject: [PATCH 06/16] Update messages.de.js typo --- customize.dist/translations/messages.de.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customize.dist/translations/messages.de.js b/customize.dist/translations/messages.de.js index 01113de2d..5e036be08 100644 --- a/customize.dist/translations/messages.de.js +++ b/customize.dist/translations/messages.de.js @@ -1115,7 +1115,7 @@ define(function () { out.readme_cat2_l2 = "Der Titel eines Dokuments kann mit einem Klick auf den Stift geändert werden."; out.readme_cat3 = "Entdecke CryptPad Apps"; out.readme_cat3_l1 = "Mit dem CryptPad Codeeditor kannst du Code wie JavaScript, Markdown, oder HTML bearbeiten"; - out.readme_cat3_l2 = "Mit dem CryptPad Präsentationseditor kannst du schnell Vorträge mit Hilfe von Markdwon gestalten"; + out.readme_cat3_l2 = "Mit dem CryptPad Präsentationseditor kannst du schnell Vorträge mit Hilfe von Markdown gestalten"; out.readme_cat3_l3 = "Mit der CryptPad Umfrage kannst du schnell Abstimmungen durchführen, insbesondere, um Meetings zu planen, die in den Kalender von allen passen."; // Tips From cd927aa50702e394349822397d8cbe28fcaa0634 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Thu, 13 Sep 2018 14:32:14 +0200 Subject: [PATCH 07/16] Add jscolor.js to .jshintignore --- .jshintignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.jshintignore b/.jshintignore index c403b39b3..0f9c58970 100644 --- a/.jshintignore +++ b/.jshintignore @@ -16,6 +16,7 @@ www/pad/mediatag-plugin.js www/pad/mediatag-plugin-dialog.js www/kanban/jkanban.js +www/kanban/jscolor.js www/common/media-tag-nacl.min.js From 57ed2636ac35e6b58c8f82d583558b7d643e865f Mon Sep 17 00:00:00 2001 From: MTRNord Date: Thu, 13 Sep 2018 14:39:49 +0200 Subject: [PATCH 08/16] jkanban.js fix whitespace changes --- www/kanban/jkanban.js | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index 0ecaf2a22..69df83596 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -85,18 +85,18 @@ //Init Drag Board self.drakeBoard = self.dragula([self.container], { - moves: function (el, source, handle, sibling) { - if (self.options.readOnly) { return false; } - if (!self.options.dragBoards) return false; - return (handle.classList.contains('kanban-board-header') || handle.classList.contains('kanban-title-board')); - }, - accepts: function (el, target, source, sibling) { - if (self.options.readOnly) { return false; } - return target.classList.contains('kanban-container'); - }, - revertOnSpill: true, - direction: 'horizontal', - }) + moves: function (el, source, handle, sibling) { + if (self.options.readOnly) { return false; } + if (!self.options.dragBoards) return false; + return (handle.classList.contains('kanban-board-header') || handle.classList.contains('kanban-title-board')); + }, + accepts: function (el, target, source, sibling) { + if (self.options.readOnly) { return false; } + return target.classList.contains('kanban-container'); + }, + revertOnSpill: true, + direction: 'horizontal', + }) .on('drag', function (el, source) { el.classList.add('is-moving'); self.options.dragBoard(el, source); @@ -311,14 +311,6 @@ if (self.options.boards !== boards) self.options.boards.push(board); - /*if (!self.options.responsivePercentage) { - //add width to container - if (self.container.style.width === '') { - self.container.style.width = parseInt(boardWidth) + (parseInt(self.options.gutter) * 2) + 'px'; - } else { - self.container.style.width = parseInt(self.container.style.width) + parseInt(boardWidth) + (parseInt(self.options.gutter) * 2) + 'px'; - } - }*/ //create node var boardNode = document.createElement('div'); boardNode.dataset.id = board.id; From 3c2417b47909039a10295ffdccda0ba26ec5bd2b Mon Sep 17 00:00:00 2001 From: MTRNord Date: Thu, 13 Sep 2018 14:46:41 +0200 Subject: [PATCH 09/16] fix more whitespace in jkanban.js --- www/kanban/jkanban.js | 68 +++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index 69df83596..ec04257fa 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -31,7 +31,7 @@ * @author: Riccardo Tartaglia */ - //Require dragula + //Require dragula var dragula = require('dragula'); (function () { @@ -591,7 +591,7 @@ }()); - }, { +}, { "dragula": 9 }], 2: [function (require, module, exports) { @@ -599,7 +599,7 @@ return Array.prototype.slice.call(a, n); } - }, {}], +}, {}], 3: [function (require, module, exports) { 'use strict'; @@ -614,7 +614,7 @@ }); }; - }, { +}, { "ticky": 10 }], 4: [function (require, module, exports) { @@ -685,7 +685,7 @@ return thing; }; - }, { +}, { "./debounce": 3, "atoa": 2 }], @@ -802,7 +802,7 @@ } }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - }, { +}, { "./eventmap": 6, "custom-event": 7 }], @@ -823,7 +823,7 @@ module.exports = eventmap; }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - }, {}], +}, {}], 7: [function (require, module, exports) { (function (global) { @@ -853,33 +853,33 @@ // IE >= 9 'function' === typeof document.createEvent ? function CustomEvent(type, params) { - var e = document.createEvent('CustomEvent'); - if (params) { - e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail); - } else { - e.initCustomEvent(type, false, false, void 0); - } - return e; - } : - - // IE <= 8 - function CustomEvent(type, params) { - var e = document.createEventObject(); - e.type = type; - if (params) { - e.bubbles = Boolean(params.bubbles); - e.cancelable = Boolean(params.cancelable); - e.detail = params.detail; - } else { - e.bubbles = false; - e.cancelable = false; - e.detail = void 0; - } - return e; + var e = document.createEvent('CustomEvent'); + if (params) { + e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail); + } else { + e.initCustomEvent(type, false, false, void 0); } + return e; + } : + + // IE <= 8 + function CustomEvent(type, params) { + var e = document.createEventObject(); + e.type = type; + if (params) { + e.bubbles = Boolean(params.bubbles); + e.cancelable = Boolean(params.cancelable); + e.detail = params.detail; + } else { + e.bubbles = false; + e.cancelable = false; + e.detail = void 0; + } + return e; + } }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - }, {}], +}, {}], 8: [function (require, module, exports) { 'use strict'; @@ -915,7 +915,7 @@ rm: rmClass }; - }, {}], +}, {}], 9: [function (require, module, exports) { (function (global) { 'use strict'; @@ -1602,7 +1602,7 @@ module.exports = dragula; }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) - }, { +}, { "./classes": 8, "contra/emitter": 4, "crossvent": 5 @@ -1621,5 +1621,5 @@ } module.exports = tick; - }, {}] +}, {}] }, {}, [1]); From 2ef397a483cd055dee8cb5567c94377732377931 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Thu, 13 Sep 2018 16:08:27 +0200 Subject: [PATCH 10/16] improve item color picker logic and UX --- www/kanban/inner.js | 39 ++++++++++++++++++++++++++++++++------- www/kanban/jkanban.js | 30 +++++------------------------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/www/kanban/inner.js b/www/kanban/inner.js index 8bec2233e..a7f73f81e 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -207,15 +207,36 @@ define([ } }); }, - colorClick: function (el) { + colorClick: function (el, type) { if (framework.isReadOnly() || framework.isLocked()) { return; } - verbose("in color click"); - var board = $(el.parentNode).attr("data-id"); - var boardJSON = kanban.getBoardJSON(board); + verbose("on color click"); + var boardJSON; + var board; + if (type === "board") { + verbose("board color click"); + board = $(el.parentNode).attr("data-id"); + boardJSON = kanban.getBoardJSON(board); + } else { + verbose("item color click"); + board = $(el.parentNode.parentNode).attr("data-id"); + var pos = kanban.findElementPosition(el); + boardJSON = kanban.getBoardJSON(board).item[pos]; + } var onchange = function (colorL) { var elL = el; - var boardL = $(elL.parentNode).attr("data-id"); - var boardJSONL = kanban.getBoardJSON(boardL); + var typeL = type; + var boardJSONL; + var boardL; + if (typeL === "board") { + verbose("board color change"); + boardL = $(elL.parentNode).attr("data-id"); + boardJSONL = kanban.getBoardJSON(boardL); + } else { + verbose("item color change"); + boardL = $(elL.parentNode.parentNode).attr("data-id"); + var pos = kanban.findElementPosition(elL); + boardJSONL = kanban.getBoardJSON(boardL).item[pos]; + } var currentColor = boardJSONL.color; verbose("Current color " + currentColor); if (currentColor !== colorL.toString()) { @@ -226,12 +247,16 @@ define([ }; var jscolorL; el._jscLinkedInstance = undefined; - jscolorL = new jscolor(el,{onFineChange: onchange, valueElement:undefined}); + jscolorL = new window.jscolor(el,{onFineChange: onchange, valueElement:undefined}); jscolorL.show(); var currentColor = boardJSON.color; + if (currentColor == undefined) { + currentColor = '' + } jscolorL.fromString(currentColor); }, buttonClick: function (el, boardId) { + e.stopPropagation(); if (framework.isReadOnly() || framework.isLocked()) { return; } UI.confirm(Messages.kanban_deleteBoard, function (yes) { if (!yes) { return; } diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index ec04257fa..e70947697 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -68,7 +68,7 @@ click: function (el) {}, boardTitleclick: function (el, boardId) {}, buttonClick: function (el, boardId) {}, - colorClick: function (el, boardId) {}, + colorClick: function (el, type) {}, addItemClick: function (el, boardId) {}, onChange: function () {} }; @@ -345,7 +345,7 @@ titleBoard.clickfn = board.boardTitleClick; __onboardTitleClickHandler(titleBoard); headerBoard.appendChild(titleBoard); - __onColorClickHandler(headerBoard); + __onColorClickHandler(headerBoard, "board"); // if add button is true, add button to the board if (addButton) { @@ -378,25 +378,7 @@ nodeItemText.dropfn = itemKanban.drop; //add click handler of item __onclickHandler(nodeItemText); - - var onchange = function (colorL) { - var currentColor = itemKanban.color; - if (currentColor !== colorL.toString()) { - itemKanban.color = colorL.toString(); - self.onChange(); - } - }; - - var jscolorL; - nodeItem._jscLinkedInstance = undefined; - jscolorL = new jscolor(nodeItem,{onFineChange: onchange, valueElement:undefined}); - var currentColor = itemKanban.color; - // If not defined dont have it undefined - if (currentColor == undefined) { - currentColor = '' - } - console.log(currentColor); - jscolorL.fromString(currentColor); + __onColorClickHandler(nodeItem, "item"); contentBoard.appendChild(nodeItem); } @@ -545,12 +527,10 @@ }); } - function __onColorClickHandler(nodeItem, clickfn) { + function __onColorClickHandler(nodeItem, type) { nodeItem.addEventListener('click', function (e) { e.preventDefault; - self.options.colorClick(this); - if (typeof (this.clickfn) === 'function') - this.clickfn(this); + self.options.colorClick(this, type); }); } From e7a1433a3087a1b9fb9c9f06cb196a5d16145323 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Thu, 13 Sep 2018 16:15:11 +0200 Subject: [PATCH 11/16] fix loading of the item color from saved state --- www/kanban/jkanban.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index e70947697..0c89abe2e 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -378,6 +378,10 @@ nodeItemText.dropfn = itemKanban.drop; //add click handler of item __onclickHandler(nodeItemText); + if (itemKanban.color !== '' && itemKanban.color !== undefined) { + jscolorL = new jscolor(nodeItem,{valueElement:undefined}); + jscolorL.fromString(itemKanban.color); + } __onColorClickHandler(nodeItem, "item"); contentBoard.appendChild(nodeItem); From 8b384d6bb362c8bb103b3f67b5cd54fe747251f4 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Thu, 13 Sep 2018 16:20:42 +0200 Subject: [PATCH 12/16] fix jscolor picker for new kanban items and fix the code according to CI --- www/kanban/inner.js | 6 +++--- www/kanban/jkanban.js | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/www/kanban/inner.js b/www/kanban/inner.js index a7f73f81e..ed34b837e 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -250,12 +250,12 @@ define([ jscolorL = new window.jscolor(el,{onFineChange: onchange, valueElement:undefined}); jscolorL.show(); var currentColor = boardJSON.color; - if (currentColor == undefined) { - currentColor = '' + if (currentColor === undefined) { + currentColor = ''; } jscolorL.fromString(currentColor); }, - buttonClick: function (el, boardId) { + buttonClick: function (el, boardId, e) { e.stopPropagation(); if (framework.isReadOnly() || framework.isLocked()) { return; } UI.confirm(Messages.kanban_deleteBoard, function (yes) { diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index 0c89abe2e..971226384 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -275,6 +275,7 @@ nodeItem.dragendfn = element.dragend; nodeItem.dropfn = element.drop; __onclickHandler(nodeItem); + __onColorClickHandler(nodeItem, "item"); board.appendChild(nodeItem); // send event that board has changed self.onChange(); From 13d511d037ad4282e5585d562c3b34597870e0b2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 20 Sep 2018 10:04:08 -0400 Subject: [PATCH 13/16] fix broken link --- customize.dist/pages.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 578c1dc24..601d3034d 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -615,17 +615,21 @@ define([ } ]); + var _link = h('a', { + href: "https://opencollective.com/cryptpad/contribute", + target: '_blank', + rel: 'noopener', + }); + var crowdFunding = AppConfig.disableCrowdfundingMessages ? undefined : h('button', [ Msg.crowdfunding_home1, h('br'), - Msg.crowdfunding_home2 + Msg.crowdfunding_home2, + link ]); + $(crowdFunding).click(function () { - var a = document.createElement("a"); - a.href = "https://opencollective.com/cryptpad/contribute"; - a.target = "_blank"; - a.rel = "noopener"; - a.click(); + link.click(); }); return [ From b27285885e0f9ca8e7ef27e0f1b1ced64001443c Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 20 Sep 2018 10:06:21 -0400 Subject: [PATCH 14/16] oops, try again --- customize.dist/pages.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/customize.dist/pages.js b/customize.dist/pages.js index 601d3034d..e4b0dc1e4 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -625,11 +625,11 @@ define([ Msg.crowdfunding_home1, h('br'), Msg.crowdfunding_home2, - link + _link ]); $(crowdFunding).click(function () { - link.click(); + _link.click(); }); return [ From 91cff453f19a8614f6d3386495b0b0acffb3dab2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 20 Sep 2018 11:40:42 -0400 Subject: [PATCH 15/16] enable shared folders by default --- www/common/application_config_internal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/application_config_internal.js b/www/common/application_config_internal.js index 7539fb0db..7e39cfc1c 100644 --- a/www/common/application_config_internal.js +++ b/www/common/application_config_internal.js @@ -130,7 +130,7 @@ define(function() { // spontaneously, resulting in the deletion of the entire folder's content. // We highly recommend to keep them disabled until they are stable enough to be enabled // by default by the CryptPad developers. - config.disableSharedFolders = true; + config.disableSharedFolders = false; return config; }); From d49f787bcd2606f4f4d2c3a5de3708808e6c5cf0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 20 Sep 2018 11:41:13 -0400 Subject: [PATCH 16/16] update changelog for 2.8.0 (Ibis) --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 924a970f6..76ae3c406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +# Ibis release (v2.8.0) + +## Goals + +We've been making use of some hidden features for a while, to make sure that they were safe to deploy. +This release, we worked on making _contextual chat_ and _shared folders_ available to everyone. + +## Update notes + +* run `bower update` to download an updated version of _marked.js_ + +### Features + +* Our kanban application now features a much more consistent and flexible colorpicker, thanks to @MTRNord (https://github.com/MTRNord) +* File upload dialogs now allow you to upload multiple files at once +* Updated German translations thanks to [b3yond](https://github.com/b3yond/) +* An explicit pad storage policy to better suit different privacy constraints + * _import local pads_ at login time is no longer default +* An embedded chat room in every pad, so you can work alongside your fellow editors more easily +* Promotion of our [crowdfunding campaign](https://opencollective.com/cryptpad), including a button on the home page, and a one-time dialog for users + +### Bug fixes + +* Updating our markdown library resolved an issue which incorrectly rendered links containing parentheses. +* We discovered an issue logging in with _very old_ credentials which were initialized without a public key. We now regenerate your keyring if you do not have public keys stored in association with your account. +* We found another bug in our login process; under certain conditions the terminating function could be called more than once. + # Hedgehog release (v2.7.0) ## Goals