Updated select2, implementing new Inspector, work in progress.

This commit is contained in:
alekseybobkov 2015-09-17 21:49:21 -07:00
parent c4e7427669
commit 86ca09970d
50 changed files with 588 additions and 32 deletions

View File

@ -48,6 +48,27 @@
el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
},
toggleClass: function(el, className, add) {
if (add === undefined) {
if (this.hasClass(el, className)) {
this.removeClass(el, className)
}
else {
this.addClass(el, className)
}
}
if (add && !this.hasClass(el, className)) {
this.addClass(el, className)
return
}
if (!add && this.hasClass(el, className)) {
this.removeClass(el, className)
return
}
},
/*
* Returns element absolution position.
* If the second parameter value is false, the scrolling

View File

@ -0,0 +1,88 @@
/*
* Inspector checkbox editor class.
*/
+function ($) { "use strict";
var Base = $.oc.inspector.propertyEditors.base,
BaseProto = Base.prototype
var CheckboxEditor = function(inspector, propertyDefinition, containerCell) {
Base.call(this, inspector, propertyDefinition, containerCell)
}
CheckboxEditor.prototype = Object.create(BaseProto)
CheckboxEditor.prototype.constructor = Base
CheckboxEditor.prototype.dispose = function() {
this.unregisterHandlers()
BaseProto.dispose.call(this)
}
CheckboxEditor.prototype.build = function() {
var editor = document.createElement('input'),
container = document.createElement('div'),
value = this.inspector.getPropertyValue(this.propertyDefinition.property),
label = document.createElement('label'),
isChecked = false,
id = this.inspector.generateSequencedId()
container.setAttribute('tabindex', 0)
container.setAttribute('class', 'custom-checkbox nolabel')
editor.setAttribute('type', 'checkbox')
editor.setAttribute('value', '1')
editor.setAttribute('placeholder', 'placeholder')
editor.setAttribute('id', id)
label.setAttribute('for', id)
label.textContent = this.propertyDefinition.title
container.appendChild(editor)
container.appendChild(label)
if (value === undefined) {
if (this.propertyDefinition.default !== undefined) {
isChecked = this.normalizeCheckedValue(this.propertyDefinition.default)
}
}
else {
isChecked = this.normalizeCheckedValue(value)
}
editor.checked = isChecked
this.containerCell.appendChild(container)
}
CheckboxEditor.prototype.normalizeCheckedValue = function(value) {
if (value == '0' || value == 'false')
return false
return value
}
CheckboxEditor.prototype.getInput = function() {
return this.containerCell.querySelector('input')
}
CheckboxEditor.prototype.registerHandlers = function() {
var input = this.getInput()
input.addEventListener('change', this.proxy(this.onInputChange))
}
CheckboxEditor.prototype.unregisterHandlers = function() {
var input = this.getInput()
input.removeEventListener('change', this.proxy(this.onInputChange))
}
CheckboxEditor.prototype.onInputChange = function() {
var isChecked = this.getInput().checked
this.inspector.setPropertyValue(this.propertyDefinition.property, isChecked ? 1 : 0)
}
$.oc.inspector.propertyEditors.checkbox = CheckboxEditor
}(window.jQuery);

View File

@ -0,0 +1,278 @@
/*
* Inspector checkbox dropdown class.
*/
+function ($) { "use strict";
var Base = $.oc.inspector.propertyEditors.base,
BaseProto = Base.prototype
var DropdownEditor = function(inspector, propertyDefinition, containerCell) {
this.indicatorContainer = null
Base.call(this, inspector, propertyDefinition, containerCell)
}
DropdownEditor.prototype = Object.create(BaseProto)
DropdownEditor.prototype.constructor = Base
DropdownEditor.prototype.init = function() {
this.dynamicOptions = this.propertyDefinition.options ? false : true
this.initialization = false
BaseProto.init.call(this)
}
DropdownEditor.prototype.dispose = function() {
this.unregisterHandlers()
this.destroyCustomSelect()
this.indicatorContainer = null
BaseProto.dispose.call(this)
}
//
// Building
//
DropdownEditor.prototype.build = function() {
var select = document.createElement('select')
$.oc.foundation.element.addClass(this.containerCell, 'dropdown')
$.oc.foundation.element.addClass(select, 'custom-select')
if (!this.dynamicOptions) {
this.loadStaticOptions(select)
}
this.containerCell.appendChild(select)
this.initCustomSelect()
if (this.dynamicOptions) {
this.loadDynamicOptions(true)
}
}
DropdownEditor.prototype.createOption = function(select, title, value) {
var option = document.createElement('option')
if (title !== null) {
option.textContent = title
}
if (value !== null) {
option.value = value
}
select.appendChild(option)
}
DropdownEditor.prototype.createOptions = function(select, options) {
for (var value in options) {
this.createOption(select, options[value], value)
}
}
DropdownEditor.prototype.initCustomSelect = function() {
var select = this.getSelect()
if (Modernizr.touch) {
return
}
var options = {
dropdownCssClass: 'ocInspectorDropdown'
}
if (this.propertyDefinition.placeholder !== undefined) {
options.placeholder = this.propertyDefinition.placeholder
}
$(select).select2(options)
if (!Modernizr.touch) {
this.indicatorContainer = $('.select2-container', this.containerCell)
this.indicatorContainer.addClass('loading-indicator-container size-small')
}
}
DropdownEditor.prototype.createPlaceholder = function(select) {
var placeholder = this.propertyDefinition.placeholder
if (placeholder !== undefined && !Modernizr.touch) {
this.createOption(select, null, null)
}
if (placeholder !== undefined && Modernizr.touch) {
this.createOption(select, placeholder, null)
}
}
//
// Helpers
//
DropdownEditor.prototype.getSelect = function() {
return this.containerCell.querySelector('select')
}
DropdownEditor.prototype.clearOptions = function(select) {
while (select.firstChild) {
select.removeChild(select.firstChild)
}
}
DropdownEditor.prototype.hasOptionValue = function(select, value) {
var options = select.children
for (var i = 0, len = options.length; i < len; i++) {
if (options[i].value == value) {
return true
}
}
return false
}
//
// Event handlers
//
DropdownEditor.prototype.registerHandlers = function() {
var select = this.getSelect()
$(select).on('change', this.proxy(this.onSelectionChange))
}
DropdownEditor.prototype.onSelectionChange = function() {
var select = this.getSelect()
this.inspector.setPropertyValue(this.propertyDefinition.property, select.value, this.initialization)
}
//
// Disposing
//
DropdownEditor.prototype.destroyCustomSelect = function() {
var select = this.getSelect()
$(select).select2('destroy')
}
DropdownEditor.prototype.unregisterHandlers = function() {
var select = this.getSelect()
$(select).off('change', this.proxy(this.onSelectionChange))
}
//
// Static options
//
DropdownEditor.prototype.loadStaticOptions = function(select) {
var value = this.inspector.getPropertyValue(this.propertyDefinition.property)
this.createPlaceholder(select)
this.createOptions(select, this.propertyDefinition.options)
if (value === undefined) {
value = this.propertyDefinition.default
}
select.value = value
}
//
// Dynamic options
//
DropdownEditor.prototype.loadDynamicOptions = function(initialization) {
var currentValue = this.inspector.getPropertyValue(this.propertyDefinition.property),
data = this.inspector.getValues(),
self = this,
$form = $(this.getSelect()).closest('form')
if (currentValue === undefined) {
currentValue = this.propertyDefinition.default
}
if (this.propertyDefinition.depends) {
this.saveDependencyValues()
}
data['inspectorProperty'] = this.propertyDefinition.property
data['inspectorClassName'] = this.inspector.options.inspectorClass
this.showLoadingIndicator()
$form.request('onInspectableGetOptions', {
data: data,
}).done(function optionsRequestDoneClosure(data) {
self.optionsRequestDone(data, currentValue, true)
}).always(
this.proxy(this.hideLoadingIndicator)
)
}
DropdownEditor.prototype.saveDependencyValues = function() {
this.prevDependencyValues = this.getDependencyValues()
}
DropdownEditor.prototype.getDependencyValues = function() {
var result = ''
for (var property in this.propertyDefinition.depends) {
var value = this.inspector.getPropertyValue(property)
if (value === undefined) {
value = '';
}
result += property + ':' + value + '-'
}
return result
}
DropdownEditor.prototype.showLoadingIndicator = function() {
if (!Modernizr.touch) {
this.indicatorContainer.loadIndicator()
}
}
DropdownEditor.prototype.hideLoadingIndicator = function() {
if (!Modernizr.touch) {
this.indicatorContainer.loadIndicator('hide')
}
}
DropdownEditor.prototype.optionsRequestDone = function(data, currentValue, initialization) {
var select = this.getSelect()
this.clearOptions(select)
this.createPlaceholder(select)
if (data.options) {
for (var i = 0, len = data.options.length; i < len; i++) {
this.createOption(select, data.options[i].title, data.options[i].value)
}
}
if (this.hasOptionValue(select, currentValue)) {
select.value = currentValue
}
else {
select.selectedIndex = this.propertyDefinition.placeholder === undefined ? 0 : -1
}
this.initialization = initialization
$(select).trigger('change')
this.initialization = false
}
$.oc.inspector.propertyEditors.dropdown = DropdownEditor
}(window.jQuery);

View File

@ -47,22 +47,24 @@
var input = this.getInput()
input.addEventListener('focus', this.proxy(this.onInputFocus))
input.addEventListener('change', this.proxy(this.onInputChange))
input.addEventListener('keyup', this.proxy(this.onInputKeyUp))
}
StringEditor.prototype.unregisterHandlers = function() {
var input = this.getInput()
input.removeEventListener('focus', this.proxy(this.onInputFocus))
input.removeEventListener('change', this.proxy(this.onInputChange))
input.removeEventListener('keyup', this.proxy(this.onInputKeyUp))
}
StringEditor.prototype.onInputFocus = function(ev) {
this.inspector.makeCellActive(this.containerCell)
}
StringEditor.prototype.onInputChange = function() {
StringEditor.prototype.onInputKeyUp = function() {
var value = this.getInput().value
this.inspector.setPropertyValue(this.propertyDefinition.property, value)
}
$.oc.inspector.propertyEditors.string = StringEditor

View File

@ -24,7 +24,7 @@
for (var i = 0, len = properties.length; i < len; i++) {
var property = properties[i]
if (property.itemType !== undefined && property.itemType == 'group' && item.title == group) {
if (property.itemType !== undefined && property.itemType == 'group' && property.title == group) {
return property
}
}
@ -50,7 +50,7 @@
fields.push(property)
}
else {
var group = findGroup(property.group)
var group = findGroup(property.group, fields)
if (!group) {
group = {
@ -69,8 +69,8 @@
}
}
for (var i = 0, len = properties.length; i < len; i++) {
var property = properties[i]
for (var i = 0, len = fields.length; i < len; i++) {
var property = fields[i]
result.properties.push(property)

View File

@ -36,12 +36,18 @@
* related to an element in the document DOM.
*/
var Surface = function(containerElement, properties, values, inspectorUniqueId, options) {
if (inspectorUniqueId === undefined) {
throw new Error('Inspector surface unique ID should be defined.')
}
this.options = $.extend({}, Surface.DEFAULTS, typeof option == 'object' && option)
this.rawProperties = properties
this.parsedProperties = $.oc.inspector.engine.processPropertyGroups(properties)
this.container = containerElement
this.inspectorUniqueId = inspectorUniqueId
this.values = values
this.originalValues = $.extend(true, {}, values) // Clone the values hash
this.idCounter = 1
this.editors = []
this.tableContainer = null
@ -55,6 +61,7 @@
Surface.prototype.constructor = Surface
Surface.prototype.dispose = function() {
this.unregisterHandlers()
this.removeElements()
this.disposeEditors()
@ -64,6 +71,7 @@
this.parsedProperties = null
this.editors = null
this.values = null
this.originalValues = null
BaseProto.dispose.call(this)
}
@ -73,6 +81,20 @@
Surface.prototype.init = function() {
this.build()
$.oc.foundation.controlUtils.markDisposable(this.tableContainer)
this.registerHandlers()
}
Surface.prototype.registerHandlers = function() {
$(this.tableContainer).one('dispose-control', this.proxy(this.dispose))
$(this.tableContainer).on('click', 'tr.group', this.proxy(this.onGroupClick))
}
Surface.prototype.unregisterHandlers = function() {
$(this.tableContainer).off('dispose-control', this.proxy(this.dispose))
$(this.tableContainer).off('click', 'tr.group', this.proxy(this.onGroupClick))
}
//
@ -125,7 +147,10 @@
// Table row
//
row.setAttribute('data-property', property.property)
if (property.property) {
row.setAttribute('data-property', property.property)
}
this.applyGroupIndexAttribute(property, row)
$.oc.foundation.element.addClass(row, this.getRowCssClass(property))
@ -242,6 +267,14 @@
return {}
}
Surface.prototype.writeGroupStatuses = function(updatedStatuses) {
var statuses = this.getInspectorGroupStatuses()
statuses[this.inspectorUniqueId] = updatedStatuses
this.setInspectorGroupStatuses(statuses)
}
Surface.prototype.getInspectorGroupStatuses = function() {
var statuses = document.body.getAttribute('data-inspector-group-statuses')
@ -252,6 +285,48 @@
return {}
}
Surface.prototype.setInspectorGroupStatuses = function(statuses) {
document.body.setAttribute('data-inspector-group-statuses', JSON.stringify(statuses))
}
Surface.prototype.toggleGroup = function(row) {
var link = row.querySelector('a'),
groupIndex = link.getAttribute('data-group-index'),
propertyRows = this.tableContainer.querySelectorAll('tr[data-group-index="'+groupIndex+'"]'),
collapse = true,
statuses = this.loadGroupStatuses(),
title = row.querySelector('span.title-element').getAttribute('title'),
duration = Math.round(50 / propertyRows.length),
rowsArray = Array.prototype.slice.call(propertyRows)
if ($.oc.foundation.element.hasClass(link, 'expanded')) {
$.oc.foundation.element.removeClass(link, 'expanded')
statuses[title] = false
} else {
$.oc.foundation.element.addClass(link, 'expanded')
collapse = false
statuses[title] = true
}
this.expandOrCollapseRows(rowsArray, collapse, duration)
this.writeGroupStatuses(statuses)
}
Surface.prototype.expandOrCollapseRows = function(rows, collapse, duration) {
var row = rows.pop(),
self = this
if (row) {
setTimeout(function toggleRow() {
$.oc.foundation.element.toggleClass(row, 'collapsed', collapse)
$.oc.foundation.element.toggleClass(row, 'expanded', !collapse)
self.expandOrCollapseRows(rows, collapse, duration)
}, duration)
}
}
//
// Editors
//
@ -265,7 +340,7 @@
throw new Error('The Inspector editor class "' + property.type +
'" is not defined in the $.oc.inspector.propertyEditors namespace.')
var cell = document.createElement('td'),
var cell = document.createElement('td'),
editor = new $.oc.inspector.propertyEditors[property.type](this, property, cell)
this.editors.push(editor)
@ -273,6 +348,12 @@
row.appendChild(cell)
}
Surface.prototype.generateSequencedId = function() {
this.idCounter ++
return this.inspectorUniqueId + '-' + this.idCounter
}
//
// Internal API for the editors
//
@ -281,6 +362,45 @@
return this.values[property]
}
Surface.prototype.getValues = function() {
var result = {}
for (var i=0, len = this.parsedProperties.properties.length; i < len; i++) {
var property = this.parsedProperties.properties[i]
if (property.itemType !== 'property') {
continue
}
var value = this.getPropertyValue(property.property)
if (value === undefined) {
value = property.default
}
result[property.property] = value
}
return result
}
Surface.prototype.setPropertyValue = function(property, value, supressChangeEvents) {
this.values[property] = value
if (!supressChangeEvents) {
if (this.originalValues[property] === undefined || this.originalValues[property] != value) {
this.markPropertyChanged(property, true)
}
else {
this.markPropertyChanged(property, false)
}
// TODO: here we should force dependent editors to update
}
return value
}
Surface.prototype.makeCellActive = function(cell) {
var tbody = cell.parentNode.parentNode.parentNode, // cell / row / tbody
cells = tbody.querySelectorAll('tr td')
@ -292,6 +412,17 @@
$.oc.foundation.element.addClass(cell, 'active')
}
Surface.prototype.markPropertyChanged = function(property, changed) {
var row = this.tableContainer.querySelector('tr[data-property="'+property+'"]')
if (changed) {
$.oc.foundation.element.addClass(row, 'changed')
}
else {
$.oc.foundation.element.removeClass(row, 'changed')
}
}
//
// Disposing
//
@ -318,6 +449,15 @@
return div.innerHTML
}
// EVENT HANDLERS
//
Surface.prototype.onGroupClick = function(ev) {
var row = ev.currentTarget
this.toggleGroup(row)
}
// DEFAULT OPTIONS
// ============================

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,18 @@
There's a hack in select2.full.js. The hack prevents DOM element leakage through the event handler. Added/updated code:
Line 3961, added .select2 namespace in the event handler registration:
this.$dropdownContainer.on('mousedown.select2', function (evt) {
Lines 4120-4124. Added new method:
```
AttachBody.prototype.destroy = function(decorated) {
this.$dropdownContainer.off('.select2')
decorated.call(this);
}
```
Filed an issue: https://github.com/select2/select2/issues/3774
The issue was closed by the developers - the problem is going to be fixed in the upcoming 4.0.1 release.

0
modules/system/assets/ui/vendor/select2/js/i18n/az.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/bg.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/ca.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/cs.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/da.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/de.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/en.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/es.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/et.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/eu.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/fa.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/fi.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/fr.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/gl.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/he.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/hi.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/hr.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/hu.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/id.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/is.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/it.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/ko.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/lt.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/lv.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/mk.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/nb.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/nl.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/pl.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/pt-BR.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/pt.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/ro.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/ru.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/sk.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/sr.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/sv.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/th.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/tr.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/uk.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/vi.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/zh-CN.js vendored Normal file → Executable file
View File

0
modules/system/assets/ui/vendor/select2/js/i18n/zh-TW.js vendored Normal file → Executable file
View File

View File

@ -3958,7 +3958,7 @@ S2.define('select2/dropdown/attachBody',[
self._detachPositioningHandler(container);
});
this.$dropdownContainer.on('mousedown', function (evt) {
this.$dropdownContainer.on('mousedown.select2', function (evt) {
evt.stopPropagation();
});
};
@ -4117,6 +4117,12 @@ S2.define('select2/dropdown/attachBody',[
this._resizeDropdown();
};
AttachBody.prototype.destroy = function(decorated) {
this.$dropdownContainer.off('.select2')
decorated.call(this);
}
return AttachBody;
});