/** * This is a helper module for porting plugins from the old * ui-attributefield.js in the aloha core to the new ui-plugin. * This interface is obsolete and must not be used for new implementations. */ define([ 'jquery', 'ui/ui', 'ui/component', 'ui/scopes', 'ui/context', 'ui/utils', 'aloha/repositorymanager', 'aloha/selection', 'aloha/console', 'ui/vendor/jquery-ui-autocomplete-html' ], function ( $, Ui, Component, Scopes, Context, Utils, RepositoryManager, Selection, console ) { 'use strict'; // Main responsibilities implemented by the attribute-field are // // * setting a target object and attribute and the subsequent change of // that target attribute (example link plugin, table plugin) // * background color highlighting of the target object (example link plugin) // * a placeholder in the attribute field with a grey foreground color // (example link plugin) // * maintain a current repository item to distinguish link plugin // repository items from literal values typed in the attribute field // (example link plugin) // * repository manager markObject on the target object if a repository // item was selected (example link plugin) /** * Creates a new attribute field. * * @param {!Object} props * A map containing the following properties * name - * label - some text that will be displayed alongside * the attribute field, * labelClass - a class to identify the label element, * valueField - * displayField - * objectTypeFilter - * placeholder - * noTargetHighlight - * targetHighlightCass - a class to be identify focused element * cls - * width - * scope - * element - the element to use. * If not supplied, a new one will be created. */ var AttributeField = function (props) { var valueField = props.valueField || 'id', displayField = props.displayField || 'name', objectTypeFilter = props.objectTypeFilter || ['all'], placeholder = props.placeholder, noTargetHighlight = !!props.noTargetHighlight, targetHighlightClass = props.targetHighlightClass, element = props.element ? $(props.element) : $(''), component, template, resourceItem, resourceValue, targetObject, targetAttribute, lastAttributeValue; if (props.cls) { element.addClass(props.cls); } if (props.width) { element.width(props.width); } component = Ui.adopt(props.name, Component, { scope: props.scope, init: function(){ if (props.element) { this.element = element; } else { if (props.label) { this.element = Utils.wrapWithLabel(props.label, element); if (props.labelClass) { this.element.addClass(props.labelClass); } } else { // Why do we have to wrap the element in a span? It // doesn't seem to work otherwise. this.element = $('').append(element); } } element.autocomplete({ 'html': true, 'appendTo': Context.selector, 'source': function( req, res ) { RepositoryManager.query({ queryString: req.term, objectTypeFilter: objectTypeFilter }, function( data ) { res($.map(data.items, function(item) { return { label: parse(template, item), value: item.name, obj: item }; })); }); }, "select": onSelect }); } }); element .bind("focus", onFocus) .bind("blur", onBlur) .bind("keydown", onKeyDown) .bind("keyup", onKeyup); setPlaceholder(); // Because IE7 doesn't give us the blur event when the editable // is deactivated and the toolbar disappears (other browsers do). // TODO unbind, otherwise mermory leak Aloha.bind('aloha-editable-deactivated', onBlur); function onSelect(event, ui) { if (ui.item) { setItem(ui.item.obj); } finishEditing(); } function onBlur() { finishEditing(); } function onFocus(event, ui) { if ( ! $(event.target).is(':visible') ) { // The check for visible fixes the bug that the background // color of the target element is not restored. // Rationale: it's possible for the input to receive the focus event, // for example if it was triggered programatically, even if // it isn't visible. Problem is, if it's not visible, then // it will not really get focused and consequently, there // will be no blur event either. However, we must be able to // assume that the blur event will be fired so that we can // clean up the background color. return; } changeTargetBackground(); // Remove placeholder if (getValue() === placeholder) { setValue(''); } } function onKeyDown(event){ // on ENTER or ESC leave the editing if ( event.keyCode == 13 || event.keyCode == 27 ) { event.preventDefault(); } } function onKeyup(event) { // If this attribute field currently refers to a repository // item, and the user edits the contents of the input field, // this attribute field seizes to refer to the repository item. if (resourceItem && resourceValue !== getValue()) { resourceItem = null; resourceValue = null; } // This handles attribute updates for non-repository, literal urls typed into the input field. // Input values that refer to a repository item are handled via setItem(). if ( ! resourceItem ) { setAttribute(targetAttribute, getValue()); } if ( ( event.keyCode == 13 || event.keyCode == 27 ) ) { // Set focus to link element and select the object Selection.getRangeObject().select(); finishEditing(); } } function finishEditing() { restoreTargetBackground(); if ( ! targetObject || lastAttributeValue === $(targetObject).attr(targetAttribute)) { return; } // when no resource item was selected, remove any marking of the target object if ( ! resourceItem ) { RepositoryManager.markObject( targetObject ); } if (getValue() === '') { setPlaceholder(); } } function changeTargetBackground() { var target = $(targetObject); if (targetHighlightClass) { target.addClass(targetHighlightClass); } if (noTargetHighlight) { return; } // Make sure that multiple invokations of // changeTargetBackground don't set an incorrect // data-original-background-color. restoreTargetBackground(); // set background color to give visual feedback which link is modified if (target.context && target.context.style && target.context.style['background-color']) { target.attr('data-original-background-color', target.context.style['background-color']); } target.css('background-color', '#80B5F2'); } function restoreTargetBackground() { var target = $(targetObject); if (targetHighlightClass) { target.removeClass(targetHighlightClass); } if (noTargetHighlight) { return; } // Remove the highlighting and restore original color if was set before var color = target.attr('data-original-background-color'); target.css('background-color', color || ''); if (!target.attr('style')) { target.removeAttr('style'); } target.removeAttr('data-original-background-color'); } function parse(template, item) { return template.replace( /\{([^}]+)\}/g, function(_, name) { return name in item ? item[ name ] : ""; }); } function setPlaceholder() { if (null === placeholder) { return; } element.css('color', '#AAA'); element.val(placeholder); } function setTemplate(tmpl){ template = tmpl; } /** * When at least on objectType is set the value in the Attribute field does a query to all registered repositories. * @param {Array} objectTypeFilter The array of objectTypeFilter to be searched for. * @void */ function setObjectTypeFilter(objTypeFilter) { objectTypeFilter = objTypeFilter; } /** * Adding a listener to the field * @param {String} eventname The name of the event. Ex. 'keyup' * @param {function} handler The function that should be called when the event happens. */ function addListener(eventName, handler) { element.bind(eventName, $.proxy(handler, attrField)); } function getValue() { return element.val(); } function setValue(value) { element.val(value); element.css('color', 'black'); } function setItem(item) { resourceItem = item; if (item) { // TODO split display field by '.' and get corresponding attribute, because it could be a properties attribute. var v = item[displayField]; // set the value into the field setValue(v); // store the value to be the "reference" value for the currently selected resource item resourceValue = v; setAttribute(targetAttribute, item[valueField]); RepositoryManager.markObject(targetObject, item); } else { resourceValue = null; } } function getItem() { return resourceItem; } /** * Sets an attribute optionally based on a regex on reference * @param {String} attr The Attribute name which should be set. Ex. "lang" * @param {String} value The value to set. Ex. "de-AT" * @param {String} regex The regex when the attribute should be set. The regex is applied to the value of refernece. * @param {String} reference The value for the regex. */ function setAttribute(attr, value, regex, reference) { if (targetObject) { // check if a reference value is submitted to check against with a regex var setAttr = true; if (typeof reference != 'undefined') { var regxp = new RegExp(regex); if ( ! reference.match(regxp) ) { setAttr = false; } } // if no regex was successful or no reference value // was submitted remove the attribute if ( setAttr ) { $(targetObject).attr(attr, value); } else { $(targetObject).removeAttr(attr); } } } /** * Sets the target Object of which the Attribute should be modified * @param {jQuery} obj the target object * @param {String} attr Attribute to be modified ex. "href" of a link * @void */ function setTargetObject(obj, attr) { targetObject = obj; targetAttribute = attr; setItem(null); if (obj && attr) { lastAttributeValue = $(obj).attr(attr); setValue($(targetObject).attr(targetAttribute)); } else { setValue(''); return; } // check whether a repository item is linked to the object RepositoryManager.getObject( obj, function ( items ) { if (items && items.length > 0) { setItem(items[0]); } } ); } function getTargetObject() { return targetObject; } function focus() { component.focus(); element.focus(); } function foreground() { component.foreground(); } function show() { element.show(); } function hide() { element.hide(); } function getInputId(){ return element.attr("id"); } function hasInputElem() { return true; } function getInputElem() { return element[0]; } var attrField = { getInputElem: getInputElem, hasInputElem: hasInputElem, getInputId: getInputId, hide: hide, show: show, foreground: foreground, focus: focus, getTargetObject: getTargetObject, setTargetObject: setTargetObject, setAttribute: setAttribute, getItem: getItem, setItem: setItem, setValue: setValue, getValue: getValue, addListener: addListener, setObjectTypeFilter: setObjectTypeFilter, setTemplate: setTemplate, setPlaceholder: setPlaceholder }; return attrField; }; return AttributeField; });