Added popup and container wrappers for Inspector, minor fixes in Inspector editors, minor extensions in the popup API.

This commit is contained in:
alekseybobkov 2015-10-13 20:28:41 -07:00
parent 409c40c248
commit 6ddc366808
18 changed files with 1755 additions and 1181 deletions

View File

@ -0,0 +1,160 @@
/*
* Inspector data interaction class.
*
* Provides methods for loading and writing Inspector configuration
* and values form and to inspectable elements.
*/
+function ($) { "use strict";
// CLASS DEFINITION
// ============================
var Base = $.oc.foundation.base,
BaseProto = Base.prototype
var DataInteraction = function(element) {
this.element = element
Base.call(this)
}
DataInteraction.prototype = Object.create(BaseProto)
DataInteraction.prototype.constructor = Base
DataInteraction.prototype.dispose = function() {
this.element = null
BaseProto.dispose.call(this)
}
DataInteraction.prototype.getElementValuesInput = function() {
return this.element.querySelector('input[data-inspector-values]')
}
DataInteraction.prototype.normalizePropertyCode = function(code, configuration) {
var lowerCaseCode = code.toLowerCase()
for (var index in configuration) {
var propertyInfo = configuration[index]
if (propertyInfo.property.toLowerCase() == lowerCaseCode) {
return propertyInfo.property
}
}
return code
}
DataInteraction.prototype.loadValues = function(configuration) {
var valuesField = this.getElementValuesInput()
if (valuesField) {
var valuesStr = $.trim(valuesField.value)
try {
return valuesStr.length === 0 ? {} : $.parseJSON(valuesStr)
} catch (err) {
throw new Error('Error parsing Inspector field values. ' + err)
}
}
var values = {},
attributes = this.element.attributes
for (var i=0, len = attributes.length; i < len; i++) {
var attribute = attributes[i],
matches = []
if (matches = attribute.name.match(/^data-property-(.*)$/)) {
// Important - values contained in data-property-xxx attributes are
// considered strings and never parsed with JSON. The use of the
// data-property-xxx attributes is very limited - they're only
// used in Pages for creating snippets from partials, where properties
// are created with a table UI widget, which doesn't allow creating
// properties of any complex types.
//
// There is no a technically reliable way to determine when a string
// is a JSON data or a regular string. Users can enter a value
// like [10], which is a proper JSON value, but meant to be a string.
//
// One possible way to resolve it, if to check the property type loaded
// from the configuration and see if the corresponding editor expects
// complex data.
var normalizedPropertyName = normalizePropertyCode(matches[1], configuration)
values[normalizedPropertyName] = attribute.value
}
}
return values
}
DataInteraction.prototype.loadConfiguration = function(onComplete) {
var configurationField = this.element.querySelector('input[data-inspector-config]'),
result = {
configuration: {},
title: null,
description: null
},
$element = $(this.element)
result.title = $element.data('inspector-title')
result.description = $element.data('inspector-description')
if (configurationField) {
result.configuration = this.parseConfiguration(configurationField.value)
onComplete(result, this)
return
}
var $form = $element.closest('form'),
data = $element.data(),
self = this
$.oc.stripeLoadIndicator.show()
var request = $form.request('onGetInspectorConfiguration', {
data: data
}).done(function inspectorConfigurationRequestDoneClosure(data) {
self.configurartionRequestDone(data, onComplete, result)
}).always(function() {
$.oc.stripeLoadIndicator.hide()
})
}
//
// Internal methods
//
DataInteraction.prototype.parseConfiguration = function(configuration) {
if (!$.isArray(configuration) && !$.isPlainObject(configuration)) {
if ($.trim(configuration) === 0) {
return {}
}
try {
return $.parseJSON(configuration)
} catch(err) {
throw new Error('Error parsing Inspector configuration. ' + err)
}
} else {
return configuration
}
}
DataInteraction.prototype.configurartionRequestDone = function(data, onComplete, result) {
result.configuration = this.parseConfiguration(data.configuration.properties)
if (data.configuration.title !== undefined) {
result.title = data.configuration.title
}
if (data.configuration.description !== undefined) {
result.description = data.configuration.description
}
onComplete(result, this)
}
$.oc.inspector.dataInteraction = DataInteraction
}(window.jQuery);

View File

@ -81,6 +81,10 @@
return this.childInspector !== null
}
BaseEditor.prototype.getRootSurface = function() {
return this.inspector.getRootSurface()
}
/**
* Updates displayed value in the editor UI. The value is already set
* in the Inspector and should be loaded from Inspector.

View File

@ -631,6 +631,7 @@
this.popup = popup.get(0)
this.buildPopupContents(this.popup)
this.getRootSurface().popupDisplayed()
}
ObjectListEditor.prototype.onPopupHidden = function(ev, link, popup) {
@ -642,6 +643,7 @@
$.oc.foundation.controlUtils.disposeControls(this.popup)
this.popup = null
this.getRootSurface().popupHidden()
}
ObjectListEditor.prototype.onSubmit = function(ev) {

View File

@ -110,11 +110,15 @@
this.popup = popup.get(0)
this.configurePopup(popup)
this.getRootSurface().popupDisplayed()
}
PopupBase.prototype.onPopupHidden = function(ev, link, popup) {
$(popup).off('.inspector', 'form', this.proxy(this.onSubmit))
this.popup = null
this.getRootSurface().popupHidden()
}
PopupBase.prototype.onSubmit = function(ev) {

View File

@ -78,7 +78,6 @@
value = $.trim($textarea.val())
this.inspector.setPropertyValue(this.propertyDefinition.property, value)
// TODO: validate here
}
$.oc.inspector.propertyEditors.text = TextEditor

View File

@ -87,5 +87,4 @@
return result
}
}(window.jQuery);

View File

@ -29,4 +29,5 @@
$.oc.inspector.helpers.generateUniqueId = function() {
return "inspectorid-" + Math.floor(Math.random() * new Date().getTime());
}
}(window.jQuery)

View File

@ -0,0 +1,141 @@
/*
* Inspector management functions.
*
* Watches inspectable elements clicks and creates Inspector surfaces in popups
* and containers.
*/
+function ($) { "use strict";
var Base = $.oc.foundation.base,
BaseProto = Base.prototype
var InspectorManager = function() {
Base.call(this)
this.init()
}
InspectorManager.prototype = Object.create(BaseProto)
InspectorManager.prototype.constructor = Base
InspectorManager.prototype.init = function() {
$(document).on('click', '[data-inspectable]', this.proxy(this.onInspectableClicked))
}
InspectorManager.prototype.getContainerElement = function($element) {
var $containerHolder = $element.closest('[data-inspector-container]')
if ($containerHolder.length === 0) {
return null
}
var $container = $containerHolder.find($containerHolder.data('inspector-container'))
if ($container.length === 0) {
throw new Error('Inspector container ' + $containerHolder.data['inspector-container'] + ' element is not found.')
}
return $container
}
InspectorManager.prototype.createInspectorPopup = function($element, containerSupported) {
new $.oc.inspector.wrappers.popup($element, null, {
containerSupported: containerSupported
})
}
InspectorManager.prototype.createInspectorContainer = function($element, $container) {
new $.oc.inspector.wrappers.container($element, null, {
containerSupported: true,
container: $container
})
}
InspectorManager.prototype.switchToPopup = function(wrapper) {
new $.oc.inspector.wrappers.popup(wrapper.$element, wrapper, {
containerSupported: true
})
wrapper.cleanupAfterSwitch()
this.setContainerPreference(false)
}
InspectorManager.prototype.switchToContainer = function(wrapper) {
var $container = this.getContainerElement(wrapper.$element)
if (!$container) {
throw new Error('Cannot switch to container: a container element is not found')
}
new $.oc.inspector.wrappers.container(wrapper.$element, wrapper, {
containerSupported: true,
container: $container
})
wrapper.cleanupAfterSwitch()
this.setContainerPreference(true)
}
InspectorManager.prototype.createInspector = function($element) {
var $container = this.getContainerElement($element)
// If there's no container option, create the Inspector popup
//
if (!$container) {
this.createInspectorPopup($element, false)
}
else {
// If the container is already in use, apply values to the inspectable elements
if (!this.applyValuesFromContainer($container)) {
return
}
// Dispose existing container wrapper, if any
$.oc.foundation.controlUtils.disposeControls($container.get(0))
if (!this.getContainerPreference()) {
// If container is not a preferred option, create Inspector popoup
this.createInspectorPopup($element, true)
}
else {
// Otherwise, create Inspector in the container
this.createInspectorContainer($element, $container)
}
}
}
InspectorManager.prototype.getContainerPreference = function() {
if (!Modernizr.localstorage) {
return false
}
return localStorage.getItem('oc.inspectorUseContainer') === "true"
}
InspectorManager.prototype.setContainerPreference = function(value) {
if (!Modernizr.localstorage) {
return
}
return localStorage.setItem('oc.inspectorUseContainer', value ? "true" : "false")
}
InspectorManager.prototype.applyValuesFromContainer = function($container) {
var applyEvent = $.Event('apply.oc.inspector')
$container.trigger(applyEvent)
if (applyEvent.isDefaultPrevented()) {
return false
}
return true
}
InspectorManager.prototype.onInspectableClicked = function(ev) {
var $element = $(ev.currentTarget)
if ($element.data('oc.inspectorVisible'))
return false
this.createInspector($element)
}
$.oc.inspector.manager = new InspectorManager()
}(window.jQuery);

View File

@ -48,6 +48,7 @@
this.values = values
this.originalValues = $.extend(true, {}, values) // Clone the values hash
this.idCounter = 1
this.popupCounter = 0
this.parentSurface = parentSurface
this.editors = []
@ -88,6 +89,8 @@
this.values = null
this.originalValues = null
this.options.onChange = null
this.options.onPopupDisplayed = null
this.options.onPopupHidden = null
this.parentSurface = null
this.groupManager = null
this.group = null
@ -212,6 +215,12 @@
}
}
Surface.prototype.moveToContainer = function(newContainer) {
this.container = newContainer
this.container.appendChild(this.tableContainer)
}
Surface.prototype.buildRow = function(property, group) {
var row = document.createElement('tr'),
th = document.createElement('th'),
@ -611,6 +620,26 @@
}
}
Surface.prototype.popupDisplayed = function() {
if (this.popupCounter === 0 && this.options.onPopupDisplayed !== null) {
this.options.onPopupDisplayed()
}
this.popupCounter++
}
Surface.prototype.popupHidden = function() {
this.popupCounter--
if (this.popupCounter < 0) {
this.popupCounter = 0
}
if (this.popupCounter === 0 && this.options.onPopupHidden !== null) {
this.options.onPopupHidden()
}
}
//
// Nested surfaces support
//
@ -809,7 +838,9 @@
Surface.DEFAULTS = {
enableExternalParameterEditor: false,
onChange: null
onChange: null,
onPopupDisplayed: null,
onPopupHidden: null
}
// REGISTRATION

View File

@ -0,0 +1,288 @@
/*
* Inspector wrapper base class.
*/
+function ($) { "use strict";
// NAMESPACES
// ============================
if ($.oc.inspector === undefined)
$.oc.inspector = {}
if ($.oc.inspector.wrappers === undefined)
$.oc.inspector.wrappers = {}
// CLASS DEFINITION
// ============================
var Base = $.oc.foundation.base,
BaseProto = Base.prototype
var BaseWrapper = function($element, sourceWrapper, options) {
this.$element = $element
if (!sourceWrapper) {
this.surface = surface
this.title = null
this.description = null
}
else {
this.surface = sourceWrapper.surface
this.title = sourceWrapper.title
this.description = sourceWrapper.description
sourceWrapper = null
}
this.options = $.extend({}, BaseWrapper.DEFAULTS, typeof options == 'object' && options)
this.switched = false
Base.call(this)
this.init()
}
BaseWrapper.prototype = Object.create(BaseProto)
BaseWrapper.prototype.constructor = Base
BaseWrapper.prototype.dispose = function() {
if (!this.switched) {
this.$element.removeClass('inspector-open')
this.setInspectorVisibleFlag(false)
}
this.surface = null
this.$element = null
this.title = null
this.description = null
BaseProto.dispose.call(this)
}
BaseWrapper.prototype.init = function() {
// Wrappers can create a new surface or inject an existing
// surface to the UI they manage.
//
// If there is no surface provided in the wrapper constructor,
// the wrapper first loads the Inspector configuration and values
// and then calls the createSurfaceAndUi() method with all information
// required for creating a new Inspector surface and UI.
if (!this.surface) {
this.loadConfiguration()
}
else {
this.adoptSurface()
}
this.$element.addClass('inspector-open')
}
//
// Helper methods
//
BaseWrapper.prototype.getElementValuesInput = function() {
return this.$element.find('input[data-inspector-values]')
}
BaseWrapper.prototype.normalizePropertyCode = function(code, configuration) {
var lowerCaseCode = code.toLowerCase()
for (var index in configuration) {
var propertyInfo = configuration[index]
if (propertyInfo.property.toLowerCase() == lowerCaseCode) {
return propertyInfo.property
}
}
return code
}
BaseWrapper.prototype.isExternalParametersEditorEnabled = function() {
return this.$element.closest('[data-inspector-external-parameters]').length > 0
}
BaseWrapper.prototype.initSurface = function(containerElement, properties, values) {
var options = {
enableExternalParameterEditor: this.isExternalParametersEditorEnabled()
}
this.surface = new $.oc.inspector.surface(
containerElement,
properties,
values,
$.oc.inspector.helpers.generateElementUniqueId(this.$element.get(0)),
options)
}
//
// Wrapper API
//
BaseWrapper.prototype.createSurfaceAndUi = function(properties, values) {
}
BaseWrapper.prototype.setInspectorVisibleFlag = function(value) {
this.$element.data('oc.inspectorVisible', value)
}
BaseWrapper.prototype.adoptSurface = function() {
}
BaseWrapper.prototype.cleanupAfterSwitch = function() {
this.switched = true
this.dispose()
}
//
// Values
//
BaseWrapper.prototype.loadValues = function(configuration) {
var $valuesField = this.getElementValuesInput()
if ($valuesField.length > 0) {
var valuesStr = $.trim($valuesField.val())
try {
return valuesStr.length === 0 ? {} : $.parseJSON(valuesStr)
} catch (err) {
throw new Error('Error parsing Inspector field values. ' + err)
}
}
var values = {},
attributes = this.$element.get(0).attributes
for (var i=0, len = attributes.length; i < len; i++) {
var attribute = attributes[i],
matches = []
if (matches = attribute.name.match(/^data-property-(.*)$/)) {
// Important - values contained in data-property-xxx attributes are
// considered strings and never parsed with JSON. The use of the
// data-property-xxx attributes is very limited - they're only
// used in Pages for creating snippets from partials, where properties
// are created with a table UI widget, which doesn't allow creating
// properties of any complex types.
//
// There is no a technically reliable way to determine when a string
// is a JSON data or a regular string. Users can enter a value
// like [10], which is a proper JSON value, but meant to be a string.
//
// One possible way to resolve it, if to check the property type loaded
// from the configuration and see if the corresponding editor expects
// complex data.
var normalizedPropertyName = this.normalizePropertyCode(matches[1], configuration)
values[normalizedPropertyName] = attribute.value
}
}
return values
}
BaseWrapper.prototype.applyValues = function() {
var $valuesField = this.getElementValuesInput(),
values = this.surface.getValues()
if ($valuesField.length > 0) {
$valuesField.val(JSON.stringify(values))
}
else {
for (var property in values) {
var value = values[property]
if ($.isArray(value) ||$.isPlainObject(value)) {
throw new Error('Inspector data-property-xxx attributes do not support complex values. Property: ' + property)
}
this.$element.attr('data-property-' + property, value)
}
}
}
//
// Configuration
//
BaseWrapper.prototype.loadConfiguration = function() {
var $configurationField = this.$element.find('input[data-inspector-config]'),
result = {
properties: {},
title: null,
description: null
}
result.title = this.$element.data('inspector-title')
result.description = this.$element.data('inspector-description')
if ($configurationField.length > 0) {
result.properties = this.parseConfiguration($configurationField.val())
this.configurationLoaded(result)
return
}
var $form = this.$element.closest('form'),
data = this.$element.data(),
self = this
$.oc.stripeLoadIndicator.show()
var request = $form.request('onGetInspectorConfiguration', {
data: data
}).done(function inspectorConfigurationRequestDoneClosure(data) {
self.onConfigurartionRequestDone(data, result)
}).always(function() {
$.oc.stripeLoadIndicator.hide()
})
}
BaseWrapper.prototype.parseConfiguration = function(configuration) {
if (!$.isArray(configuration) && !$.isPlainObject(configuration)) {
if ($.trim(configuration) === 0) {
return {}
}
try {
return $.parseJSON(configuration)
} catch(err) {
throw new Error('Error parsing Inspector configuration. ' + err)
}
} else {
return configuration
}
}
BaseWrapper.prototype.configurationLoaded = function(configuration) {
var values = this.loadValues(configuration.properties)
this.title = configuration.title
this.description = configuration.description
this.createSurfaceAndUi(configuration.properties, values)
}
BaseWrapper.prototype.onConfigurartionRequestDone = function(data, result) {
result.properties = this.parseConfiguration(data.configuration.properties)
if (data.configuration.title !== undefined) {
result.title = data.configuration.title
}
if (data.configuration.description !== undefined) {
result.description = data.configuration.description
}
this.configurationLoaded(result)
}
BaseWrapper.DEFAULTS = {
containerSupported: false
}
$.oc.inspector.wrappers.base = BaseWrapper
}(window.jQuery);

View File

@ -0,0 +1,215 @@
/*
* Inspector container wrapper.
*/
+function ($) { "use strict";
// CLASS DEFINITION
// ============================
var Base = $.oc.inspector.wrappers.base,
BaseProto = Base.prototype
var InspectorContainer = function($element, surface, options) {
if (!options.container) {
throw new Error('Cannot create Inspector container wrapper without a container element.')
}
this.surfaceContainer = null
Base.call(this, $element, surface, options)
}
InspectorContainer.prototype = Object.create(BaseProto)
InspectorContainer.prototype.constructor = Base
InspectorContainer.prototype.init = function() {
this.registerHandlers()
BaseProto.init.call(this)
}
InspectorContainer.prototype.dispose = function() {
this.unregisterHandlers()
this.removeControls()
this.surfaceContainer = null
BaseProto.dispose.call(this)
}
InspectorContainer.prototype.createSurfaceAndUi = function(properties, values) {
this.buildUi()
this.initSurface(this.surfaceContainer, properties, values)
}
InspectorContainer.prototype.adoptSurface = function() {
this.buildUi()
this.surface.moveToContainer(this.surfaceContainer)
}
InspectorContainer.prototype.buildUi = function() {
var scrollable = this.isScrollable(),
head = this.buildHead(),
layoutElements = this.buildLayout()
layoutElements.headContainer.appendChild(head)
if (scrollable) {
var scrollpad = this.buildScrollpad()
this.surfaceContainer = scrollpad.container
layoutElements.bodyContainer.appendChild(scrollpad.scrollpad)
$(scrollpad.scrollpad).scrollpad()
}
else {
this.surfaceContainer = layoutElements.bodyContainer
}
this.setInspectorVisibleFlag(true)
}
InspectorContainer.prototype.buildHead = function() {
var container = document.createElement('div'),
header = document.createElement('h3'),
paragraph = document.createElement('p'),
detachButton = document.createElement('span'),
closeButton = document.createElement('span')
container.setAttribute('class', 'inspector-header')
detachButton.setAttribute('class', 'oc-icon-external-link-square detach')
closeButton.setAttribute('class', 'close')
header.textContent = this.title
paragraph.textContent = this.description
closeButton.innerHTML = '&times;';
container.appendChild(header)
container.appendChild(paragraph)
container.appendChild(detachButton)
container.appendChild(closeButton)
return container
}
InspectorContainer.prototype.buildScrollpad = function() {
var scrollpad = document.createElement('div'),
scrollWrapper = document.createElement('div'),
scrollableContainer = document.createElement('div')
scrollpad.setAttribute('class', 'control-scrollpad')
scrollpad.setAttribute('data-control', 'scrollpad')
scrollWrapper.setAttribute('class', 'scroll-wrapper inspector-wrapper')
scrollpad.appendChild(scrollWrapper)
scrollWrapper.appendChild(scrollableContainer)
return {
scrollpad: scrollpad,
container: scrollableContainer
}
}
InspectorContainer.prototype.buildLayout = function() {
var layout = document.createElement('div'),
headRow = document.createElement('div'),
bodyRow = document.createElement('div'),
bodyCell = document.createElement('div'),
layoutRelative = document.createElement('div')
layout.setAttribute('class', 'layout')
headRow.setAttribute('class', 'layout-row min-size')
bodyRow.setAttribute('class', 'layout-row')
bodyCell.setAttribute('class', 'layout-cell')
layoutRelative.setAttribute('class', 'layout-relative')
bodyCell.appendChild(layoutRelative)
bodyRow.appendChild(bodyCell)
layout.appendChild(headRow)
layout.appendChild(bodyRow)
this.options.container.get(0).appendChild(layout)
$.oc.foundation.controlUtils.markDisposable(layout)
this.registerLayoutHandlers(layout)
return {
headContainer: headRow,
bodyContainer: layoutRelative
}
}
InspectorContainer.prototype.validateAndApply = function() {
if (!this.surface.validate()) {
return false
}
this.applyValues()
return true
}
InspectorContainer.prototype.isScrollable = function() {
return this.options.container.data('inspector-scrollable') !== undefined
}
InspectorContainer.prototype.getLayout = function() {
return this.options.container.get(0).querySelector('div.layout')
}
InspectorContainer.prototype.registerLayoutHandlers = function(layout) {
var $layout = $(layout)
$layout.one('dispose-control', this.proxy(this.dispose))
$layout.on('click', 'span.close', this.proxy(this.onClose))
$layout.on('click', 'span.detach', this.proxy(this.onDetach))
}
InspectorContainer.prototype.registerHandlers = function() {
this.options.container.on('apply.oc.inspector', this.proxy(this.onApplyValues))
}
InspectorContainer.prototype.unregisterHandlers = function() {
var $layout = $(this.getLayout())
this.options.container.off('apply.oc.inspector', this.proxy(this.onApplyValues))
$layout.off('dispose-control', this.proxy(this.dispose))
$layout.off('click', 'span.close', this.proxy(this.onClose))
$layout.off('click', 'span.detach', this.proxy(this.onDetach))
}
InspectorContainer.prototype.removeControls = function() {
if (this.isScrollable()) {
this.options.container.find('.control-scrollpad').scrollpad('dispose')
}
var layout = this.getLayout()
layout.parentNode.removeChild(layout)
}
InspectorContainer.prototype.onApplyValues = function(ev) {
if (!this.validateAndApply()) {
ev.preventDefault()
return false
}
}
InspectorContainer.prototype.onClose = function(ev) {
if (!this.validateAndApply()) {
ev.preventDefault()
return false
}
this.surface.dispose()
this.dispose()
}
InspectorContainer.prototype.onDetach = function() {
$.oc.inspector.manager.switchToPopup(this)
}
$.oc.inspector.wrappers.container = InspectorContainer
}(window.jQuery);

View File

@ -0,0 +1,203 @@
/*
* Inspector popup wrapper.
*/
+function ($) { "use strict";
// CLASS DEFINITION
// ============================
var Base = $.oc.inspector.wrappers.base,
BaseProto = Base.prototype
var InspectorPopup = function($element, surface, options) {
this.$popoverContainer = null
this.popoverObj = null
this.cleaningUp = false
Base.call(this, $element, surface, options)
}
InspectorPopup.prototype = Object.create(BaseProto)
InspectorPopup.prototype.constructor = Base
InspectorPopup.prototype.dispose = function() {
this.unregisterHandlers()
this.$popoverContainer = null
this.popoverObj = null
BaseProto.dispose.call(this)
}
InspectorPopup.prototype.createSurfaceAndUi = function(properties, values, title, description) {
this.showPopover()
this.initSurface(this.$popoverContainer.find('[data-surface-container]').get(0), properties, values)
this.registerPopupHandlers()
}
InspectorPopup.prototype.adoptSurface = function() {
this.showPopover()
this.surface.moveToContainer(this.$popoverContainer.find('[data-surface-container]').get(0))
this.registerPopupHandlers()
}
InspectorPopup.prototype.cleanupAfterSwitch = function() {
this.cleaningUp = true
this.switched = true
this.forceClose()
// The parent cleanupAfterSwitch() is not called because
// disposing happens in onHide() triggered by forceClose()
}
InspectorPopup.prototype.getPopoverContents = function() {
return '<div class="popover-head"> \
<h3 data-inspector-title></h3> \
<p data-inspector-description></p> \
<button type="button" class="close" \
data-dismiss="popover" \
aria-hidden="true">&times;</button> \
</div> \
<form autocomplete="off" onsubmit="return false"> \
<div data-surface-container></div> \
<form>'
}
InspectorPopup.prototype.showPopover = function() {
var offset = this.$element.data('inspector-offset'),
offsetX = this.$element.data('inspector-offset-x'),
offsetY = this.$element.data('inspector-offset-y'),
placement = this.$element.data('inspector-placement'),
fallbackPlacement = this.$element.data('inspector-fallback-placement')
if (offset === undefined) {
offset = 15
}
if (placement === undefined) {
placement = 'bottom'
}
if (fallbackPlacement === undefined) {
fallbackPlacement = 'bottom'
}
this.$element.ocPopover({
content: this.getPopoverContents(),
highlightModalTarget: true,
modal: true,
placement: placement,
fallbackPlacement: fallbackPlacement,
containerClass: 'control-inspector',
container: this.$element.data('inspector-container'),
offset: offset,
offsetX: offsetX,
offsetY: offsetY,
width: 400
})
this.setInspectorVisibleFlag(true)
this.popoverObj = this.$element.data('oc.popover')
this.$popoverContainer = this.popoverObj.$container
if (this.options.containerSupported) {
var moveToContainerButton = $('<span class="inspector-move-to-container oc-icon-download">')
this.$popoverContainer.find('.popover-head').append(moveToContainerButton)
}
this.$popoverContainer.find('[data-inspector-title]').text(this.title)
this.$popoverContainer.find('[data-inspector-description]').text(this.description)
}
InspectorPopup.prototype.forceClose = function() {
this.$popoverContainer.trigger('close.oc.popover')
}
InspectorPopup.prototype.registerPopupHandlers = function() {
this.surface.options.onPopupDisplayed = this.proxy(this.onPopupEditorDisplayed)
this.surface.options.onPopupHidden = this.proxy(this.onPopupEditorHidden)
this.popoverObj.options.onCheckDocumentClickTarget = this.proxy(this.onCheckDocumentClickTarget)
this.$element.on('hiding.oc.popover', this.proxy(this.onBeforeHide))
this.$element.on('hide.oc.popover', this.proxy(this.onHide))
this.$popoverContainer.on('keydown', this.proxy(this.onPopoverKeyDown))
if (this.options.containerSupported) {
this.$popoverContainer.on('click', 'span.inspector-move-to-container', this.proxy(this.onMoveToContainer))
}
}
InspectorPopup.prototype.unregisterHandlers = function() {
this.popoverObj.options.onCheckDocumentClickTarget = null
this.$element.off('hiding.oc.popover', this.proxy(this.onBeforeHide))
this.$element.off('hide.oc.popover', this.proxy(this.onHide))
this.$popoverContainer.off('keydown', this.proxy(this.onPopoverKeyDown))
if (this.options.containerSupported) {
this.$popoverContainer.off('click', 'span.inspector-move-to-container', this.proxy(this.onMoveToContainer))
}
this.surface.options.onPopupDisplayed = null
this.surface.options.onPopupHidden = null
}
InspectorPopup.prototype.onBeforeHide = function(ev) {
if (this.cleaningUp) {
return
}
if (!this.surface.validate()) {
ev.preventDefault()
return false
}
var hidingEvent = $.Event('hiding.oc.inspector'),
values = this.surface.getValues()
this.$element.trigger(hidingEvent, [{values: values}])
if (hidingEvent.isDefaultPrevented()) {
ev.preventDefault()
return false
}
this.applyValues()
}
InspectorPopup.prototype.onHide = function(ev) {
this.dispose()
}
InspectorPopup.prototype.onPopoverKeyDown = function(ev) {
if(ev.keyCode == 13) {
$(ev.currentTarget).trigger('close.oc.popover')
}
}
InspectorPopup.prototype.onPopupEditorDisplayed = function() {
this.popoverObj.options.closeOnPageClick = false
this.popoverObj.options.closeOnEsc = false
}
InspectorPopup.prototype.onPopupEditorHidden = function() {
this.popoverObj.options.closeOnPageClick = true
this.popoverObj.options.closeOnEsc = true
}
InspectorPopup.prototype.onCheckDocumentClickTarget = function(element) {
if ($.contains(this.$element, element) || this.$element.get(0) === element) {
return true
}
}
InspectorPopup.prototype.onMoveToContainer = function() {
$.oc.inspector.manager.switchToContainer(this)
}
$.oc.inspector.wrappers.popup = InspectorPopup
}(window.jQuery);

View File

@ -97,6 +97,7 @@
$(document).off('.oc.popover')
this.docClickHandler = null
this.options.onCheckDocumentClickTarget = null
}
Popover.prototype.show = function(options) {
@ -185,6 +186,10 @@
if ($(e.target).hasClass('select2-offscreen'))
return false
if (!self.options.closeOnEsc) { // The value of the option could be changed after the popover is displayed
return false
}
if (e.keyCode == 27) {
self.hide()
return false
@ -320,6 +325,10 @@
if (!this.options.closeOnPageClick)
return
if (this.options.onCheckDocumentClickTarget && this.options.onCheckDocumentClickTarget(e.target)) {
return
}
if ($.contains(this.$container.get(0), e.target))
return
@ -338,7 +347,8 @@
container: false,
containerClass: null,
offset: 15,
useAnimation: false
useAnimation: false,
onCheckDocumentClickTarget: null
}
// POPOVER PLUGIN DEFINITION

View File

@ -59,10 +59,10 @@
@zindex-navbar: 1000;
@zindex-dropdown: 1000;
@zindex-popover: 1010;
@zindex-tooltip: 1030;
@zindex-navbar-fixed: 1030;
@zindex-modal-background: 1040;
@zindex-modal: 1050;
@zindex-tooltip: 1060; // Tooltips should always be on the top
//
// Typography

View File

@ -153,6 +153,10 @@ div.control-popover {
font-size: 13px;
font-weight: 100;
margin: 10px 0 0 0;
&:empty {
display: none;
}
}
.close {
@ -162,6 +166,7 @@ div.control-popover {
top: 12px;
color: @color-popover-head-text;
outline: none;
.opacity(0.4);
&:hover {
.opacity(1);

File diff suppressed because it is too large Load Diff

View File

@ -516,7 +516,7 @@ input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"
.btn-text{font-size:13px;padding:9px 0;vertical-align:middle;display:inline-block}
.btn-text a{color:#666666}
.btn-text a:hover{color:#0181b9;text-decoration:none}
.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}
.tooltip{position:absolute;z-index:1060;display:block;visibility:visible;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}
.tooltip.in{opacity:0.9;filter:alpha(opacity=90)}
.tooltip.top{margin-top:-3px;padding:5px 0}
.tooltip.right{margin-left:3px;padding:0 5px}
@ -1202,7 +1202,8 @@ div.control-popover .popover-head{background:#d35400;padding:14px 16px;position:
div.control-popover .popover-head:before{z-index:602;position:absolute}
div.control-popover .popover-head h3{font-size:14px;font-weight:600;margin-top:0;margin-bottom:0;padding-right:15px;line-height:130%}
div.control-popover .popover-head p{font-size:13px;font-weight:100;margin:10px 0 0 0}
div.control-popover .popover-head .close{float:none;position:absolute;right:11px;top:12px;color:#ffffff;outline:none}
div.control-popover .popover-head p:empty{display:none}
div.control-popover .popover-head .close{float:none;position:absolute;right:11px;top:12px;color:#ffffff;outline:none;opacity:0.4;filter:alpha(opacity=40)}
div.control-popover .popover-head .close:hover{opacity:1;filter:alpha(opacity=100)}
div.control-popover.placement-bottom .popover-head:before{content:'';display:block;width:0;height:0;border-left:7.5px solid transparent;border-right:7.5px solid transparent;border-bottom:8px solid #d35400;left:15px;top:-8px}
div.control-popover.placement-left .popover-head:before{content:'';display:block;width:0;height:0;border-top:7.5px solid transparent;border-bottom:7.5px solid transparent;border-left:8px solid #d35400;right:-8px;top:7px}

View File

@ -6,7 +6,6 @@
=require js/foundation.js
=require js/flashmessage.js
=require js/inspector.js
=require js/checkbox.js
=require js/dropdown.js
=require js/callout.js