// Create.js - On-site web editing interface // (c) 2011-2012 Henri Bergius, IKS Consortium // Create may be freely distributed under the MIT license. // For all details and documentation: // http://createjs.org/ (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false window:false console:false */ 'use strict'; // # Widget for adding items to a collection jQuery.widget('Midgard.midgardCollectionAdd', { options: { editingWidgets: null, collection: null, model: null, definition: null, view: null, disabled: false, vie: null, editableOptions: null, templates: { button: '' } }, _create: function () { this.addButtons = []; var widget = this; if (!widget.options.collection.localStorage) { try { widget.options.collection.url = widget.options.model.url(); } catch (e) { if (window.console) { console.log(e); } } } widget.options.collection.on('add', function (model) { model.primaryCollection = widget.options.collection; widget.options.vie.entities.add(model); model.collection = widget.options.collection; }); // Re-check collection constraints widget.options.collection.on('add remove reset', widget.checkCollectionConstraints, widget); widget._bindCollectionView(widget.options.view); }, _bindCollectionView: function (view) { var widget = this; view.on('add', function (itemView) { itemView.$el.effect('slide', function () { widget._makeEditable(itemView); }); }); }, _makeEditable: function (itemView) { this.options.editableOptions.disabled = this.options.disabled; this.options.editableOptions.model = itemView.model; itemView.$el.midgardEditable(this.options.editableOptions); }, _init: function () { if (this.options.disabled) { this.disable(); return; } this.enable(); }, hideButtons: function () { _.each(this.addButtons, function (button) { button.hide(); }); }, showButtons: function () { _.each(this.addButtons, function (button) { button.show(); }); }, checkCollectionConstraints: function () { if (this.options.disabled) { return; } if (!this.options.view.canAdd()) { this.hideButtons(); return; } if (!this.options.definition) { // We have now information on the constraints applying to this collection this.showButtons(); return; } if (!this.options.definition.max || this.options.definition.max === -1) { // No maximum constraint this.showButtons(); return; } if (this.options.collection.length < this.options.definition.max) { this.showButtons(); return; } // Collection is already full by its definition this.hideButtons(); }, enable: function () { var widget = this; var addButton = jQuery(_.template(this.options.templates.button, { icon: 'plus', label: this.options.editableOptions.localize('Add', this.options.editableOptions.language) })).button(); addButton.addClass('midgard-create-add'); addButton.click(function () { widget.addItem(addButton); }); jQuery(widget.options.view.el).after(addButton); widget.addButtons.push(addButton); widget.checkCollectionConstraints(); }, disable: function () { _.each(this.addButtons, function (button) { button.remove(); }); this.addButtons = []; }, _getTypeActions: function (options) { var widget = this; var actions = []; _.each(this.options.definition.range, function (type) { var nsType = widget.options.collection.vie.namespaces.uri(type); if (!widget.options.view.canAdd(nsType)) { return; } actions.push({ name: type, label: type, cb: function () { widget.options.collection.add({ '@type': type }, options); }, className: 'create-ui-btn' }); }); return actions; }, addItem: function (button, options) { if (options === undefined) { options = {}; } var addOptions = _.extend({}, options, { validate: false }); var itemData = {}; if (this.options.definition && this.options.definition.range) { if (this.options.definition.range.length === 1) { // Items can be of single type, add that itemData['@type'] = this.options.definition.range[0]; } else { // Ask user which type to add jQuery('body').midgardNotifications('create', { bindTo: button, gravity: 'L', body: this.options.editableOptions.localize('Choose type to add', this.options.editableOptions.language), timeout: 0, actions: this._getTypeActions(addOptions) }); return; } } else { // Check the view templates for possible non-Thing type to use var keys = _.keys(this.options.view.templates); if (keys.length == 2) { itemData['@type'] = keys[0]; } } this.options.collection.add(itemData, addOptions); } }); })(jQuery); // Create.js - On-site web editing interface // (c) 2011-2012 Henri Bergius, IKS Consortium // Create may be freely distributed under the MIT license. // For all details and documentation: // http://createjs.org/ (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false window:false console:false */ 'use strict'; // # Widget for adding items anywhere inside a collection jQuery.widget('Midgard.midgardCollectionAddBetween', jQuery.Midgard.midgardCollectionAdd, { _bindCollectionView: function (view) { var widget = this; view.on('add', function (itemView) { //itemView.el.effect('slide'); widget._makeEditable(itemView); widget._refreshButtons(); }); view.on('remove', function () { widget._refreshButtons(); }); }, _refreshButtons: function () { var widget = this; window.setTimeout(function () { widget.disable(); widget.enable(); }, 1); }, prepareButton: function (index) { var widget = this; var addButton = jQuery(_.template(this.options.templates.button, { icon: 'plus', label: '' })).button(); addButton.addClass('midgard-create-add'); addButton.click(function () { widget.addItem(addButton, { at: index }); }); return addButton; }, enable: function () { var widget = this; var firstAddButton = widget.prepareButton(0); jQuery(widget.options.view.el).prepend(firstAddButton); widget.addButtons.push(firstAddButton); jQuery.each(widget.options.view.entityViews, function (cid, view) { var index = widget.options.collection.indexOf(view.model); var addButton = widget.prepareButton(index + 1); jQuery(view.el).append(addButton); widget.addButtons.push(addButton); }); this.checkCollectionConstraints(); }, disable: function () { var widget = this; jQuery.each(widget.addButtons, function (idx, button) { button.remove(); }); widget.addButtons = []; } }); })(jQuery); // Create.js - On-site web editing interface // (c) 2011-2012 Henri Bergius, IKS Consortium // Create may be freely distributed under the MIT license. // For all details and documentation: // http://createjs.org/ (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false window:false VIE:false */ 'use strict'; // Define Create's EditableEntity widget. jQuery.widget('Midgard.midgardEditable', { options: { propertyEditors: {}, collections: [], model: null, // the configuration (mapping and options) of property editor widgets propertyEditorWidgetsConfiguration: { hallo: { widget: 'halloWidget', options: {} } }, // the available property editor widgets by data type propertyEditorWidgets: { 'default': 'hallo' }, collectionWidgets: { 'default': 'midgardCollectionAdd' }, toolbarState: 'full', vie: null, domService: 'rdfa', predicateSelector: '[property]', disabled: false, localize: function (id, language) { return window.midgardCreate.localize(id, language); }, language: null, // Current state of the Editable state: null, // Callback function for validating changes between states. Receives the previous state, new state, possibly property, and a callback acceptStateChange: true, // Callback function for listening (and reacting) to state changes. stateChange: null, // Callback function for decorating the full editable. Will be called on instantiation decorateEditableEntity: null, // Callback function for decorating a single property editor widget. Will // be called on editing widget instantiation. decoratePropertyEditor: null, // Deprecated. editables: [], // Now `propertyEditors`. editors: {}, // Now `propertyEditorWidgetsConfiguration`. widgets: {} // Now `propertyEditorW }, // Aids in consistently passing parameters to events and callbacks. _params: function(predicate, extended) { var entityParams = { entity: this.options.model, editableEntity: this, entityElement: this.element, // Deprecated. editable: this, element: this.element, instance: this.options.model }; var propertyParams = (predicate) ? { predicate: predicate, propertyEditor: this.options.propertyEditors[predicate], propertyElement: this.options.propertyEditors[predicate].element, // Deprecated. property: predicate, element: this.options.propertyEditors[predicate].element } : {}; return _.extend(entityParams, propertyParams, extended); }, _create: function () { // Backwards compatibility: // - this.options.propertyEditorWidgets used to be this.options.widgets // - this.options.propertyEditorWidgetsConfiguration used to be // this.options.editors if (this.options.widgets) { this.options.propertyEditorWidgets = _.extend(this.options.propertyEditorWidgets, this.options.widgets); } if (this.options.editors) { this.options.propertyEditorWidgetsConfiguration = _.extend(this.options.propertyEditorWidgetsConfiguration, this.options.editors); } this.vie = this.options.vie; this.domService = this.vie.service(this.options.domService); if (!this.options.model) { var widget = this; this.vie.load({ element: this.element }).from(this.options.domService).execute().done(function (entities) { widget.options.model = entities[0]; }); } if (_.isFunction(this.options.decorateEditableEntity)) { this.options.decorateEditableEntity(this._params()); } }, _init: function () { // Backwards compatibility: // - this.options.propertyEditorWidgets used to be this.options.widgets // - this.options.propertyEditorWidgetsConfiguration used to be // this.options.editors if (this.options.widgets) { this.options.propertyEditorWidgets = _.extend(this.options.propertyEditorWidgets, this.options.widgets); } if (this.options.editors) { this.options.propertyEditorWidgetsConfiguration = _.extend(this.options.propertyEditorWidgetsConfiguration, this.options.editors); } // Old way of setting the widget inactive if (this.options.disabled === true) { this.setState('inactive'); return; } if (this.options.disabled === false && this.options.state === 'inactive') { this.setState('candidate'); return; } this.options.disabled = false; if (this.options.state) { this.setState(this.options.state); return; } this.setState('candidate'); }, // Method used for cycling between the different states of the Editable widget: // // * Inactive: editable is loaded but disabled // * Candidate: editable is enabled but not activated // * Highlight: user is hovering over the editable (not set by Editable widget directly) // * Activating: an editor widget is being activated for user to edit with it (skipped for editors that activate instantly) // * Active: user is actually editing something inside the editable // * Changed: user has made changes to the editable // * Invalid: the contents of the editable have validation errors // // In situations where state changes are triggered for a particular property editor, the `predicate` // argument will provide the name of that property. // // State changes may carry optional context information in a JavaScript object. The payload of these context objects is not // standardized, and is meant to be set and used by the application controller // // The callback parameter is optional and will be invoked after a state change has been accepted (after the 'statechange' // event) or rejected. setState: function (state, predicate, context, callback) { var previous = this.options.state; var current = state; if (current === previous) { return; } if (this.options.acceptStateChange === undefined || !_.isFunction(this.options.acceptStateChange)) { // Skip state transition validation this._doSetState(previous, current, predicate, context); if (_.isFunction(callback)) { callback(true); } return; } var widget = this; this.options.acceptStateChange(previous, current, predicate, context, function (accepted) { if (accepted) { widget._doSetState(previous, current, predicate, context); } if (_.isFunction(callback)) { callback(accepted); } return; }); }, getState: function () { return this.options.state; }, _doSetState: function (previous, current, predicate, context) { this.options.state = current; if (current === 'inactive') { this.disable(); } else if ((previous === null || previous === 'inactive') && current !== 'inactive') { this.enable(); } this._trigger('statechange', null, this._params(predicate, { previous: previous, current: current, context: context })); }, findEditablePredicateElements: function (callback) { this.domService.findPredicateElements(this.options.model.id, jQuery(this.options.predicateSelector, this.element), false).each(callback); }, getElementPredicate: function (element) { return this.domService.getElementPredicate(element); }, enable: function () { var editableEntity = this; if (!this.options.model) { return; } this.findEditablePredicateElements(function () { editableEntity._enablePropertyEditor(jQuery(this)); }); this._trigger('enable', null, this._params()); if (!this.vie.view || !this.vie.view.Collection) { return; } _.each(this.domService.views, function (view) { if (view instanceof this.vie.view.Collection && this.options.model === view.owner) { var predicate = view.collection.predicate; var editableOptions = _.clone(this.options); editableOptions.state = null; var collection = this.enableCollection({ model: this.options.model, collection: view.collection, property: predicate, definition: this.getAttributeDefinition(predicate), view: view, element: view.el, vie: editableEntity.vie, editableOptions: editableOptions }); editableEntity.options.collections.push(collection); } }, this); }, disable: function () { _.each(this.options.propertyEditors, function (editable) { this.disableEditor({ widget: this, editable: editable, entity: this.options.model, element: jQuery(editable) }); }, this); this.options.propertyEditors = {}; // Deprecated. this.options.editables = []; _.each(this.options.collections, function (collectionWidget) { var editableOptions = _.clone(this.options); editableOptions.state = 'inactive'; this.disableCollection({ widget: this, model: this.options.model, element: collectionWidget, vie: this.vie, editableOptions: editableOptions }); }, this); this.options.collections = []; this._trigger('disable', null, this._params()); }, _enablePropertyEditor: function (element) { var widget = this; var predicate = this.getElementPredicate(element); if (!predicate) { return true; } if (this.options.model.get(predicate) instanceof Array) { // For now we don't deal with multivalued properties in the editable return true; } var propertyElement = this.enablePropertyEditor({ widget: this, element: element, entity: this.options.model, property: predicate, vie: this.vie, decorate: this.options.decoratePropertyEditor, decorateParams: _.bind(this._params, this), changed: function (content) { widget.setState('changed', predicate); var changedProperties = {}; changedProperties[predicate] = content; widget.options.model.set(changedProperties, { silent: true }); widget._trigger('changed', null, widget._params(predicate)); }, activating: function () { widget.setState('activating', predicate); }, activated: function () { widget.setState('active', predicate); widget._trigger('activated', null, widget._params(predicate)); }, deactivated: function () { widget.setState('candidate', predicate); widget._trigger('deactivated', null, widget._params(predicate)); } }); if (!propertyElement) { return; } var widgetType = propertyElement.data('createWidgetName'); this.options.propertyEditors[predicate] = propertyElement.data(widgetType); // Deprecated. this.options.editables.push(propertyElement); this._trigger('enableproperty', null, this._params(predicate)); }, // returns the name of the property editor widget to use for the given property _propertyEditorName: function (data) { if (this.options.propertyEditorWidgets[data.property] !== undefined) { // Property editor widget configuration set for specific RDF predicate return this.options.propertyEditorWidgets[data.property]; } // Load the property editor widget configuration for the data type var propertyType = 'default'; var attributeDefinition = this.getAttributeDefinition(data.property); if (attributeDefinition) { propertyType = attributeDefinition.range[0]; } if (this.options.propertyEditorWidgets[propertyType] !== undefined) { return this.options.propertyEditorWidgets[propertyType]; } return this.options.propertyEditorWidgets['default']; }, _propertyEditorWidget: function (editor) { return this.options.propertyEditorWidgetsConfiguration[editor].widget; }, _propertyEditorOptions: function (editor) { return this.options.propertyEditorWidgetsConfiguration[editor].options; }, getAttributeDefinition: function (property) { var type = this.options.model.get('@type'); if (!type) { return; } if (!type.attributes) { return; } return type.attributes.get(property); }, // Deprecated. enableEditor: function (data) { return this.enablePropertyEditor(data); }, enablePropertyEditor: function (data) { var editorName = this._propertyEditorName(data); if (editorName === null) { return; } var editorWidget = this._propertyEditorWidget(editorName); data.editorOptions = this._propertyEditorOptions(editorName); data.toolbarState = this.options.toolbarState; data.disabled = false; // Pass metadata that could be useful for some implementations. data.editorName = editorName; data.editorWidget = editorWidget; if (typeof jQuery(data.element)[editorWidget] !== 'function') { throw new Error(editorWidget + ' widget is not available'); } jQuery(data.element)[editorWidget](data); jQuery(data.element).data('createWidgetName', editorWidget); return jQuery(data.element); }, // Deprecated. disableEditor: function (data) { return this.disablePropertyEditor(data); }, disablePropertyEditor: function (data) { var widgetName = jQuery(data.element).data('createWidgetName'); data.disabled = true; if (widgetName) { // only if there has been an editing widget registered jQuery(data.element)[widgetName](data); jQuery(data.element).removeClass('ui-state-disabled'); if (data.element.is(':focus')) { data.element.blur(); } } }, collectionWidgetName: function (data) { if (this.options.collectionWidgets[data.property] !== undefined) { // Widget configuration set for specific RDF predicate return this.options.collectionWidgets[data.property]; } var propertyType = 'default'; var attributeDefinition = this.getAttributeDefinition(data.property); if (attributeDefinition) { propertyType = attributeDefinition.range[0]; } if (this.options.collectionWidgets[propertyType] !== undefined) { return this.options.collectionWidgets[propertyType]; } return this.options.collectionWidgets['default']; }, enableCollection: function (data) { var widgetName = this.collectionWidgetName(data); if (widgetName === null) { return; } data.disabled = false; if (typeof jQuery(data.element)[widgetName] !== 'function') { throw new Error(widgetName + ' widget is not available'); } jQuery(data.element)[widgetName](data); jQuery(data.element).data('createCollectionWidgetName', widgetName); return jQuery(data.element); }, disableCollection: function (data) { var widgetName = jQuery(data.element).data('createCollectionWidgetName'); if (widgetName === null) { return; } data.disabled = true; if (widgetName) { // only if there has been an editing widget registered jQuery(data.element)[widgetName](data); jQuery(data.element).removeClass('ui-state-disabled'); } } }); })(jQuery); // Create.js - On-site web editing interface // (c) 2012 Tobias Herrmann, IKS Consortium // Create may be freely distributed under the MIT license. // For all details and documentation: // http://createjs.org/ (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false document:false */ 'use strict'; // # Base property editor widget // // This property editor widget provides a very simplistic `contentEditable` // property editor that can be used as standalone, but should more usually be // used as the base class for other property editor widgets. // This property editor widget is only useful for textual properties! // // Subclassing this base property editor widget is easy: // // jQuery.widget('Namespace.MyWidget', jQuery.Create.editWidget, { // // override any properties // }); jQuery.widget('Create.editWidget', { options: { disabled: false, vie: null }, // override to enable the widget enable: function () { this.element.attr('contenteditable', 'true'); }, // override to disable the widget disable: function (disable) { this.element.attr('contenteditable', 'false'); }, // called by the jQuery UI plugin factory when creating the property editor // widget instance _create: function () { this._registerWidget(); this._initialize(); if (_.isFunction(this.options.decorate) && _.isFunction(this.options.decorateParams)) { // TRICKY: we can't use this.options.decorateParams()'s 'propertyName' // parameter just yet, because it will only be available after this // object has been created, but we're currently in the constructor! // Hence we have to duplicate part of its logic here. this.options.decorate(this.options.decorateParams(null, { propertyName: this.options.property, propertyEditor: this, propertyElement: this.element, // Deprecated. editor: this, predicate: this.options.property, element: this.element })); } }, // called every time the property editor widget is called _init: function () { if (this.options.disabled) { this.disable(); return; } this.enable(); }, // override this function to initialize the property editor widget functions _initialize: function () { var self = this; this.element.on('focus', function () { if (self.options.disabled) { return; } self.options.activated(); }); this.element.on('blur', function () { if (self.options.disabled) { return; } self.options.deactivated(); }); var before = this.element.html(); this.element.on('keyup paste', function (event) { if (self.options.disabled) { return; } var current = jQuery(this).html(); if (before !== current) { before = current; self.options.changed(current); } }); }, // used to register the property editor widget name with the DOM element _registerWidget: function () { this.element.data("createWidgetName", this.widgetName); } }); })(jQuery); // Create.js - On-site web editing interface // (c) 2012 Tobias Herrmann, IKS Consortium // (c) 2011 Rene Kapusta, Evo42 // Create may be freely distributed under the MIT license. // For all details and documentation: // http://createjs.org/ (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false document:false Aloha:false */ 'use strict'; // # Aloha editing widget // // This widget allows editing textual contents using the // [Aloha](http://aloha-editor.org) rich text editor. // // Due to licensing incompatibilities, Aloha Editor needs to be installed // and configured separately. jQuery.widget('Create.alohaWidget', jQuery.Create.editWidget, { _initialize: function () {}, enable: function () { var options = this.options; var editable; var currentElement = Aloha.jQuery(options.element.get(0)).aloha(); _.each(Aloha.editables, function (aloha) { // Find the actual editable instance so we can hook to the events // correctly if (aloha.obj.get(0) === currentElement.get(0)) { editable = aloha; } }); if (!editable) { return; } editable.vieEntity = options.entity; // Subscribe to activation and deactivation events Aloha.bind('aloha-editable-activated', function (event, data) { if (data.editable !== editable) { return; } options.activated(); }); Aloha.bind('aloha-editable-deactivated', function (event, data) { if (data.editable !== editable) { return; } options.deactivated(); }); Aloha.bind('aloha-smart-content-changed', function (event, data) { if (data.editable !== editable) { return; } if (!data.editable.isModified()) { return true; } options.changed(data.editable.getContents()); data.editable.setUnmodified(); }); this.options.disabled = false; }, disable: function () { Aloha.jQuery(this.options.element.get(0)).mahalo(); this.options.disabled = true; } }); })(jQuery); // Create.js - On-site web editing interface // (c) 2012 Tobias Herrmann, IKS Consortium // Create may be freely distributed under the MIT license. // For all details and documentation: (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false document:false CKEDITOR:false */ 'use strict'; // # CKEditor editing widget // // This widget allows editing textual content areas with the // [CKEditor](http://ckeditor.com/) rich text editor. jQuery.widget('Create.ckeditorWidget', jQuery.Create.editWidget, { enable: function () { this.element.attr('contentEditable', 'true'); this.editor = CKEDITOR.inline(this.element.get(0)); this.options.disabled = false; var widget = this; this.editor.on('focus', function () { widget.options.activated(); }); this.editor.on('blur', function () { widget.options.activated(); }); this.editor.on('key', function () { widget.options.changed(widget.editor.getData()); }); this.editor.on('paste', function () { widget.options.changed(widget.editor.getData()); }); this.editor.on('afterCommandExec', function () { widget.options.changed(widget.editor.getData()); }); }, disable: function () { if (!this.editor) { return; } this.element.attr('contentEditable', 'false'); this.editor.destroy(); this.editor = null; }, _initialize: function () { CKEDITOR.disableAutoInline = true; } }); })(jQuery); // Create.js - On-site web editing interface // (c) 2012 Tobias Herrmann, IKS Consortium // Create may be freely distributed under the MIT license. // For all details and documentation: // http://createjs.org/ (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false document:false */ 'use strict'; // # Hallo editing widget // // This widget allows editing textual content areas with the // [Hallo](http://hallojs.org) rich text editor. jQuery.widget('Create.halloWidget', jQuery.Create.editWidget, { options: { editorOptions: {}, disabled: true, toolbarState: 'full', vie: null, entity: null }, enable: function () { jQuery(this.element).hallo({ editable: true }); this.options.disabled = false; }, disable: function () { jQuery(this.element).hallo({ editable: false }); this.options.disabled = true; }, _initialize: function () { jQuery(this.element).hallo(this.getHalloOptions()); var self = this; jQuery(this.element).on('halloactivated', function (event, data) { self.options.activated(); }); jQuery(this.element).on('hallodeactivated', function (event, data) { self.options.deactivated(); }); jQuery(this.element).on('hallomodified', function (event, data) { self.options.changed(data.content); data.editable.setUnmodified(); }); jQuery(document).on('midgardtoolbarstatechange', function(event, data) { // Switch between Hallo configurations when toolbar state changes if (data.display === self.options.toolbarState) { return; } self.options.toolbarState = data.display; var newOptions = self.getHalloOptions(); self.element.hallo('changeToolbar', newOptions.parentElement, newOptions.toolbar, true); }); }, getHalloOptions: function() { var defaults = { plugins: { halloformat: {}, halloblock: {}, hallolists: {}, hallolink: {}, halloimage: { entity: this.options.entity } }, buttonCssClass: 'create-ui-btn-small', placeholder: '[' + this.options.property + ']' }; if (typeof this.element.annotate === 'function' && this.options.vie.services.stanbol) { // Enable Hallo Annotate plugin by default if user has annotate.js // loaded and VIE has Stanbol enabled defaults.plugins.halloannotate = { vie: this.options.vie }; } if (this.options.toolbarState === 'full') { // Use fixed toolbar in the Create tools area defaults.parentElement = jQuery('.create-ui-toolbar-dynamictoolarea .create-ui-tool-freearea'); defaults.toolbar = 'halloToolbarFixed'; } else { // Tools area minimized, use floating toolbar defaults.parentElement = 'body'; defaults.toolbar = 'halloToolbarContextual'; } return _.extend(defaults, this.options.editorOptions); } }); })(jQuery); // Create.js - On-site web editing interface // (c) 2012 Henri Bergius, IKS Consortium // Create may be freely distributed under the MIT license. // For all details and documentation: // http://createjs.org/ (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false document:false */ 'use strict'; // # Redactor editing widget // // This widget allows editing textual content areas with the // [Redactor](http://redactorjs.com/) rich text editor. jQuery.widget('Create.redactorWidget', jQuery.Create.editWidget, { editor: null, options: { editorOptions: {}, disabled: true }, enable: function () { jQuery(this.element).redactor(this.getRedactorOptions()); this.options.disabled = false; }, disable: function () { jQuery(this.element).destroyEditor(); this.options.disabled = true; }, _initialize: function () { var self = this; jQuery(this.element).on('focus', function (event) { self.options.activated(); }); /* jQuery(this.element).on('blur', function (event) { self.options.deactivated(); }); */ }, getRedactorOptions: function () { var self = this; var overrides = { keyupCallback: function (obj, event) { self.options.changed(jQuery(self.element).getCode()); }, execCommandCallback: function (obj, command) { self.options.changed(jQuery(self.element).getCode()); } }; return _.extend(self.options.editorOptions, overrides); } }); })(jQuery); // Create.js - On-site web editing interface // (c) 2011-2012 Henri Bergius, IKS Consortium // Create may be freely distributed under the MIT license. // For all details and documentation: // http://createjs.org/ (function (jQuery, undefined) { // Run JavaScript in strict mode /*global jQuery:false _:false window:false */ 'use strict'; jQuery.widget('Midgard.midgardStorage', { saveEnabled: true, options: { // Whether to use localstorage localStorage: false, removeLocalstorageOnIgnore: true, // VIE instance to use for storage handling vie: null, // URL callback for Backbone.sync url: '', // Whether to enable automatic saving autoSave: false, // How often to autosave in milliseconds autoSaveInterval: 5000, // Whether to save entities that are referenced by entities // we're saving to the server. saveReferencedNew: false, saveReferencedChanged: false, // Namespace used for events from midgardEditable-derived widget editableNs: 'midgardeditable', // CSS selector for the Edit button, leave to null to not bind // notifications to any element editSelector: '#midgardcreate-edit a', localize: function (id, language) { return window.midgardCreate.localize(id, language); }, language: null }, _create: function () { var widget = this; this.changedModels = []; if (window.localStorage) { this.options.localStorage = true; } this.vie = this.options.vie; this.vie.entities.on('add', function (model) { // Add the back-end URL used by Backbone.sync model.url = widget.options.url; model.toJSON = model.toJSONLD; }); widget._bindEditables(); if (widget.options.autoSave) { widget._autoSave(); } }, _autoSave: function () { var widget = this; widget.saveEnabled = true; var doAutoSave = function () { if (!widget.saveEnabled) { return; } if (widget.changedModels.length === 0) { return; } widget.saveRemoteAll({ // We make autosaves silent so that potential changes from server // don't disrupt user while writing. silent: true }); }; var timeout = window.setInterval(doAutoSave, widget.options.autoSaveInterval); this.element.on('startPreventSave', function () { if (timeout) { window.clearInterval(timeout); timeout = null; } widget.disableAutoSave(); }); this.element.on('stopPreventSave', function () { if (!timeout) { timeout = window.setInterval(doAutoSave, widget.options.autoSaveInterval); } widget.enableAutoSave(); }); }, enableAutoSave: function () { this.saveEnabled = true; }, disableAutoSave: function () { this.saveEnabled = false; }, _bindEditables: function () { var widget = this; this.restorables = []; var restorer; widget.element.on(widget.options.editableNs + 'changed', function (event, options) { if (_.indexOf(widget.changedModels, options.instance) === -1) { widget.changedModels.push(options.instance); } widget._saveLocal(options.instance); }); widget.element.on(widget.options.editableNs + 'disable', function (event, options) { widget.revertChanges(options.instance); }); widget.element.on(widget.options.editableNs + 'enable', function (event, options) { if (!options.instance._originalAttributes) { options.instance._originalAttributes = _.clone(options.instance.attributes); } if (!options.instance.isNew() && widget._checkLocal(options.instance)) { // We have locally-stored modifications, user needs to be asked widget.restorables.push(options.instance); } /*_.each(options.instance.attributes, function (attributeValue, property) { if (attributeValue instanceof widget.vie.Collection) { widget._readLocalReferences(options.instance, property, attributeValue); } });*/ }); widget.element.on('midgardcreatestatechange', function (event, options) { if (options.state === 'browse' || widget.restorables.length === 0) { widget.restorables = []; if (restorer) { restorer.close(); } return; } restorer = widget.checkRestore(); }); widget.element.on('midgardstorageloaded', function (event, options) { if (_.indexOf(widget.changedModels, options.instance) === -1) { widget.changedModels.push(options.instance); } }); }, checkRestore: function () { var widget = this; if (widget.restorables.length === 0) { return; } var message; var restorer; if (widget.restorables.length === 1) { message = _.template(widget.options.localize('localModification', widget.options.language), { label: widget.restorables[0].getSubjectUri() }); } else { message = _.template(widget.options.localize('localModifications', widget.options.language), { number: widget.restorables.length }); } var doRestore = function (event, notification) { widget.restoreLocalAll(); restorer.close(); }; var doIgnore = function (event, notification) { widget.ignoreLocal(); restorer.close(); }; restorer = jQuery('body').midgardNotifications('create', { bindTo: widget.options.editSelector, gravity: 'TR', body: message, timeout: 0, actions: [ { name: 'restore', label: widget.options.localize('Restore', widget.options.language), cb: doRestore, className: 'create-ui-btn' }, { name: 'ignore', label: widget.options.localize('Ignore', widget.options.language), cb: doIgnore, className: 'create-ui-btn' } ], callbacks: { beforeShow: function () { if (!window.Mousetrap) { return; } window.Mousetrap.bind(['command+shift+r', 'ctrl+shift+r'], function (event) { event.preventDefault(); doRestore(); }); window.Mousetrap.bind(['command+shift+i', 'ctrl+shift+i'], function (event) { event.preventDefault(); doIgnore(); }); }, afterClose: function () { if (!window.Mousetrap) { return; } window.Mousetrap.unbind(['command+shift+r', 'ctrl+shift+r']); window.Mousetrap.unbind(['command+shift+i', 'ctrl+shift+i']); } } }); return restorer; }, restoreLocalAll: function () { _.each(this.restorables, function (instance) { this.readLocal(instance); }, this); this.restorables = []; }, ignoreLocal: function () { if (this.options.removeLocalstorageOnIgnore) { _.each(this.restorables, function (instance) { this._removeLocal(instance); }, this); } this.restorables = []; }, saveReferences: function (model) { _.each(model.attributes, function (value, property) { if (!value || !value.isCollection) { return; } value.each(function (referencedModel) { if (this.changedModels.indexOf(referencedModel) !== -1) { // The referenced model is already in the save queue return; } if (referencedModel.isNew() && this.options.saveReferencedNew) { return referencedModel.save(); } if (referencedModel.hasChanged() && this.options.saveReferencedChanged) { return referencedModel.save(); } }, this); }, this); }, saveRemote: function (model, options) { // Optionally handle entities referenced in this model first this.saveReferences(model); this._trigger('saveentity', null, { entity: model, options: options }); var widget = this; model.save(null, _.extend({}, options, { success: function (m, response) { // From now on we're going with the values we have on server model._originalAttributes = _.clone(model.attributes); widget._removeLocal(model); window.setTimeout(function () { // Remove the model from the list of changed models after saving widget.changedModels.splice(widget.changedModels.indexOf(model), 1); }, 0); if (_.isFunction(options.success)) { options.success(m, response); } widget._trigger('savedentity', null, { entity: model, options: options }); }, error: function (m, response) { if (_.isFunction(options.error)) { options.error(m, response); } } })); }, saveRemoteAll: function (options) { var widget = this; if (widget.changedModels.length === 0) { return; } widget._trigger('save', null, { entities: widget.changedModels, options: options, // Deprecated models: widget.changedModels }); var notification_msg; var needed = widget.changedModels.length; if (needed > 1) { notification_msg = _.template(widget.options.localize('saveSuccessMultiple', widget.options.language), { number: needed }); } else { notification_msg = _.template(widget.options.localize('saveSuccess', widget.options.language), { label: widget.changedModels[0].getSubjectUri() }); } widget.disableAutoSave(); _.each(widget.changedModels, function (model) { this.saveRemote(model, { success: function (m, response) { needed--; if (needed <= 0) { // All models were happily saved widget._trigger('saved', null, { options: options }); if (options && _.isFunction(options.success)) { options.success(m, response); } jQuery('body').midgardNotifications('create', { body: notification_msg }); widget.enableAutoSave(); } }, error: function (m, err) { if (options && _.isFunction(options.error)) { options.error(m, err); } jQuery('body').midgardNotifications('create', { body: _.template(widget.options.localize('saveError', widget.options.language), { error: err.responseText || '' }), timeout: 0 }); widget._trigger('error', null, { instance: model }); } }); }, this); }, _saveLocal: function (model) { if (!this.options.localStorage) { return; } if (model.isNew()) { // Anonymous object, save as refs instead if (!model.primaryCollection) { return; } return this._saveLocalReferences(model.primaryCollection.subject, model.primaryCollection.predicate, model); } window.localStorage.setItem(model.getSubjectUri(), JSON.stringify(model.toJSONLD())); }, _getReferenceId: function (model, property) { return model.id + ':' + property; }, _saveLocalReferences: function (subject, predicate, model) { if (!this.options.localStorage) { return; } if (!subject || !predicate) { return; } var widget = this; var identifier = subject + ':' + predicate; var json = model.toJSONLD(); if (window.localStorage.getItem(identifier)) { var referenceList = JSON.parse(window.localStorage.getItem(identifier)); var index = _.pluck(referenceList, '@').indexOf(json['@']); if (index !== -1) { referenceList[index] = json; } else { referenceList.push(json); } window.localStorage.setItem(identifier, JSON.stringify(referenceList)); return; } window.localStorage.setItem(identifier, JSON.stringify([json])); }, _checkLocal: function (model) { if (!this.options.localStorage) { return false; } var local = window.localStorage.getItem(model.getSubjectUri()); if (!local) { return false; } return true; }, hasLocal: function (model) { if (!this.options.localStorage) { return false; } if (!window.localStorage.getItem(model.getSubjectUri())) { return false; } return true; }, readLocal: function (model) { if (!this.options.localStorage) { return; } var local = window.localStorage.getItem(model.getSubjectUri()); if (!local) { return; } if (!model._originalAttributes) { model._originalAttributes = _.clone(model.attributes); } var parsed = JSON.parse(local); var entity = this.vie.entities.addOrUpdate(parsed, { overrideAttributes: true }); this._trigger('loaded', null, { instance: entity }); }, _readLocalReferences: function (model, property, collection) { if (!this.options.localStorage) { return; } var identifier = this._getReferenceId(model, property); var local = window.localStorage.getItem(identifier); if (!local) { return; } collection.add(JSON.parse(local)); }, revertChanges: function (model) { var widget = this; // Remove unsaved collection members if (!model) { return; } _.each(model.attributes, function (attributeValue, property) { if (attributeValue instanceof widget.vie.Collection) { var removables = []; attributeValue.forEach(function (model) { if (model.isNew()) { removables.push(model); } }); attributeValue.remove(removables); } }); // Restore original object properties if (!model.changedAttributes()) { if (model._originalAttributes) { model.set(model._originalAttributes); } return; } model.set(model.previousAttributes()); }, _removeLocal: function (model) { if (!this.options.localStorage) { return; } window.localStorage.removeItem(model.getSubjectUri()); } }); })(jQuery); if (window.midgardCreate === undefined) { window.midgardCreate = {}; } if (window.midgardCreate.locale === undefined) { window.midgardCreate.locale = {}; } window.midgardCreate.locale.en = { // Session-state buttons for the main toolbar 'Save': 'Save', 'Saving': 'Saving', 'Cancel': 'Cancel', 'Edit': 'Edit', // Storage status messages 'localModification': 'Item "<%= label %>" has local modifications', 'localModifications': '<%= number %> items on this page have local modifications', 'Restore': 'Restore', 'Ignore': 'Ignore', 'saveSuccess': 'Item "<%= label %>" saved successfully', 'saveSuccessMultiple': '<%= number %> items saved successfully', 'saveError': 'Error occurred while saving
<%= error %>', // Tagging 'Item tags': 'Item tags', 'Suggested tags': 'Suggested tags', 'Tags': 'Tags', 'add a tag': 'add a tag', // Collection widgets 'Add': 'Add', 'Choose type to add': 'Choose type to add' };