333 lines
9.8 KiB
JavaScript
333 lines
9.8 KiB
JavaScript
/*
|
|
* Inspector Surface class.
|
|
*
|
|
* The class creates Inspector user interface and all the editors
|
|
* corresponding to the passed configuration in a specified container
|
|
* element.
|
|
*
|
|
*/
|
|
+function ($) { "use strict";
|
|
|
|
// NAMESPACES
|
|
// ============================
|
|
|
|
if ($.oc === undefined)
|
|
$.oc = {}
|
|
|
|
if ($.oc.inspector === undefined)
|
|
$.oc.inspector = {}
|
|
|
|
// CLASS DEFINITION
|
|
// ============================
|
|
|
|
var Base = $.oc.foundation.base,
|
|
BaseProto = Base.prototype
|
|
|
|
/**
|
|
* Creates the Inspector surface in a container.
|
|
* - containerElement container DOM element
|
|
* - properties array (array of objects)
|
|
* - values - property values, an object
|
|
* - inspectorUniqueId - a string containing the unique inspector identifier.
|
|
* The identifier should be a constant for an inspectable element. Use
|
|
* $.oc.inspector.helpers.generateElementUniqueId(element) to generate a persistent ID
|
|
* for an element. Use $.oc.inspector.helpers.generateUniqueId() to generate an ID
|
|
* not associated with an element. Inspector uses the ID for storing configuration
|
|
* related to an element in the document DOM.
|
|
*/
|
|
var Surface = function(containerElement, properties, values, inspectorUniqueId, options) {
|
|
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.editors = []
|
|
this.tableContainer = null
|
|
|
|
Base.call(this)
|
|
|
|
this.init()
|
|
}
|
|
|
|
Surface.prototype = Object.create(BaseProto)
|
|
Surface.prototype.constructor = Surface
|
|
|
|
Surface.prototype.dispose = function() {
|
|
this.removeElements()
|
|
this.disposeEditors()
|
|
|
|
this.container = null
|
|
this.tableContainer = null
|
|
this.rawProperties = null
|
|
this.parsedProperties = null
|
|
this.editors = null
|
|
this.values = null
|
|
|
|
BaseProto.dispose.call(this)
|
|
}
|
|
|
|
// INTERNAL METHODS
|
|
// ============================
|
|
|
|
Surface.prototype.init = function() {
|
|
this.build()
|
|
}
|
|
|
|
//
|
|
// Building
|
|
//
|
|
|
|
/**
|
|
* Builds the Inspector table. The markup generated by this method looks
|
|
* like this:
|
|
*
|
|
* <div>
|
|
* <table>
|
|
* <tbody>
|
|
* <tr>
|
|
* <th data-property="label">
|
|
* <div>
|
|
* <div>
|
|
* <span class="title-element" title="Label">
|
|
* <a href="javascript:;" class="expandControl expanded" data-group-index="1">Expand/Collapse</a>
|
|
* Label
|
|
* </span>
|
|
* </div>
|
|
* </div>
|
|
* </th>
|
|
* <td>
|
|
* Editor markup
|
|
* </td>
|
|
* </tr>
|
|
* </tbody>
|
|
* </table>
|
|
* </div>
|
|
*/
|
|
Surface.prototype.build = function() {
|
|
this.tableContainer = document.createElement('div')
|
|
|
|
var dataTable = document.createElement('table'),
|
|
tbody = document.createElement('tbody')
|
|
|
|
$.oc.foundation.element.addClass(dataTable, 'inspector-fields')
|
|
if (this.parsedProperties.hasGroups) {
|
|
$.oc.foundation.element.addClass(dataTable, 'has-groups')
|
|
}
|
|
|
|
for (var i=0, len = this.parsedProperties.properties.length; i < len; i++) {
|
|
var property = this.parsedProperties.properties[i],
|
|
row = document.createElement('tr'),
|
|
th = document.createElement('th'),
|
|
titleSpan = document.createElement('span'),
|
|
description = this.buildPropertyDescription(property)
|
|
|
|
// Table row
|
|
//
|
|
row.setAttribute('data-property', property.property)
|
|
this.applyGroupIndexAttribute(property, row)
|
|
$.oc.foundation.element.addClass(row, this.getRowCssClass(property))
|
|
|
|
// Property head
|
|
//
|
|
this.applyHeadColspan(th, property)
|
|
|
|
titleSpan.setAttribute('class', 'title-element')
|
|
titleSpan.setAttribute('title', this.escapeJavascriptString(property.title))
|
|
this.buildGroupExpandControl(titleSpan, property)
|
|
titleSpan.innerHTML += this.escapeJavascriptString(property.title)
|
|
|
|
var outerDiv = document.createElement('div'),
|
|
innerDiv = document.createElement('div')
|
|
|
|
innerDiv.appendChild(titleSpan)
|
|
|
|
if (description) {
|
|
innerDiv.appendChild(description)
|
|
}
|
|
|
|
outerDiv.appendChild(innerDiv)
|
|
th.appendChild(outerDiv)
|
|
row.appendChild(th)
|
|
|
|
// Editor
|
|
//
|
|
this.buildEditor(row, property)
|
|
|
|
tbody.appendChild(row)
|
|
}
|
|
|
|
dataTable.appendChild(tbody)
|
|
this.tableContainer.appendChild(dataTable)
|
|
|
|
this.container.appendChild(this.tableContainer)
|
|
}
|
|
|
|
Surface.prototype.applyGroupIndexAttribute = function(property, row) {
|
|
if (property.groupIndex !== undefined && property.itemType == 'property') {
|
|
row.setAttribute('data-group-index', property.groupIndex)
|
|
}
|
|
}
|
|
|
|
Surface.prototype.getRowCssClass = function(property) {
|
|
var result = property.itemType
|
|
|
|
if (property.itemType == 'property' && property.groupIndex !== undefined) {
|
|
result += ' grouped'
|
|
result += this.isGroupExpanded(property.group) ? ' expanded' : ' collapsed'
|
|
}
|
|
|
|
if (property.itemType == 'property' && !property.showExternalParam) {
|
|
result += ' no-external-parameter'
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
Surface.prototype.applyHeadColspan = function(th, property) {
|
|
if (property.itemType == 'group') {
|
|
th.setAttribute('colspan', 2)
|
|
}
|
|
}
|
|
|
|
Surface.prototype.buildGroupExpandControl = function(titleSpan, property) {
|
|
if (property.itemType !== 'group') {
|
|
return
|
|
}
|
|
|
|
var statusClass = this.isGroupExpanded(property.title) ? 'expanded' : '',
|
|
anchor = document.createElement('a')
|
|
|
|
anchor.setAttribute('class', 'expandControl ' + statusClass)
|
|
anchor.setAttribute('href', 'javascript:;')
|
|
anchor.setAttribute('data-group-index', property.groupIndex)
|
|
anchor.innerHTML = '<span>Expand/collapse</span>'
|
|
|
|
titleSpan.appendChild(anchor)
|
|
}
|
|
|
|
Surface.prototype.buildPropertyDescription = function(property) {
|
|
if (property.description === undefined || property.description === null) {
|
|
return null
|
|
}
|
|
|
|
var span = document.createElement('span')
|
|
span.setAttribute('title', this.escapeJavascriptString(property.description))
|
|
span.setAttribute('class', 'info oc-icon-info with-tooltip')
|
|
|
|
return span
|
|
}
|
|
|
|
//
|
|
// Field grouping
|
|
//
|
|
|
|
Surface.prototype.isGroupExpanded = function(group) {
|
|
var statuses = this.loadGroupStatuses()
|
|
|
|
if (statuses[group] !== undefined)
|
|
return statuses[group]
|
|
|
|
return false
|
|
}
|
|
|
|
Surface.prototype.loadGroupStatuses = function() {
|
|
var statuses = this.getInspectorGroupStatuses()
|
|
|
|
if (statuses[this.inspectorUniqueId] !== undefined) {
|
|
return statuses[this.inspectorUniqueId]
|
|
}
|
|
|
|
return {}
|
|
}
|
|
|
|
Surface.prototype.getInspectorGroupStatuses = function() {
|
|
var statuses = document.body.getAttribute('data-inspector-group-statuses')
|
|
|
|
if (statuses !== null) {
|
|
return JSON.parse(statuses)
|
|
}
|
|
|
|
return {}
|
|
}
|
|
|
|
//
|
|
// Editors
|
|
//
|
|
|
|
Surface.prototype.buildEditor = function(row, property) {
|
|
if (property.itemType !== 'property') {
|
|
return
|
|
}
|
|
|
|
if ($.oc.inspector.propertyEditors[property.type] === undefined)
|
|
throw new Error('The Inspector editor class "' + property.type +
|
|
'" is not defined in the $.oc.inspector.propertyEditors namespace.')
|
|
|
|
var cell = document.createElement('td'),
|
|
editor = new $.oc.inspector.propertyEditors[property.type](this, property, cell)
|
|
|
|
this.editors.push(editor)
|
|
|
|
row.appendChild(cell)
|
|
}
|
|
|
|
//
|
|
// Internal API for the editors
|
|
//
|
|
|
|
Surface.prototype.getPropertyValue = function(property) {
|
|
return this.values[property]
|
|
}
|
|
|
|
Surface.prototype.makeCellActive = function(cell) {
|
|
var tbody = cell.parentNode.parentNode.parentNode, // cell / row / tbody
|
|
cells = tbody.querySelectorAll('tr td')
|
|
|
|
for (var i = 0, len = cells.length; i < len; i++) {
|
|
$.oc.foundation.element.removeClass(cells[i], 'active')
|
|
}
|
|
|
|
$.oc.foundation.element.addClass(cell, 'active')
|
|
}
|
|
|
|
//
|
|
// Disposing
|
|
//
|
|
|
|
Surface.prototype.removeElements = function() {
|
|
this.tableContainer.parentNode.removeChild(this.tableContainer);
|
|
}
|
|
|
|
Surface.prototype.disposeEditors = function() {
|
|
for (var i = 0, len = this.editors.length; i < len; i++) {
|
|
var editor = this.editors[i]
|
|
|
|
editor.dispose()
|
|
}
|
|
}
|
|
|
|
//
|
|
// Helpers
|
|
//
|
|
|
|
Surface.prototype.escapeJavascriptString = function(str) {
|
|
var div = document.createElement('div')
|
|
div.appendChild(document.createTextNode(str))
|
|
return div.innerHTML
|
|
}
|
|
|
|
// DEFAULT OPTIONS
|
|
// ============================
|
|
|
|
Surface.DEFAULTS = {
|
|
showExternalParam: false
|
|
}
|
|
|
|
// REGISTRATION
|
|
// ============================
|
|
|
|
$.oc.inspector.surface = Surface
|
|
|
|
}(window.jQuery); |