diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index 96eeff815..c9cce185f 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -228,6 +228,10 @@ .control-filelist.hero ul li > div.controls > a.control{width:16px;height:23px;background:transparent;overflow:hidden;display:inline-block;color:#ffffff !important;padding:0} .control-filelist.hero ul li > div.controls > a.control:before{font-size:17px} .control-filelist.hero ul li:hover > div.controls{display:block} +.control-filelist.hero ul li.separator{position:relative;border-bottom:1px solid #95a5a6;padding:17px 15px 18px 15px} +.control-filelist.hero ul li.separator:before{z-index:31;content:'';display:block;width:0;height:0;border-left:9.5px solid transparent;border-right:9.5px solid transparent;border-top:11px solid #ffffff;border-bottom-width:0;position:absolute;left:13px;bottom:-8px} +.control-filelist.hero ul li.separator:after{z-index:30;content:'';display:block;width:0;height:0;border-left:8.5px solid transparent;border-right:8.5px solid transparent;border-top:9px solid #95a5a6;border-bottom-width:0;position:absolute;left:14px;bottom:-9px} +.control-filelist.hero ul li.separator h5{color:#2b3e50;font-size:15px;margin:0;font-weight:600;padding:0} .control-filelist.hero ul > li.group > ul > li > a{padding-left:66px} .control-filelist.hero.single-level ul li:hover{background:#58b6f7} .control-filelist.hero.single-level ul li:hover > a{background:#58b6f7;border-bottom:1px solid #58b6f7 !important} @@ -618,6 +622,7 @@ body{webkit-font-smoothing:antialiased;font-family:'Open Sans',Arial,sans-serif; .layout > .layout-cell.center{text-align:center} .layout > .layout-cell.middle{vertical-align:middle} .whiteboard{background:white} +.layout-fill-container{position:absolute;left:0;top:0;width:100%;height:100%} .layout-cell.width-fix > form,[data-calculate-width] > form,.layout-cell.width-fix > div,[data-calculate-width] > div{display:inline-block} body.compact-container .layout.layout-container,body.compact-container .layout .layout-container{padding:0 !important} body.slim-container .layout.layout-container,body.slim-container .layout .layout-container{padding-left:0 !important;padding-right:0 !important} @@ -627,6 +632,24 @@ body.slim-container .layout.layout-container,body.slim-container .layout .layout .layout.responsive-sidebar > .layout-cell:last-child{display:table-header-group;width:auto;height:auto} .layout.responsive-sidebar > .layout-cell:last-child .layout-absolute{position:static} } +.flex-layout-column{display:-webkit-box;display:-webkit-flex;display:-moz-flex;display:-ms-flexbox;display:-ms-flex;display:flex;-webkit-flex-direction:column;-moz-flex-direction:column;-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column} +.flex-layout-column.full-height-strict{height:100%} +.flex-layout-column.absolute{position:absolute !important} +.flex-layout-column.fill-container{position:absolute;left:0;top:0;width:100%;height:100%} +.flex-layout-row{display:-webkit-box;display:-webkit-flex;display:-moz-flex;display:-ms-flexbox;display:-ms-flex;display:flex;-webkit-flex-direction:row;-moz-flex-direction:row;-webkit-box-orient:horizontal;-ms-flex-direction:row;flex-direction:row} +.flex-layout-column.justify-center,.flex-layout-row.justify-center{-webkit-justify-content:center;-moz-justify-content:center;-ms-justify-content:;-webkit-box-pack:center;justify-content:center} +.flex-layout-column.align-center,.flex-layout-row.align-center{-webkit-align-items:center;-moz-align-items:center;-ms-align-items:center;align-items:center;-webkit-align-content:center;-moz-align-content:center;-webkit-box-align:center;-ms-align-content:center;align-content:center} +.flex-layout-column.full-height,.flex-layout-row.full-height{min-height:100%} +.flex-layout-item{margin:0} +.flex-layout-item.fix{-webkit-box-flex:0;-webkit-flex:0 0 auto;-moz-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto} +.flex-layout-item.stretch{-webkit-box-flex:1;-webkit-flex:1 1 auto;-moz-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto} +.flex-layout-item.stretch-constrain{-webkit-box-flex:1;-webkit-flex:1;-moz-flex:1;-ms-flex:1;flex:1} +.flex-layout-item.center{-webkit-align-self:center;-moz-align-self:center;-ms-align-self:center;align-self:center} +.flex-layout-item.relative{position:relative} +.flex-layout-item.layout-container{max-width:none} +.flex-layout-item.width-100{width:100px} +.flex-layout-item.width-200{width:200px} +.flex-layout-item.width-300{width:300px} body.mainmenu-open{overflow:hidden} nav#layout-mainmenu.navbar{background-color:#111111;padding:0 0 0 20px;line-height:0;white-space:nowrap} nav#layout-mainmenu.navbar a{text-decoration:none} @@ -782,7 +805,7 @@ html.csstransitions body.outer.preload .outer-form-container{-webkit-transform:s .fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li.active a > span.title,.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li.active a > span.title{background-color:#e67e22;z-index:105} .fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li.active a > span.title:before,.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li.active a > span.title:before{background-position:left -40px;z-index:107} .fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li.active a > span.title:after,.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li.active a > span.title:after{background-position:-80px -40px;z-index:107} -.fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li[data-modified] span.tab-close i,.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li[data-modified] span.tab-close i{top:4px;font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0} +.fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li[data-modified] span.tab-close i,.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li[data-modified] span.tab-close i{top:3px;font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0} .fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li[data-modified] span.tab-close i:before,.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li[data-modified] span.tab-close i:before{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f111";font-size:9px} .fancy-layout .control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li:first-child,.fancy-layout.control-tabs.master-tabs > div > div.tabs-container > ul.nav-tabs > li:first-child{margin-left:0} .fancy-layout .control-tabs.master-tabs[data-closable] > div > div.tabs-container > ul.nav-tabs > li a > span.title,.fancy-layout.control-tabs.master-tabs[data-closable] > div > div.tabs-container > ul.nav-tabs > li a > span.title{padding-right:10px} @@ -800,7 +823,7 @@ html.csstransitions body.outer.preload .outer-form-container{-webkit-transform:s .fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs{background:#f9f9f9} .fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li{margin-left:-19px} .fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li:first-child,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li:first-child{margin-left:0;padding-left:8px} -.fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a{padding:8px 16px 0 16px;font-weight:400;color:#2b3e50;opacity:0.6;filter:alpha(opacity=60)} +.fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a{padding:8px 16px 0 16px;font-weight:400;height:33px;color:#2b3e50;opacity:0.6;filter:alpha(opacity=60)} .fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a > span.title,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a > span.title{position:relative;display:inline-block;padding:4px 5px 9px 5px;font-size:13px;z-index:100;height:25px !important;background-color:transparent} .fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a > span.title:before,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a > span.title:before,.fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a > span.title:after,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a > span.title:after{content:' ';position:absolute;background:transparent url(../images/secondary-tab-shape-content.svg) no-repeat left top;width:15px;height:25px;top:0;z-index:100;display:none} .fancy-layout .control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a > span.title:before,.fancy-layout.control-tabs.secondary-tabs.secondary-content-tabs > div > ul.nav-tabs > li a > span.title:before{left:-15px} diff --git a/modules/backend/assets/js/october-min.js b/modules/backend/assets/js/october-min.js index 9d04aa730..6e572f9ca 100644 --- a/modules/backend/assets/js/october-min.js +++ b/modules/backend/assets/js/october-min.js @@ -745,7 +745,8 @@ this.scrollbarSize=null this.updateScrollbarTimer=null this.dragOffset=null Base.call(this) -this.init()} +this.init() +$.oc.foundation.controlUtils.markDisposable(element)} Scrollpad.prototype=Object.create(BaseProto) Scrollpad.prototype.constructor=Scrollpad Scrollpad.prototype.dispose=function(){this.unregisterHandlers() @@ -771,10 +772,12 @@ this.scrollbarElement=el.querySelector('.scrollpad-scrollbar') this.dragHandleElement=el.querySelector('.scrollpad-scrollbar > .drag-handle')} Scrollpad.prototype.registerHandlers=function(){this.$el.on('mouseenter',this.proxy(this.onMouseEnter)) this.$el.on('mouseleave',this.proxy(this.onMouseLeave)) +this.$el.one('dispose-control',this.proxy(this.dispose)) this.scrollContentElement.addEventListener('scroll',this.proxy(this.onScroll)) this.dragHandleElement.addEventListener('mousedown',this.proxy(this.onStartDrag))} Scrollpad.prototype.unregisterHandlers=function(){this.$el.off('mouseenter',this.proxy(this.onMouseEnter)) this.$el.off('mouseleave',this.proxy(this.onMouseLeave)) +this.$el.off('dispose-control',this.proxy(this.dispose)) this.scrollContentElement.removeEventListener('scroll',this.proxy(this.onScroll)) this.dragHandleElement.removeEventListener('mousedown',this.proxy(this.onStartDrag)) document.removeEventListener('mousemove',this.proxy(this.onMouseMove)) @@ -1133,16 +1136,19 @@ this.$fixButton=$('',item:'
  • ',minLength:1} +$.fn.autocomplete.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1,bodyContainer:false} $.fn.autocomplete.Constructor=Autocomplete $.fn.autocomplete.noConflict=function(){$.fn.autocomplete=old return this} diff --git a/modules/backend/assets/js/october.autocomplete.js b/modules/backend/assets/js/october.autocomplete.js index ff9bd3f74..3b65797fa 100644 --- a/modules/backend/assets/js/october.autocomplete.js +++ b/modules/backend/assets/js/october.autocomplete.js @@ -51,7 +51,8 @@ }, show: function () { - var pos = $.extend({}, this.$element.position(), { + var offset = this.options.bodyContainer ? this.$element.offset() : this.$element.position(), + pos = $.extend({}, offset, { height: this.$element[0].offsetHeight }), cssOptions = { @@ -63,10 +64,16 @@ cssOptions.width = this.$element[0].offsetWidth } - this.$menu - .insertAfter(this.$element) - .css(cssOptions) - .show() + this.$menu.css(cssOptions) + + if (this.options.bodyContainer) { + $(document.body).append(this.$menu) + } + else { + this.$menu.insertAfter(this.$element) + } + + this.$menu.show() this.shown = true return this @@ -347,7 +354,8 @@ items: 8, menu: '', item: '
  • ', - minLength: 1 + minLength: 1, + bodyContainer: false } $.fn.autocomplete.Constructor = Autocomplete diff --git a/modules/backend/assets/js/october.flyout.js b/modules/backend/assets/js/october.flyout.js index 22f2e4d1c..251bd71d8 100644 --- a/modules/backend/assets/js/october.flyout.js +++ b/modules/backend/assets/js/october.flyout.js @@ -38,6 +38,8 @@ var $cells = this.$el.find('> .layout-cell'), $flyout = this.$el.find('> .flyout') + $('[data-control=layout-sidepanel]').sidePanelTab('hideSidePanel') + this.removeOverlay() for (var i = 0; i < $cells.length; i++) { diff --git a/modules/backend/assets/js/october.scrollpad.js b/modules/backend/assets/js/october.scrollpad.js index 338c0969f..8e2be6293 100644 --- a/modules/backend/assets/js/october.scrollpad.js +++ b/modules/backend/assets/js/october.scrollpad.js @@ -62,6 +62,8 @@ // this.init() + + $.oc.foundation.controlUtils.markDisposable(element) } Scrollpad.prototype = Object.create(BaseProto) @@ -113,6 +115,9 @@ Scrollpad.prototype.registerHandlers = function() { this.$el.on('mouseenter', this.proxy(this.onMouseEnter)) this.$el.on('mouseleave', this.proxy(this.onMouseLeave)) + + this.$el.one('dispose-control', this.proxy(this.dispose)) + this.scrollContentElement.addEventListener('scroll', this.proxy(this.onScroll)) this.dragHandleElement.addEventListener('mousedown', this.proxy(this.onStartDrag)) } @@ -120,6 +125,7 @@ Scrollpad.prototype.unregisterHandlers = function() { this.$el.off('mouseenter', this.proxy(this.onMouseEnter)) this.$el.off('mouseleave', this.proxy(this.onMouseLeave)) + this.$el.off('dispose-control', this.proxy(this.dispose)) this.scrollContentElement.removeEventListener('scroll', this.proxy(this.onScroll)) this.dragHandleElement.removeEventListener('mousedown', this.proxy(this.onStartDrag)) diff --git a/modules/backend/assets/js/october.sidepaneltab.js b/modules/backend/assets/js/october.sidepaneltab.js index da3bc442e..c783889f2 100644 --- a/modules/backend/assets/js/october.sidepaneltab.js +++ b/modules/backend/assets/js/october.sidepaneltab.js @@ -31,6 +31,10 @@ $('.fix-button-container', this.$el).append(this.$fixButton) this.$sideNavItems.click(function(){ + if ($(this).data('no-side-panel')) { + return + } + if (Modernizr.touch && $(window).width() < self.options.breakpoint) { if ($(this).data('menu-item') == self.visibleItemId && self.panelVisible) { self.hideSidePanel() @@ -45,13 +49,16 @@ }) if (!Modernizr.touch) { - self.$sideNav.mouseenter(function(){ - if ($(window).width() < self.options.breakpoint || !self.panelFixed()) { - self.panelOpenTimeout = setTimeout(function () { - self.displaySidePanel() - }, self.tabOpenDelay) - } - }) + // The side panel now opens only when a menu item is hovered and + // when the item doesn't have the "data-no-side-panel" attribute. + // TODO: remove the comment and the code below if no issues noticed. + // self.$sideNav.mouseenter(function(){ + // if ($(window).width() < self.options.breakpoint || !self.panelFixed()) { + // self.panelOpenTimeout = setTimeout(function () { + // self.displaySidePanel() + // }, self.tabOpenDelay) + // } + // }) self.$sideNav.mouseleave(function(){ clearTimeout(self.panelOpenTimeout) @@ -63,8 +70,14 @@ self.$sideNavItems.mouseenter(function(){ if ($(window).width() < self.options.breakpoint || !self.panelFixed()) { + if ($(this).data('no-side-panel')) { + self.hideSidePanel() + return + } + var _this = this self.tabOpenTimeout = setTimeout(function () { + self.displaySidePanel() self.displayTab(_this) }, self.tabOpenDelay) } @@ -74,7 +87,6 @@ clearTimeout(self.tabOpenTimeout) }) - $(window).resize(function() { self.updatePanelPosition() self.updateActiveTab() @@ -198,7 +210,7 @@ var data = $this.data('oc.sidePanelTab') var options = $.extend({}, SidePanelTab.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('oc.sidePanelTab', (data = new SidePanelTab(this, options))) - if (typeof option == 'string') data[option].call($this) + if (typeof option == 'string') data[option].call(data) }) } diff --git a/modules/backend/assets/less/controls/filelist.less b/modules/backend/assets/less/controls/filelist.less index c7d103494..824748caa 100644 --- a/modules/backend/assets/less/controls/filelist.less +++ b/modules/backend/assets/less/controls/filelist.less @@ -336,6 +336,36 @@ &:hover > div.controls { display: block; } + + &.separator { + position: relative; + border-bottom: 1px solid #95a5a6; + padding: 17px 15px 18px 15px; + + &:before { + z-index: 31; + .triangle(down, 19px, 11px, white); + position: absolute; + left: 13px; + bottom: -8px; + } + + &:after { + z-index: 30; + .triangle(down, 17px, 9px, #95a5a6); + position: absolute; + left: 14px; + bottom: -9px; + } + + h5 { + color: #2b3e50; + font-size: 15px; + margin: 0; + font-weight: 600; + padding: 0; + } + } } > li.group { diff --git a/modules/backend/assets/less/layout/fancylayout.less b/modules/backend/assets/less/layout/fancylayout.less index 8c4d5c4bb..d68220b7e 100644 --- a/modules/backend/assets/less/layout/fancylayout.less +++ b/modules/backend/assets/less/layout/fancylayout.less @@ -146,7 +146,7 @@ &[data-modified] { span.tab-close i { - top: 4px; + top: 3px; .hide-text(); &:before { @@ -232,6 +232,7 @@ a { padding: 8px 16px 0 16px; font-weight: 400; + height: 33px; color: #2b3e50; .opacity(0.6); diff --git a/modules/backend/assets/less/layout/flexlayout.less b/modules/backend/assets/less/layout/flexlayout.less new file mode 100644 index 000000000..0f3fe3d7c --- /dev/null +++ b/modules/backend/assets/less/layout/flexlayout.less @@ -0,0 +1,54 @@ +.flex-layout-column { + .flex-display(); + .flex-direction-column(); + + &.full-height-strict { + height: 100%; + } + + &.absolute { + position: absolute!important; + } + + &.fill-container { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + } +} + +.flex-layout-row { + .flex-display(); + .flex-direction-row(); +} + +.flex-layout-column, .flex-layout-row { + &.justify-center {.justify-content(center);} + &.align-center { + .align-items(center); + .align-content(center); + } + + &.full-height { + min-height: 100%; + // height: 100%; + } +} + +.flex-layout-item { + margin: 0; + &.fix {.flex-fix();} + &.stretch {.flex-stretch();} + &.stretch-constrain {.flex-stretch-constrain();} + &.center {.align-self(center);} + + &.relative {position: relative;} + + &.layout-container {max-width: none;} + + &.width-100 {width: 100px;} + &.width-200 {width: 200px;} + &.width-300 {width: 300px;} +} \ No newline at end of file diff --git a/modules/backend/assets/less/layout/layout.less b/modules/backend/assets/less/layout/layout.less index ad4b4a431..da8c8491e 100644 --- a/modules/backend/assets/less/layout/layout.less +++ b/modules/backend/assets/less/layout/layout.less @@ -142,6 +142,14 @@ body { background: white; } +.layout-fill-container { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; +} + // // Calculated fixed width // diff --git a/modules/backend/assets/less/october.less b/modules/backend/assets/less/october.less index cb5339d3e..4fa7b625d 100644 --- a/modules/backend/assets/less/october.less +++ b/modules/backend/assets/less/october.less @@ -56,6 +56,7 @@ // Layout @import "layout/layout.less"; +@import "layout/flexlayout.less"; @import "layout/mainmenu.less"; @import "layout/sidenav.less"; @import "layout/sidepanel.less"; diff --git a/modules/backend/formwidgets/codeeditor/assets/js/build-min.js b/modules/backend/formwidgets/codeeditor/assets/js/build-min.js index 3fa617592..338dfd1d1 100644 --- a/modules/backend/formwidgets/codeeditor/assets/js/build-min.js +++ b/modules/backend/formwidgets/codeeditor/assets/js/build-min.js @@ -4103,7 +4103,20 @@ if(prevIndent!=-1&&prevIndent][-+\\d\\s]*$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"}],"qqstring":[{token:"string",regex:'(?=(?:(?:\\\\.)|(?:[^:]))*?:)',next:"start"},{token:"string",regex:'.+'}]};};oop.inherits(YamlHighlightRules,TextHighlightRules);exports.YamlHighlightRules=YamlHighlightRules;});ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(require,exports,module){"use strict";var Range=require("../range").Range;var MatchingBraceOutdent=function(){};(function(){this.checkOutdent=function(line,input){if(!/^\s+$/.test(line)) +return false;return/^\s*\}/.test(input);};this.autoOutdent=function(doc,row){var line=doc.getLine(row);var match=line.match(/^(\s*\})/);if(!match)return 0;var column=match[1].length;var openBracePos=doc.findMatchingBracket({row:row,column:column});if(!openBracePos||openBracePos.row==row)return 0;var indent=this.$getIndent(doc.getLine(openBracePos.row));doc.replace(new Range(row,0,row,column-1),indent);};this.$getIndent=function(line){return line.match(/^\s*/)[0];};}).call(MatchingBraceOutdent.prototype);exports.MatchingBraceOutdent=MatchingBraceOutdent;});ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(require,exports,module){"use strict";var oop=require("../../lib/oop");var BaseFoldMode=require("./fold_mode").FoldMode;var Range=require("../../range").Range;var FoldMode=exports.FoldMode=function(){};oop.inherits(FoldMode,BaseFoldMode);(function(){this.getFoldWidgetRange=function(session,foldStyle,row){var range=this.indentationBlock(session,row);if(range) +return range;var re=/\S/;var line=session.getLine(row);var startLevel=line.search(re);if(startLevel==-1||line[startLevel]!="#") +return;var startColumn=line.length;var maxRow=session.getLength();var startRow=row;var endRow=row;while(++rowstartRow){var endColumn=session.getLine(endRow).length;return new Range(startRow,startColumn,endRow,endColumn);}};this.getFoldWidget=function(session,foldStyle,row){var line=session.getLine(row);var indent=line.search(/\S/);var next=session.getLine(row+1);var prev=session.getLine(row-1);var prevIndent=prev.search(/\S/);var nextIndent=next.search(/\S/);if(indent==-1){session.foldWidgets[row-1]=prevIndent!=-1&&prevIndent=|<<=|>>=|>>>=|<>|<|>|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],"start":[DocCommentHighlightRules.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment_regex_allowed"},{token:"comment",regex:"\\/\\/",next:"line_comment_regex_allowed"},{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],"regex":[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],"regex_character_class":[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],"function_arguments":[{token:"variable.parameter",regex:identifierRe},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],"comment_regex_allowed":[DocCommentHighlightRules.getTagRule(),{token:"comment",regex:"\\*\\/",next:"start"},{defaultToken:"comment",caseInsensitive:true}],"comment":[DocCommentHighlightRules.getTagRule(),{token:"comment",regex:"\\*\\/",next:"no_regex"},{defaultToken:"comment",caseInsensitive:true}],"line_comment_regex_allowed":[DocCommentHighlightRules.getTagRule(),{token:"comment",regex:"$|^",next:"start"},{defaultToken:"comment",caseInsensitive:true}],"line_comment":[DocCommentHighlightRules.getTagRule(),{token:"comment",regex:"$|^",next:"no_regex"},{defaultToken:"comment",caseInsensitive:true}],"qqstring":[{token:"constant.language.escape",regex:escapedRe},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],"qstring":[{token:"constant.language.escape",regex:escapedRe},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!options||!options.noES6){this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(val,state,stack){this.next=val=="{"?this.nextState:"";if(val=="{"&&stack.length){stack.unshift("start",state);return"paren";} if(val=="}"&&stack.length){stack.shift();this.next=stack.shift();if(this.next.indexOf("string")!=-1) return"paren.quasi.end";} diff --git a/modules/backend/formwidgets/codeeditor/assets/js/build.js b/modules/backend/formwidgets/codeeditor/assets/js/build.js index 931a6fc5d..c5df0cbfb 100644 --- a/modules/backend/formwidgets/codeeditor/assets/js/build.js +++ b/modules/backend/formwidgets/codeeditor/assets/js/build.js @@ -22,6 +22,7 @@ =require ../vendor/ace/mode-css.js =require ../vendor/ace/mode-scss.js =require ../vendor/ace/mode-sass.js +=require ../vendor/ace/mode-yaml.js =require ../vendor/ace/mode-javascript.js =require codeeditor.js diff --git a/modules/backend/formwidgets/codeeditor/assets/vendor/ace/mode-yaml.js b/modules/backend/formwidgets/codeeditor/assets/vendor/ace/mode-yaml.js new file mode 100644 index 000000000..39966fa6c --- /dev/null +++ b/modules/backend/formwidgets/codeeditor/assets/vendor/ace/mode-yaml.js @@ -0,0 +1,256 @@ +ace.define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + +var YamlHighlightRules = function() { + this.$rules = { + "start" : [ + { + token : "comment", + regex : "#.*$" + }, { + token : "list.markup", + regex : /^(?:-{3}|\.{3})\s*(?=#|$)/ + }, { + token : "list.markup", + regex : /^\s*[\-?](?:$|\s)/ + }, { + token: "constant", + regex: "!![\\w//]+" + }, { + token: "constant.language", + regex: "[&\\*][a-zA-Z0-9-_]+" + }, { + token: ["meta.tag", "keyword"], + regex: /^(\s*\w.*?)(\:(?:\s+|$))/ + },{ + token: ["meta.tag", "keyword"], + regex: /(\w+?)(\s*\:(?:\s+|$))/ + }, { + token : "keyword.operator", + regex : "<<\\w*:\\w*" + }, { + token : "keyword.operator", + regex : "-\\s*(?=[{])" + }, { + token : "string", // single line + regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]' + }, { + token : "string", // multi line string start + regex : '[|>][-+\\d\\s]*$', + next : "qqstring" + }, { + token : "string", // single quoted string + regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']" + }, { + token : "constant.numeric", // float + regex : /(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)/ + }, { + token : "constant.numeric", // other number + regex : /[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/ + }, { + token : "constant.language.boolean", + regex : "(?:true|false|TRUE|FALSE|True|False|yes|no)\\b" + }, { + token : "paren.lparen", + regex : "[[({]" + }, { + token : "paren.rparen", + regex : "[\\])}]" + } + ], + "qqstring" : [ + { + token : "string", + regex : '(?=(?:(?:\\\\.)|(?:[^:]))*?:)', + next : "start" + }, { + token : "string", + regex : '.+' + } + ]}; + +}; + +oop.inherits(YamlHighlightRules, TextHighlightRules); + +exports.YamlHighlightRules = YamlHighlightRules; +}); + +ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(require, exports, module) { +"use strict"; + +var Range = require("../range").Range; + +var MatchingBraceOutdent = function() {}; + +(function() { + + this.checkOutdent = function(line, input) { + if (! /^\s+$/.test(line)) + return false; + + return /^\s*\}/.test(input); + }; + + this.autoOutdent = function(doc, row) { + var line = doc.getLine(row); + var match = line.match(/^(\s*\})/); + + if (!match) return 0; + + var column = match[1].length; + var openBracePos = doc.findMatchingBracket({row: row, column: column}); + + if (!openBracePos || openBracePos.row == row) return 0; + + var indent = this.$getIndent(doc.getLine(openBracePos.row)); + doc.replace(new Range(row, 0, row, column-1), indent); + }; + + this.$getIndent = function(line) { + return line.match(/^\s*/)[0]; + }; + +}).call(MatchingBraceOutdent.prototype); + +exports.MatchingBraceOutdent = MatchingBraceOutdent; +}); + +ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"], function(require, exports, module) { +"use strict"; + +var oop = require("../../lib/oop"); +var BaseFoldMode = require("./fold_mode").FoldMode; +var Range = require("../../range").Range; + +var FoldMode = exports.FoldMode = function() {}; +oop.inherits(FoldMode, BaseFoldMode); + +(function() { + + this.getFoldWidgetRange = function(session, foldStyle, row) { + var range = this.indentationBlock(session, row); + if (range) + return range; + + var re = /\S/; + var line = session.getLine(row); + var startLevel = line.search(re); + if (startLevel == -1 || line[startLevel] != "#") + return; + + var startColumn = line.length; + var maxRow = session.getLength(); + var startRow = row; + var endRow = row; + + while (++row < maxRow) { + line = session.getLine(row); + var level = line.search(re); + + if (level == -1) + continue; + + if (line[level] != "#") + break; + + endRow = row; + } + + if (endRow > startRow) { + var endColumn = session.getLine(endRow).length; + return new Range(startRow, startColumn, endRow, endColumn); + } + }; + this.getFoldWidget = function(session, foldStyle, row) { + var line = session.getLine(row); + var indent = line.search(/\S/); + var next = session.getLine(row + 1); + var prev = session.getLine(row - 1); + var prevIndent = prev.search(/\S/); + var nextIndent = next.search(/\S/); + + if (indent == -1) { + session.foldWidgets[row - 1] = prevIndent!= -1 && prevIndent < nextIndent ? "start" : ""; + return ""; + } + if (prevIndent == -1) { + if (indent == nextIndent && line[indent] == "#" && next[indent] == "#") { + session.foldWidgets[row - 1] = ""; + session.foldWidgets[row + 1] = ""; + return "start"; + } + } else if (prevIndent == indent && line[indent] == "#" && prev[indent] == "#") { + if (session.getLine(row - 2).search(/\S/) == -1) { + session.foldWidgets[row - 1] = "start"; + session.foldWidgets[row + 1] = ""; + return ""; + } + } + + if (prevIndent!= -1 && prevIndent < indent) + session.foldWidgets[row - 1] = "start"; + else + session.foldWidgets[row - 1] = ""; + + if (indent < nextIndent) + return "start"; + else + return ""; + }; + +}).call(FoldMode.prototype); + +}); + +ace.define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/coffee"], function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var TextMode = require("./text").Mode; +var YamlHighlightRules = require("./yaml_highlight_rules").YamlHighlightRules; +var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent; +var FoldMode = require("./folding/coffee").FoldMode; + +var Mode = function() { + this.HighlightRules = YamlHighlightRules; + this.$outdent = new MatchingBraceOutdent(); + this.foldingRules = new FoldMode(); +}; +oop.inherits(Mode, TextMode); + +(function() { + + this.lineCommentStart = "#"; + + this.getNextLineIndent = function(state, line, tab) { + var indent = this.$getIndent(line); + + if (state == "start") { + var match = line.match(/^.*[\{\(\[]\s*$/); + if (match) { + indent += tab; + } + } + + return indent; + }; + + this.checkOutdent = function(state, line, input) { + return this.$outdent.checkOutdent(line, input); + }; + + this.autoOutdent = function(state, doc, row) { + this.$outdent.autoOutdent(doc, row); + }; + + + this.$id = "ace/mode/yaml"; +}).call(Mode.prototype); + +exports.Mode = Mode; + +}); \ No newline at end of file diff --git a/modules/backend/formwidgets/richeditor/assets/js/build-min.js b/modules/backend/formwidgets/richeditor/assets/js/build-min.js index 2dcbe2308..4417d4816 100755 --- a/modules/backend/formwidgets/richeditor/assets/js/build-min.js +++ b/modules/backend/formwidgets/richeditor/assets/js/build-min.js @@ -2203,4 +2203,4 @@ data[option].apply(data,methodArgs)}})} $.fn.richEditor.Constructor=RichEditor $.fn.richEditor.noConflict=function(){$.fn.richEditor=old return this} -$(document).render(function(){$('[data-control="richeditor"]').richEditor()})}(window.jQuery); +$(document).render(function(){$('[data-control="richeditor"]').richEditor()})}(window.jQuery); \ No newline at end of file diff --git a/modules/backend/lang/en/lang.php b/modules/backend/lang/en/lang.php index bcaa55c0e..837137278 100644 --- a/modules/backend/lang/en/lang.php +++ b/modules/backend/lang/en/lang.php @@ -178,6 +178,8 @@ return [ 'saving_name' => 'Saving :name...', 'delete' => 'Delete', 'deleting' => 'Deleting...', + 'confirm_delete' => 'Do you really want to delete this record?', + 'confirm_delete_multiple' => 'Do you really want to delete the selected records?', 'deleting_name' => 'Deleting :name...', 'reset_default' => 'Reset to default', 'resetting' => 'Resetting', @@ -206,7 +208,8 @@ return [ 'insert_row_below' => 'Insert Row Below', 'delete_row' => 'Delete Row', 'concurrency_file_changed_title' => 'File was changed', - 'concurrency_file_changed_description' => "The file you're editing has been changed on disk by another user. You can either reload the file and lose your changes or override the file on the disk." + 'concurrency_file_changed_description' => "The file you're editing has been changed on disk by another user. You can either reload the file and lose your changes or override the file on the disk.", + 'return_to_list' => 'Return to the list' ], 'recordfinder' => [ 'find_record' => 'Find Record' diff --git a/modules/backend/widgets/Table.php b/modules/backend/widgets/Table.php index 2e5d5657b..cdcdd4cf4 100644 --- a/modules/backend/widgets/Table.php +++ b/modules/backend/widgets/Table.php @@ -197,4 +197,21 @@ class Table extends WidgetBase 'options' => $options ]; } + + public function onGetAutocompleteOptions() + { + $columnName = Input::get('column'); + $rowData = Input::get('rowData'); + + $eventResults = $this->fireEvent('table.getAutocompleteOptions', [$columnName, $rowData]); + + $options = []; + if (count($eventResults)) { + $options = $eventResults[0]; + } + + return [ + 'options' => $options + ]; + } } diff --git a/modules/backend/widgets/table/README.md b/modules/backend/widgets/table/README.md index eec6d0365..477b3cf16 100644 --- a/modules/backend/widgets/table/README.md +++ b/modules/backend/widgets/table/README.md @@ -161,6 +161,24 @@ Multiple fields are allowed as well: **Note:** Dependent drop-down should always be defined after their master columns. +### Autocomplete cell processor + +The autocomplete column type can load options from the column configuration or with AJAX. Example column configuration: + + color: + title: Color + type: autocomplete + options: + red: Red + green: Green + blue: Blue + +If the `options` element is not presented in the configuration, the options will be loaded with AJAX. + +**TODO:** Document the AJAX interface + +The editor can have the `dependsOn` property similar to the drop-down editor. + # Server-side table widget (Backend\Widgets\Table) ## Configuration diff --git a/modules/backend/widgets/table/assets/css/table.css b/modules/backend/widgets/table/assets/css/table.css index 4a8a574b7..2c15a9f7e 100644 --- a/modules/backend/widgets/table/assets/css/table.css +++ b/modules/backend/widgets/table/assets/css/table.css @@ -72,16 +72,16 @@ padding: 1px; } .control-table table.data td.active { - border-color: #5fb6f5 !important; + border-color: #4da7e8 !important; } .control-table table.data td.active .content-container { padding: 0; - border: 1px solid #5fb6f5; + border: 1px solid #4da7e8; } .control-table table.data td.active .content-container:before, .control-table table.data td.active .content-container:after { content: ' '; - background: #5fb6f5; + background: #4da7e8; position: absolute; left: -2px; top: -2px; @@ -189,7 +189,7 @@ color: #95a5a6; } .control-table .pagination ul li.active { - background: #5fb6f5; + background: #4da7e8; } .control-table .pagination ul li.active a { color: #ffffff; @@ -207,9 +207,10 @@ } } /* - * String editor + * String and autocomplete editors */ -.control-table td[data-column-type=string] input[type=text] { +.control-table td[data-column-type=string] input[type=text], +.control-table td[data-column-type=autocomplete] input[type=text] { width: 100%; height: 100%; display: block; @@ -217,6 +218,18 @@ border: none; padding: 5px 10px; } +ul.table-widget-autocomplete { + background: white; + font-size: 13px; + margin-top: 0; + border: 1px solid #808c8d; + border-top: 1px solid #ecf0f1; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +ul.table-widget-autocomplete li a { + padding: 5px 10px; +} /* * Checkbox editor */ @@ -254,7 +267,7 @@ top: -4px; } .control-table td[data-column-type=checkbox] div[data-checkbox-element]:focus { - border-color: #5fb6f5; + border-color: #4da7e8; outline: none; } /* @@ -366,6 +379,6 @@ html.cssanimations .control-table td[data-column-type=dropdown] [data-view-conta .table-control-dropdown-list li:hover, .table-control-dropdown-list li:focus, .table-control-dropdown-list li.selected { - background: #5fb6f5; + background: #4da7e8; color: white; } diff --git a/modules/backend/widgets/table/assets/js/build-min.js b/modules/backend/widgets/table/assets/js/build-min.js index f6a9f321d..752a5a174 100644 --- a/modules/backend/widgets/table/assets/js/build-min.js +++ b/modules/backend/widgets/table/assets/js/build-min.js @@ -221,6 +221,7 @@ this.navigation.pageIndex=newPageIndex} this.recordsAddedOrDeleted++ var keyColumn=this.options.keyColumn,recordData={},self=this recordData[keyColumn]=-1*this.recordsAddedOrDeleted +this.$el.trigger('oc.tableNewRow',[recordData]) this.dataSource.createRecord(recordData,placement,relativeToKey,this.navigation.getPageFirstRowOffset(),this.options.recordsPerPage,function onAddRecordDataTableSuccess(records,totalCount){self.buildDataTable(records,totalCount) var row=self.findRowByKey(recordData[keyColumn]) if(!row) @@ -863,6 +864,54 @@ DropdownProcessor.prototype.elementBelongsToProcessor=function(element){if(!this return false return this.tableObj.parentContainsElement(this.itemListElement,element)} $.oc.table.processor.dropdown=DropdownProcessor;}(window.jQuery);+function($){"use strict";if($.oc.table===undefined) +throw new Error("The $.oc.table namespace is not defined. Make sure that the table.js script is loaded.");if($.oc.table.processor===undefined) +throw new Error("The $.oc.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");var Base=$.oc.table.processor.string,BaseProto=Base.prototype +var AutocompleteProcessor=function(tableObj,columnName,columnConfiguration){this.cachedOptionPromises={} +Base.call(this,tableObj,columnName,columnConfiguration)} +AutocompleteProcessor.prototype=Object.create(BaseProto) +AutocompleteProcessor.prototype.constructor=AutocompleteProcessor +AutocompleteProcessor.prototype.dispose=function(){this.cachedOptionPromises=null +BaseProto.dispose.call(this)} +AutocompleteProcessor.prototype.onUnfocus=function(){if(!this.activeCell) +return +this.removeAutocomplete() +BaseProto.onUnfocus.call(this)} +AutocompleteProcessor.prototype.renderCell=function(value,cellContentContainer){BaseProto.renderCell.call(this,value,cellContentContainer)} +AutocompleteProcessor.prototype.buildEditor=function(cellElement,cellContentContainer,isClick){BaseProto.buildEditor.call(this,cellElement,cellContentContainer,isClick) +var self=this +this.fetchOptions(cellElement,function autocompleteFetchOptions(options){self.buildAutoComplete(options) +self=null})} +AutocompleteProcessor.prototype.fetchOptions=function(cellElement,onSuccess){if(this.columnConfiguration.options){if(onSuccess!==undefined){onSuccess(this.columnConfiguration.options)}}else{if(this.triggerGetOptions(onSuccess)===false){return} +var row=cellElement.parentNode,cachingKey=this.createOptionsCachingKey(row),viewContainer=this.getViewContainer(cellElement) +$.oc.foundation.element.addClass(viewContainer,'loading') +if(!this.cachedOptionPromises[cachingKey]){var requestData={column:this.columnName,rowData:this.tableObj.getRowData(row)},handlerName=this.tableObj.getAlias()+'::onGetAutocompleteOptions' +this.cachedOptionPromises[cachingKey]=this.tableObj.$el.request(handlerName,{data:requestData})} +this.cachedOptionPromises[cachingKey].done(function onAutocompleteLoadOptionsSuccess(data){if(onSuccess!==undefined){onSuccess(data.options)}}).always(function onAutocompleteLoadOptionsAlways(){$.oc.foundation.element.removeClass(viewContainer,'loading')})}} +AutocompleteProcessor.prototype.createOptionsCachingKey=function(row){var cachingKey='non-dependent',dependsOn=this.columnConfiguration.dependsOn +if(dependsOn){if(typeof dependsOn=='object'){for(var i=0,len=dependsOn.length;i',bodyContainer:true})} +AutocompleteProcessor.prototype.prepareItems=function(items){var result={} +if($.isArray(items)){for(var i=0,len=items.length;i', + bodyContainer: true + }) + } + + AutocompleteProcessor.prototype.prepareItems = function(items) { + var result = {} + + if ($.isArray(items)) { + for (var i = 0, len = items.length; i < len; i++) { + result[items[i]] = items[i] + } + } + else { + result = items + } + + return result + } + + AutocompleteProcessor.prototype.removeAutocomplete = function() { + var input = this.getInput() + + $(input).autocomplete('destroy') + } + + $.oc.table.processor.autocomplete = AutocompleteProcessor; +}(window.jQuery); \ No newline at end of file diff --git a/modules/backend/widgets/table/assets/js/table.processor.dropdown.js b/modules/backend/widgets/table/assets/js/table.processor.dropdown.js index 6ad9e08d9..e0c5fb683 100644 --- a/modules/backend/widgets/table/assets/js/table.processor.dropdown.js +++ b/modules/backend/widgets/table/assets/js/table.processor.dropdown.js @@ -212,7 +212,7 @@ if (!this.cachedOptionPromises[cachingKey]) { var requestData = { - column: this.columnName, + column: this.columnName, rowData: this.tableObj.getRowData(row) }, handlerName = this.tableObj.getAlias()+'::onGetDropdownOptions' diff --git a/modules/backend/widgets/table/assets/less/table.less b/modules/backend/widgets/table/assets/less/table.less index 3e696a070..8f152a1d0 100644 --- a/modules/backend/widgets/table/assets/less/table.less +++ b/modules/backend/widgets/table/assets/less/table.less @@ -265,11 +265,12 @@ } /* - * String editor + * String and autocomplete editors */ .control-table { - td[data-column-type=string] { + td[data-column-type=string], + td[data-column-type=autocomplete] { input[type=text] { width: 100%; height: 100%; @@ -281,6 +282,19 @@ } } +ul.table-widget-autocomplete { + background: white; + font-size: 13px; + margin-top: 0; + border: 1px solid #808c8d; + border-top: 1px solid #ecf0f1; + .border-bottom-radius(4px); + + li a { + padding: 5px 10px; + } +} + /* * Checkbox editor */ diff --git a/modules/system/assets/ui/docs/input-monitor.md b/modules/system/assets/ui/docs/input-monitor.md index 01ebf0c6f..a6a197243 100644 --- a/modules/system/assets/ui/docs/input-monitor.md +++ b/modules/system/assets/ui/docs/input-monitor.md @@ -51,6 +51,7 @@ Click the "Mark changed" button and "Reload page". - changed.oc.changeMonitor - triggered when the form data changes. - unchanged.oc.changeMonitor - triggered when the form data unchanges. +- ready.oc.changeMonitor triggered when the change monitor instance finishes initialization. ## JavaScript API diff --git a/modules/system/assets/ui/js/foundation.element.js b/modules/system/assets/ui/js/foundation.element.js index 20b28465f..fd5fba037 100644 --- a/modules/system/assets/ui/js/foundation.element.js +++ b/modules/system/assets/ui/js/foundation.element.js @@ -131,6 +131,15 @@ input = null }, 0) } + }, + + elementContainsPoint: function(element, point) { + var elementPosition = $.oc.foundation.element.absolutePosition(element), + elementRight = elementPosition.left + element.offsetWidth, + elementBottom = elementPosition.top + element.offsetHeight + + return point.x >= elementPosition.left && point.x <= elementRight + && point.y >= elementPosition.top && point.y <= elementBottom } } diff --git a/modules/system/assets/ui/js/input.monitor.js b/modules/system/assets/ui/js/input.monitor.js index 9e7335917..ebbb1be50 100644 --- a/modules/system/assets/ui/js/input.monitor.js +++ b/modules/system/assets/ui/js/input.monitor.js @@ -39,6 +39,7 @@ $(window).on('beforeunload', this.proxy(this.onBeforeUnload)) this.$el.one('dispose-control', this.proxy(this.dispose)) + this.$el.trigger('ready.oc.changeMonitor') } ChangeMonitor.prototype.dispose = function() { diff --git a/modules/system/assets/ui/js/inspector.editor.autocomplete.js b/modules/system/assets/ui/js/inspector.editor.autocomplete.js index 4f79a1b6a..574c2ed3c 100644 --- a/modules/system/assets/ui/js/inspector.editor.autocomplete.js +++ b/modules/system/assets/ui/js/inspector.editor.autocomplete.js @@ -139,6 +139,10 @@ $.oc.foundation.element.addClass(container, 'loading-indicator-container size-small') this.showLoadingIndicator() + if (this.triggerGetItems(data) === false) { + return + } + data['inspectorProperty'] = this.propertyDefinition.property data['inspectorClassName'] = this.inspector.options.inspectorClass @@ -149,13 +153,37 @@ .always(this.proxy(this.hideLoadingIndicator)) } - AutocompleteEditor.prototype.itemsRequestDone = function(data, currentValue, initialization) { + AutocompleteEditor.prototype.triggerGetItems = function(values) { + var $inspectable = this.getInspectableElement() + if (!$inspectable) { + return true + } + + var itemsEvent = $.Event('autocompleteitems.oc.inspector') + + $inspectable.trigger(itemsEvent, [{ + values: values, + callback: this.proxy(this.itemsRequestDone), + property: this.inspector.getPropertyPath(this.propertyDefinition.property), + propertyDefinition: this.propertyDefinition + }]) + + if (itemsEvent.isDefaultPrevented()) { + return false + } + + return true + } + + AutocompleteEditor.prototype.itemsRequestDone = function(data) { if (this.isDisposed()) { // Handle the case when the asynchronous request finishes after // the editor is disposed return } + this.hideLoadingIndicator() + var loadedItems = {} if (data.options) { diff --git a/modules/system/assets/ui/js/inspector.editor.base.js b/modules/system/assets/ui/js/inspector.editor.base.js index 915ed0d7f..c0f0cd9f0 100644 --- a/modules/system/assets/ui/js/inspector.editor.base.js +++ b/modules/system/assets/ui/js/inspector.editor.base.js @@ -111,6 +111,18 @@ throw new Error(errorMessage + ' Property: ' + this.propertyDefinition.property) } + BaseEditor.prototype.getInspectableElement = function() { + return this.getRootSurface().getInspectableElement() + } + + BaseEditor.prototype.isEmptyValue = function(value) { + return value === undefined + || value === null + || (typeof value == 'object' && $.isEmptyObject(value) ) + || (typeof value == 'string' && $.trim(value).length === 0) + || (Object.prototype.toString.call(value) === '[object Array]' && value.length === 0) + } + // // Validation // @@ -127,7 +139,7 @@ return this.inspector.getPropertyValue(this.propertyDefinition.property) } - BaseEditor.prototype.validate = function() { + BaseEditor.prototype.validate = function(silentMode) { var value = this.getValueToValidate() if (value === undefined) { @@ -136,7 +148,9 @@ var validationResult = this.validationSet.validate(value) if (validationResult !== null) { - $.oc.flashMsg({text: validationResult, 'class': 'error', 'interval': 5}) + if (!silentMode) { + $.oc.flashMsg({text: validationResult, 'class': 'error', 'interval': 5}) + } return false } diff --git a/modules/system/assets/ui/js/inspector.editor.checkbox.js b/modules/system/assets/ui/js/inspector.editor.checkbox.js index fb7e676f2..14e399be7 100644 --- a/modules/system/assets/ui/js/inspector.editor.checkbox.js +++ b/modules/system/assets/ui/js/inspector.editor.checkbox.js @@ -79,6 +79,14 @@ this.getInput().checked = this.normalizeCheckedValue(value) } + CheckboxEditor.prototype.isEmptyValue = function(value) { + if (value === 0 || value === '0' || value === 'false') { + return true + } + + return BaseProto.isEmptyValue.call(this, value) + } + CheckboxEditor.prototype.registerHandlers = function() { var input = this.getInput() diff --git a/modules/system/assets/ui/js/inspector.editor.dropdown.js b/modules/system/assets/ui/js/inspector.editor.dropdown.js index 241eb5c2e..f77937371 100644 --- a/modules/system/assets/ui/js/inspector.editor.dropdown.js +++ b/modules/system/assets/ui/js/inspector.editor.dropdown.js @@ -54,11 +54,41 @@ } } + DropdownEditor.prototype.formatSelectOption = function(state) { + if (!state.id) + return state.text; // optgroup + + var option = state.element, + iconClass = option.getAttribute('data-icon'), + imageSrc = option.getAttribute('data-image') + + if (iconClass) { + return ' ' + state.text + } + + if (imageSrc) { + return ' ' + state.text + } + + return state.text + } + DropdownEditor.prototype.createOption = function(select, title, value) { var option = document.createElement('option') if (title !== null) { - option.textContent = title + if (!$.isArray(title)) { + option.textContent = title + } else { + if (title[1].indexOf('.') !== -1) { + option.setAttribute('data-image', title[1]) + } + else { + option.setAttribute('data-icon', title[1]) + } + + option.textContent = title[0] + } } if (value !== null) { @@ -89,6 +119,12 @@ options.placeholder = this.propertyDefinition.placeholder } + options.templateResult = this.formatSelectOption + options.templateSelection = this.formatSelectOption + options.escapeMarkup = function(m) { + return m + } + $(select).select2(options) if (!Modernizr.touch) { @@ -135,6 +171,24 @@ return false } + DropdownEditor.prototype.normalizeValue = function(value) { + if (!this.propertyDefinition.booleanValues) { + return value + } + + var str = String(value) + + if (str.length === 0) { + return '' + } + + if (str === 'true') { + return true + } + + return false + } + // // Event handlers // @@ -148,7 +202,7 @@ DropdownEditor.prototype.onSelectionChange = function() { var select = this.getSelect() - this.inspector.setPropertyValue(this.propertyDefinition.property, select.value, this.initialization) + this.inspector.setPropertyValue(this.propertyDefinition.property, this.normalizeValue(select.value), this.initialization) } DropdownEditor.prototype.onInspectorPropertyChanged = function(property, value) { @@ -192,12 +246,24 @@ var select = this.getSelect() if (select) { - return select.value + return this.normalizeValue(select.value) } return undefined } + DropdownEditor.prototype.isEmptyValue = function(value) { + if (this.propertyDefinition.booleanValues) { + if (value === '') { + return true + } + + return false + } + + return BaseProto.isEmptyValue.call(this, value) + } + // // Disposing // @@ -248,6 +314,11 @@ currentValue = this.propertyDefinition.default } + var callback = function dropdownOptionsRequestDoneClosure(data) { + self.hideLoadingIndicator() + self.optionsRequestDone(data, currentValue, true) + } + if (this.propertyDefinition.depends) { this.saveDependencyValues() } @@ -257,15 +328,39 @@ this.showLoadingIndicator() + if (this.triggerGetOptions(data, callback) === false) { + return + } + $form.request('onInspectableGetOptions', { data: data, - }).done(function dropdownOptionsRequestDoneClosure(data) { - self.optionsRequestDone(data, currentValue, true) - }).always( + }).done(callback).always( this.proxy(this.hideLoadingIndicator) ) } + DropdownEditor.prototype.triggerGetOptions = function(values, callback) { + var $inspectable = this.getInspectableElement() + if (!$inspectable) { + return true + } + + var optionsEvent = $.Event('dropdownoptions.oc.inspector') + + $inspectable.trigger(optionsEvent, [{ + values: values, + callback: callback, + property: this.inspector.getPropertyPath(this.propertyDefinition.property), + propertyDefinition: this.propertyDefinition + }]) + + if (optionsEvent.isDefaultPrevented()) { + return false + } + + return true + } + DropdownEditor.prototype.saveDependencyValues = function() { this.prevDependencyValues = this.getDependencyValues() } diff --git a/modules/system/assets/ui/js/inspector.editor.object.js b/modules/system/assets/ui/js/inspector.editor.object.js index a8922111b..d88c28118 100644 --- a/modules/system/assets/ui/js/inspector.editor.object.js +++ b/modules/system/assets/ui/js/inspector.editor.object.js @@ -49,7 +49,8 @@ this.inspector.getInspectorUniqueId() + '-' + this.propertyDefinition.property, options, this.inspector, - this.group) + this.group, + this.propertyDefinition.property) this.inspector.mergeChildSurface(this.childInspector, currentRow) } @@ -70,14 +71,6 @@ return this.getValueOrRemove(value) } - ObjectEditor.prototype.isEmptyValue = function(value) { - return value === undefined - || value === null - || $.isEmptyObject(value) - || (typeof value == 'string' && $.trim(value).length === 0) - || (Object.prototype.toString.call(value) === '[object Array]' && value.length === 0) - } - ObjectEditor.prototype.getValueOrRemove = function(value) { if (this.propertyDefinition.ignoreIfPropertyEmpty === undefined) { return value @@ -120,8 +113,8 @@ return this.getValueOrRemove(result) } - ObjectEditor.prototype.validate = function() { - var values = this.childInspector.getValues() + ObjectEditor.prototype.validate = function(silentMode) { + var values = values = this.childInspector.getValues() if (this.cleanUpValue(values) === $.oc.inspector.removedProperty) { // Ignore any validation rules if the object's required @@ -130,7 +123,7 @@ return true } - return this.childInspector.validate() + return this.childInspector.validate(silentMode) } // diff --git a/modules/system/assets/ui/js/inspector.editor.set.js b/modules/system/assets/ui/js/inspector.editor.set.js index a8aa1b320..6bc6f759f 100644 --- a/modules/system/assets/ui/js/inspector.editor.set.js +++ b/modules/system/assets/ui/js/inspector.editor.set.js @@ -319,19 +319,21 @@ currentValue = [] } - if (isChecked) { - if (currentValue.indexOf(checkboxValue) === -1) { - currentValue.push(checkboxValue) - } - } - else { - var index = currentValue.indexOf(checkboxValue) - if (index !== -1) { - currentValue.splice(index, 1) + var resultValue = [] + for (var itemValue in this.propertyDefinition.items) { + if (itemValue !== checkboxValue) { + if (currentValue.indexOf(itemValue) !== -1) { + resultValue.push(itemValue) + } + } + else { + if (isChecked) { + resultValue.push(itemValue) + } } } - this.inspector.setPropertyValue(this.propertyDefinition.property, this.cleanUpValue(currentValue)) + this.inspector.setPropertyValue(this.propertyDefinition.property, this.cleanUpValue(resultValue)) this.setLinkText(this.getLink()) } diff --git a/modules/system/assets/ui/js/inspector.editor.stringlist.js b/modules/system/assets/ui/js/inspector.editor.stringlist.js index ee03a74ac..2dd009201 100644 --- a/modules/system/assets/ui/js/inspector.editor.stringlist.js +++ b/modules/system/assets/ui/js/inspector.editor.stringlist.js @@ -65,6 +65,8 @@ } $textarea.focus() + + this.configureComment(popup) } StringListEditor.prototype.handleSubmit = function($form) { @@ -88,7 +90,6 @@ } this.inspector.setPropertyValue(this.propertyDefinition.property, resultValue) -// TODO: validate here } $.oc.inspector.propertyEditors.stringList = StringListEditor diff --git a/modules/system/assets/ui/js/inspector.editor.text.js b/modules/system/assets/ui/js/inspector.editor.text.js index bbcf5d505..27903ebf7 100644 --- a/modules/system/assets/ui/js/inspector.editor.text.js +++ b/modules/system/assets/ui/js/inspector.editor.text.js @@ -46,6 +46,7 @@ \