1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-18 06:57:52 +00:00
seahub/media/aloha-0.22.7/lib/aloha/editable.js
llj 720ac28c22 [aloha] upgraded to 0.22.7 and added textcolor plugin
* textcolor: fixed plugin bugs, added translation for zh
* image: fixed default.jpg src bug
* added 'ru' support for seaf edit
* rm aloha-0.22.3 and ununsed files in aloha-0.22.7
2013-01-15 14:48:04 +08:00

1033 lines
30 KiB
JavaScript

/* editable.js is part of Aloha Editor project http://aloha-editor.org
*
* Aloha Editor is a WYSIWYG HTML5 inline editing library and editor.
* Copyright (c) 2010-2012 Gentics Software GmbH, Vienna, Austria.
* Contributors http://aloha-editor.org/contribution.php
*
* Aloha Editor is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* Aloha Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As an additional permission to the GNU GPL version 2, you may distribute
* non-source (e.g., minimized or compacted) forms of the Aloha-Editor
* source code without the copy of the GNU GPL normally required,
* provided you include this license notice and a URL through which
* recipients can access the Corresponding Source.
*/
define([
'aloha/core',
'util/class',
'jquery',
'aloha/pluginmanager',
'aloha/selection',
'aloha/markup',
'aloha/contenthandlermanager',
'aloha/console',
'aloha/block-jump',
'aloha/ephemera',
'util/dom2',
'PubSub',
'aloha/copypaste'
], function (
Aloha,
Class,
$,
PluginManager,
Selection,
Markup,
ContentHandlerManager,
console,
BlockJump,
Ephemera,
Dom,
PubSub,
CopyPaste
) {
'use strict';
var jQuery = $;
var unescape = window.unescape,
GENTICS = window.GENTICS,
// True, if the next editable activate event should not be handled
ignoreNextActivateEvent = false;
/**
* A cache to hold information derived, and used in getContents().
* @type {object<string,(string|jQuery.<HTMLElement>)>}
* @private
*/
var editableContentCache = {};
// default supported and custom content handler settings
// @TODO move to new config when implemented in Aloha
Aloha.defaults.contentHandler = {};
Aloha.defaults.contentHandler.initEditable = ['blockelement', 'sanitize'];
Aloha.defaults.contentHandler.getContents = ['blockelement', 'sanitize', 'basic'];
// The insertHtml contenthandler ( paste ) will, by default, use all
// registered content handlers.
//Aloha.defaults.contentHandler.insertHtml = void 0;
if (typeof Aloha.settings.contentHandler === 'undefined') {
Aloha.settings.contentHandler = {};
}
var defaultContentSerializer = function (editableElement) {
return jQuery(editableElement).html();
};
var contentSerializer = defaultContentSerializer;
/**
* Triggers smartContentChange handlers.
*
* @param {Aloha.Editable}
* @return {string} Content that has been processed by getContent handlers
* and smartContentChange handlers.
*/
function handleSmartContentChange(editable) {
return ContentHandlerManager.handleContent(editable.getContents(), {
contenthandler: Aloha.settings.contentHandler.smartContentChange
}, editable);
}
/**
* List of observed key, mapped against their keycodes.
*
* @type {object<number, string>}
* @const
*/
var KEYCODES = {
65: 'a'
};
/**
* Handlers for various key combos.
* Each handler ought to return false if they do not want the event to
* continue propagating.
*/
var keyBindings = {
'ctrl+a': function () {
var editable = CopyPaste.getEditableAt(CopyPaste.getRange());
if (editable) {
CopyPaste.selectAllOf(editable.obj[0]);
return false;
}
}
}
/**
* Gets the name of the modifier key if is in effect for the given event.
*
* eg: <Ctrl>+c
*
* @param {jQuery.Event} $event
* @return {string|null} Modifier string or null if no modifier is in
* effect.
*
*/
function keyModifier($event) {
return $event.altKey ? 'alt' :
$event.ctrlKey ? 'ctrl' :
$event.shiftKey ? 'shift' : null;
}
/**
* Handles keydown events that are fired on the page's document.
*
* @param {jQuery.Event) $event
* @return {boolean} Returns false to stop propagation; undefined otherwise.
*/
function onKeydown($event) {
if (!Aloha.activeEditable) {
return;
}
var key = KEYCODES[$event.which];
if (key) {
var modifier = keyModifier($event);
var combo = (modifier ? modifier + '+' : '') + key;
if (keyBindings[combo]) {
return keyBindings[combo]($event);
}
}
}
$(document).keydown(onKeydown);
/**
* Editable object
* @namespace Aloha
* @class Editable
* @method
* @constructor
* @param {Object} obj jQuery object reference to the object
*/
Aloha.Editable = Class.extend({
_constructor: function (obj) {
// check wheter the object has an ID otherwise generate and set
// globally unique ID
if (!obj.attr('id')) {
obj.attr('id', GENTICS.Utils.guid());
}
// store object reference
this.obj = obj;
this.originalObj = obj;
this.ready = false;
// delimiters, timer and idle for smartContentChange
// smartContentChange triggers -- tab: '\u0009' - space: '\u0020' - enter: 'Enter'
// backspace: U+0008 - delete: U+007F
this.sccDelimiters = [':', ';', '.', '!', '?', ',',
unescape('%u0009'), unescape('%u0020'), unescape('%u0008'), unescape('%u007F'), 'Enter'];
this.sccIdle = 5000;
this.sccDelay = 500;
this.sccTimerIdle = false;
this.sccTimerDelay = false;
// see keyset http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html
this.keyCodeMap = {
93: 'Apps', // The Application key
18: 'Alt', // The Alt ( Menu ) key.
20: 'CapsLock', // The Caps Lock ( Capital ) key.
17: 'Control', // The Control ( Ctrl ) key.
40: 'Down', // The Down Arrow key.
35: 'End', // The End key.
13: 'Enter', // The Enter key.
112: 'F1', // The F1 key.
113: 'F2', // The F2 key.
114: 'F3', // The F3 key.
115: 'F4', // The F4 key.
116: 'F5', // The F5 key.
117: 'F6', // The F6 key.
118: 'F7', // The F7 key.
119: 'F8', // The F8 key.
120: 'F9', // The F9 key.
121: 'F10', // The F10 key.
122: 'F11', // The F11 key.
123: 'F12', // The F12 key.
// Anybody knows the keycode for F13-F24?
36: 'Home', // The Home key.
45: 'Insert', // The Insert ( Ins ) key.
37: 'Left', // The Left Arrow key.
224: 'Meta', // The Meta key.
34: 'PageDown', // The Page Down ( Next ) key.
33: 'PageUp', // The Page Up key.
19: 'Pause', // The Pause key.
44: 'PrintScreen', // The Print Screen ( PrintScrn, SnapShot ) key.
39: 'Right', // The Right Arrow key.
145: 'Scroll', // The scroll lock key
16: 'Shift', // The Shift key.
38: 'Up', // The Up Arrow key.
91: 'Win', // The left Windows Logo key.
92: 'Win' // The right Windows Logo key.
};
this.placeholderClass = 'aloha-placeholder';
Aloha.registerEditable(this);
},
/**
* Initialize the editable
* @return void
* @hide
*/
init: function () {
var me = this;
// TODO make editables their own settings.
this.settings = Aloha.settings;
// smartContentChange settings
// @TODO move to new config when implemented in Aloha
if (Aloha.settings && Aloha.settings.smartContentChange) {
if (Aloha.settings.smartContentChange.delimiters) {
this.sccDelimiters = Aloha.settings.smartContentChange.delimiters;
}
if (Aloha.settings.smartContentChange.idle) {
this.sccIdle = Aloha.settings.smartContentChange.idle;
}
if (Aloha.settings.smartContentChange.delay) {
this.sccDelay = Aloha.settings.smartContentChange.delay;
}
}
// check if Aloha can handle the obj as Editable
if (!this.check(this.obj)) {
//Aloha.log( 'warn', this, 'Aloha cannot handle {' + this.obj[0].nodeName + '}' );
this.destroy();
return;
}
// apply content handler to clean up content
if (typeof Aloha.settings.contentHandler.getContents === 'undefined') {
Aloha.settings.contentHandler.getContents = Aloha.defaults.contentHandler.getContents;
}
// apply content handler to clean up content
if (typeof Aloha.settings.contentHandler.initEditable === 'undefined') {
Aloha.settings.contentHandler.initEditable = Aloha.defaults.contentHandler.initEditable;
}
var content = me.obj.html();
content = ContentHandlerManager.handleContent(content, {
contenthandler: Aloha.settings.contentHandler.initEditable,
command: 'initEditable'
}, me);
me.obj.html(content);
// Because editables can only properly be initialized when Aloha
// plugins are loaded.
Aloha.bind('aloha-plugins-loaded', function () {
me.obj.addClass('aloha-editable').contentEditable(true);
me.obj.mousedown(function (e) {
if (!Aloha.eventHandled) {
Aloha.eventHandled = true;
return me.activate(e);
}
});
me.obj.mouseup(function (e) {
Aloha.eventHandled = false;
});
me.obj.focus(function (e) {
return me.activate(e);
});
// by catching the keydown we can prevent the browser from doing its own thing
// if it does not handle the keyStroke it returns true and therefore all other
// events (incl. browser's) continue
//me.obj.keydown( function( event ) {
//me.obj.add('.aloha-block', me.obj).live('keydown', function (event) { // live not working but would be usefull
me.obj.add('.aloha-block', me.obj).keydown(function (event) {
var letEventPass = Markup.preProcessKeyStrokes(event);
me.keyCode = event.which;
if (!letEventPass) {
// the event will not proceed to key press, therefore trigger smartContentChange
me.smartContentChange(event);
}
return letEventPass;
});
// handle keypress
me.obj.keypress(function (event) {
// triggers a smartContentChange to get the right charcode
// To test try http://www.w3.org/2002/09/tests/keys.html
Aloha.activeEditable.smartContentChange(event);
});
// handle shortcut keys
me.obj.keyup(function (event) {
if (event.keyCode === 27) {
Aloha.deactivateEditable();
return false;
}
});
// register the onSelectionChange Event with the Editable field
me.obj.contentEditableSelectionChange(function (event) {
Selection.onChange(me.obj, event);
return me.obj;
});
// mark the editable as unmodified
me.setUnmodified();
// we don't do the sanitizing on aloha ready, since some plugins add elements into the content and bind
// events to it. If we sanitize by replacing the html, all events would get lost. TODO: think about a
// better solution for the sanitizing, without destroying the events apply content handler to clean up content
// var content = me.obj.html();
// if ( typeof Aloha.settings.contentHandler.initEditable === 'undefined' ) {
// Aloha.settings.contentHandler.initEditable = Aloha.defaults.contentHandler.initEditable;
// }
// content = ContentHandlerManager.handleContent( content, {
// contenthandler: Aloha.settings.contentHandler.initEditable
// } );
// me.obj.html( content );
me.snapshotContent = me.getContents();
// FF bug: check for empty editable contents ( no <br>; no whitespace )
if (jQuery.browser.mozilla) {
me.initEmptyEditable();
}
me.initPlaceholder();
me.ready = true;
// disable object resizing.
// we do this in here and with a slight delay, because
// starting with FF 15, this would cause a JS error
// if done before the first DOM object is made contentEditable.
window.setTimeout(function () {
Aloha.disableObjectResizing();
}, 20);
// throw a new event when the editable has been created
/**
* @event editableCreated fires after a new editable has been created, eg. via $( '#editme' ).aloha()
* The event is triggered in Aloha's global scope Aloha
* @param {Event} e the event object
* @param {Array} a an array which contains a reference to the currently created editable on its first position
*/
Aloha.trigger('aloha-editable-created', [me]);
PubSub.pub('aloha.editable.created', {data: me});
});
},
/**
* True, if this editable is active for editing
* @property
* @type boolean
*/
isActive: false,
/**
* stores the original content to determine if it has been modified
* @hide
*/
originalContent: null,
/**
* every time a selection is made in the current editable the selection has to
* be saved for further use
* @hide
*/
range: undefined,
/**
* Check if object can be edited by Aloha Editor
* @return {boolean } editable true if Aloha Editor can handle else false
* @hide
*/
check: function () {
/* TODO check those elements
'map', 'meter', 'object', 'output', 'progress', 'samp',
'time', 'area', 'datalist', 'figure', 'kbd', 'keygen',
'mark', 'math', 'wbr', 'area',
*/
// Extract El
var me = this,
obj = this.obj,
el = obj.get(0),
nodeName = el.nodeName.toLowerCase(),
// supported elements
textElements = ['a', 'abbr', 'address', 'article', 'aside', 'b', 'bdo', 'blockquote', 'cite', 'code', 'command', 'del', 'details', 'dfn', 'div', 'dl', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'i', 'ins', 'menu', 'nav', 'p', 'pre', 'q', 'ruby', 'section', 'small', 'span', 'strong', 'sub', 'sup', 'var'],
i,
div;
for (i = 0; i < textElements.length; ++i) {
if (nodeName === textElements[i]) {
return true;
}
}
// special handled elements
switch (nodeName) {
case 'label':
case 'button':
// TODO need some special handling.
break;
case 'textarea':
case 'input':
// Create a div alongside the textarea
div = jQuery('<div id="' + this.getId() + '-aloha" class="aloha-' + nodeName + '" />').insertAfter(obj);
// Resize the div to the textarea and
// Populate the div with the value of the textarea
// Then, hide the textarea
div.height(obj.height()).width(obj.width()).html(obj.val());
obj.hide();
// Attach a onsubmit to the form to place the HTML of the
// div back into the textarea
obj.parents('form:first').submit(function () {
obj.val(me.getContents());
});
// Swap textarea reference with the new div
this.obj = div;
// Supported
return true;
default:
break;
}
// the following elements are not supported
/*
'canvas', 'audio', 'br', 'embed', 'fieldset', 'hgroup', 'hr',
'iframe', 'img', 'input', 'map', 'script', 'select', 'style',
'svg', 'table', 'ul', 'video', 'ol', 'form', 'noscript',
*/
return false;
},
/**
* Init Placeholder
*
* @return void
*/
initPlaceholder: function () {
if (Aloha.settings.placeholder && this.isEmpty()) {
this.addPlaceholder();
}
},
/**
* Check if the conteneditable is empty.
*
* @return {Boolean}
*/
isEmpty: function () {
var editableTrimedContent = jQuery.trim(this.getContents()),
onlyBrTag = (editableTrimedContent === '<br>') ? true : false;
return (editableTrimedContent.length === 0 || onlyBrTag);
},
/**
* Check if the editable div is not empty. Fixes a FF browser bug
* see issue: https://github.com/alohaeditor/Aloha-Editor/issues/269
*
* @return {undefined}
*/
initEmptyEditable: function () {
var obj = this.obj;
if (this.empty(this.getContents())) {
jQuery(obj).prepend('<br class="aloha-cleanme" />');
}
},
/**
* Add placeholder in editable
*
* @return void
*/
addPlaceholder: function () {
var div = jQuery('<div>'),
span = jQuery('<span>'),
el,
obj = this.obj;
if (GENTICS.Utils.Dom.allowsNesting(obj[0], div[0])) {
el = div;
} else {
el = span;
}
if (jQuery("." + this.placeholderClass, obj).length !== 0) {
return;
}
jQuery(obj).append(el.addClass(this.placeholderClass));
jQuery.each(Aloha.settings.placeholder, function (selector, selectorConfig) {
if (obj.is(selector)) {
el.html(selectorConfig);
}
});
// remove browser br
jQuery('br', obj).remove();
// delete div, span, el;
},
/**
* remove placeholder from contenteditable. If setCursor is true,
* will also set the cursor to the start of the selection. However,
* this will be ASYNCHRONOUS, so if you rely on the fact that
* the placeholder is removed after calling this method, setCursor
* should be false ( or not set )
*
* @return void
*/
removePlaceholder: function (obj, setCursor) {
var placeholderClass = this.placeholderClass,
range;
if (jQuery("." + this.placeholderClass, obj).length === 0) {
return;
}
// set the cursor // remove placeholder
if (setCursor === true) {
window.setTimeout(function () {
range = new Selection.SelectionRange();
range.startContainer = range.endContainer = obj.get(0);
range.startOffset = range.endOffset = 0;
jQuery('.' + placeholderClass, obj).remove();
range.select();
}, 100);
} else {
jQuery('.' + placeholderClass, obj).remove();
}
},
/**
* destroy the editable
* @return void
*/
destroy: function () {
// leave the element just to get sure
if (this === Aloha.getActiveEditable()) {
this.blur();
}
// special handled elements
switch (this.originalObj.get(0).nodeName.toLowerCase()) {
case 'label':
case 'button':
// TODO need some special handling.
break;
case 'textarea':
case 'input':
// restore content to original textarea
this.originalObj.val(this.getContents());
this.obj.remove();
this.originalObj.show();
break;
default:
break;
}
// now the editable is not ready any more
this.ready = false;
// remove the placeholder if needed.
this.removePlaceholder(this.obj);
// initialize the object and disable contentEditable
// unbind all events
// TODO should only unbind the specific handlers.
this.obj.removeClass('aloha-editable').contentEditable(false).unbind('mousedown click dblclick focus keydown keypress keyup');
/* TODO remove this event, it should implemented as bind and unbind
// register the onSelectionChange Event with the Editable field
this.obj.contentEditableSelectionChange( function( event ) {
Aloha.Selection.onChange( me.obj, event );
return me.obj;
} );
*/
// throw a new event when the editable has been created
/**
* @event editableCreated fires after a new editable has been destroyes, eg. via $( '#editme' ).mahalo()
* The event is triggered in Aloha's global scope Aloha
* @param {Event} e the event object
* @param {Array} a an array which contains a reference to the currently created editable on its first position
*/
Aloha.trigger('aloha-editable-destroyed', [this]);
PubSub.pub('aloha.editable.destroyed', {data: this});
// finally register the editable with Aloha
Aloha.unregisterEditable(this);
},
/**
* marks the editables current state as unmodified. Use this method to inform the editable
* that it's contents have been saved
* @method
*/
setUnmodified: function () {
this.originalContent = this.getContents();
},
/**
* check if the editable has been modified during the edit process#
* @method
* @return boolean true if the editable has been modified, false otherwise
*/
isModified: function () {
return this.originalContent !== this.getContents();
},
/**
* String representation of the object
* @method
* @return Aloha.Editable
*/
toString: function () {
return 'Aloha.Editable';
},
/**
* check whether the editable has been disabled
*/
isDisabled: function () {
return !this.obj.contentEditable() || this.obj.contentEditable() === 'false';
},
/**
* disable this editable
* a disabled editable cannot be written on by keyboard
*/
disable: function () {
return this.isDisabled() || this.obj.contentEditable(false);
},
/**
* enable this editable
* reenables a disabled editable to be writteable again
*/
enable: function () {
return this.isDisabled() && this.obj.contentEditable(true);
},
/**
* activates an Editable for editing
* disables all other active items
* @method
*/
activate: function (e) {
// get active Editable before setting the new one.
var oldActive = Aloha.getActiveEditable();
// We need to ommit this call when this flag is set to true.
// This flag will only be set to true before the removePlaceholder method
// is called since that method invokes a focus event which will again trigger
// this method. We want to avoid double invokation of this method.
if (ignoreNextActivateEvent) {
ignoreNextActivateEvent = false;
return;
}
// handle special case in which a nested editable is focused by a click
// in this case the "focus" event would be triggered on the parent element
// which actually shifts the focus away to it's parent. this if is here to
// prevent this situation
if (e && e.type === 'focus' && oldActive !== null && oldActive.obj.parent().get(0) === e.currentTarget) {
return;
}
// leave immediately if this is already the active editable
if (this.isActive || this.isDisabled()) {
// we don't want parent editables to be triggered as well, so return false
return;
}
this.obj.addClass('aloha-editable-active');
Aloha.activateEditable(this);
ignoreNextActivateEvent = true;
this.removePlaceholder(this.obj, true);
ignoreNextActivateEvent = false;
this.isActive = true;
/**
* @event editableActivated fires after the editable has been activated by clicking on it.
* This event is triggered in Aloha's global scope Aloha
* @param {Event} e the event object
* @param {Array} a an array which contains a reference to last active editable on its first position, as well
* as the currently active editable on it's second position
*/
// trigger a 'general' editableActivated event
Aloha.trigger('aloha-editable-activated', {
'oldActive': oldActive,
'editable': this
});
},
/**
* handle the blur event
* this must not be attached to the blur event, which will trigger far too often
* eg. when a table within an editable is selected
* @hide
*/
blur: function () {
this.obj.blur();
this.isActive = false;
this.initPlaceholder();
this.obj.removeClass('aloha-editable-active');
/**
* @event editableDeactivated fires after the editable has been activated by clicking on it.
* This event is triggered in Aloha's global scope Aloha
* @param {Event} e the event object
* @param {Array} a an array which contains a reference to this editable
*/
Aloha.trigger('aloha-editable-deactivated', {
editable: this
});
/**
* @event smartContentChanged
*/
Aloha.activeEditable.smartContentChange({
type: 'blur'
}, null);
},
/**
* check if the string is empty
* used for zerowidth check
* @return true if empty or string is null, false otherwise
* @hide
*/
empty: function (str) {
// br is needed for chrome
return (null === str) || (jQuery.trim(str) === '' || str === '<br/>');
},
/**
* Get the contents of this editable as a HTML string or child node DOM
* objects.
*
* @param {boolean} asObject Whether or not to retreive the contents of
* this editable as child node objects or as
* HTML string.
* @return {string|jQuery.<HTMLElement>} Contents of the editable as
* DOM objects or an HTML string.
*/
getContents: function (asObject) {
var raw = this.obj.html();
var cache = editableContentCache[this.getId()];
if (!cache || raw !== cache.raw) {
BlockJump.removeZeroWidthTextNodeFix();
var $clone = this.obj.clone(false);
this.removePlaceholder($clone);
$clone = jQuery(Ephemera.prune($clone[0]));
PluginManager.makeClean($clone);
// TODO rewrite ContentHandlerManager to accept DOM trees instead of strings
$clone = jQuery('<div>' + ContentHandlerManager.handleContent($clone.html(), {
contenthandler: Aloha.settings.contentHandler.getContents,
command: 'getContents'
}, this) + '</div>');
cache = editableContentCache[this.getId()] = {};
cache.raw = raw;
cache.element = $clone;
}
if (asObject) {
return cache.element.clone().contents();
}
if (null == cache.serialized) {
cache.serialized = contentSerializer(cache.element[0]);
}
return cache.serialized;
},
/**
* Set the contents of this editable as a HTML string
* @param content as html
* @param return as object or html string
* @return contents of the editable
*/
setContents: function (content, asObject) {
var reactivate = null;
if (Aloha.getActiveEditable() === this) {
Aloha.deactivateEditable();
reactivate = this;
}
this.obj.html(content);
if (null !== reactivate) {
reactivate.activate();
}
this.smartContentChange({
type: 'set-contents'
});
return asObject ? this.obj.contents() : contentSerializer(this.obj[0]);
},
/**
* Get the id of this editable
* @method
* @return id of this editable
*/
getId: function () {
return this.obj.attr('id');
},
/**
* Generates and signals a smartContentChange event.
*
* A smart content change occurs when a special editing action, or a
* combination of interactions are performed by the user during the
* course of editing within an editable.
* The smart content change event would therefore signal to any
* component that is listening to this event, that content has been
* inserted into the editable that may need to be prococessed in a
* special way
* This is used for smart actions within the content/while editing.
* @param {Event} event
* @hide
*/
smartContentChange: function (event) {
var me = this,
uniChar = null,
re,
match;
// ignore meta keys like crtl+v or crtl+l and so on
if (event && (event.metaKey || event.crtlKey || event.altKey)) {
return false;
}
if (event && event.originalEvent) {
// regex to strip unicode
re = new RegExp("U\\+(\\w{4})");
match = re.exec(event.originalEvent.keyIdentifier);
// Use among browsers reliable which http://api.jquery.com/keypress
uniChar = (this.keyCodeMap[this.keyCode] || String.fromCharCode(event.which) || 'unknown');
}
var snapshot = null;
function getSnapshotContent() {
if (null == snapshot) {
snapshot = me.getSnapshotContent();
}
return snapshot;
}
// handle "Enter" -- it's not "U+1234" -- when returned via "event.originalEvent.keyIdentifier"
// reference: http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html
if (jQuery.inArray(uniChar, this.sccDelimiters) >= 0) {
clearTimeout(this.sccTimerIdle);
clearTimeout(this.sccTimerDelay);
this.sccTimerDelay = window.setTimeout(function () {
Aloha.trigger('aloha-smart-content-changed', {
'editable': me,
'keyIdentifier': event.originalEvent.keyIdentifier,
'keyCode': event.keyCode,
'char': uniChar,
'triggerType': 'keypress', // keypress, timer, blur, paste
'getSnapshotContent': getSnapshotContent
});
handleSmartContentChange(me);
console.debug('Aloha.Editable',
'smartContentChanged: event type keypress triggered');
}, this.sccDelay);
} else if (event && event.type === 'paste') {
Aloha.trigger('aloha-smart-content-changed', {
'editable': me,
'keyIdentifier': null,
'keyCode': null,
'char': null,
'triggerType': 'paste',
'getSnapshotContent': getSnapshotContent
});
handleSmartContentChange(me);
} else if (event && event.type === 'blur') {
Aloha.trigger('aloha-smart-content-changed', {
'editable': me,
'keyIdentifier': null,
'keyCode': null,
'char': null,
'triggerType': 'blur',
'getSnapshotContent': getSnapshotContent
});
handleSmartContentChange(me);
} else if (event && event.type === 'block-change') {
Aloha.trigger('aloha-smart-content-changed', {
'editable': me,
'keyIdentifier': null,
'keyCode': null,
'char': null,
'triggerType': 'block-change',
'getSnapshotContent': getSnapshotContent
});
handleSmartContentChange(me);
} else if (uniChar !== null) {
// in the rare case idle time is lower then delay time
clearTimeout(this.sccTimerDelay);
clearTimeout(this.sccTimerIdle);
this.sccTimerIdle = window.setTimeout(function () {
Aloha.trigger('aloha-smart-content-changed', {
'editable': me,
'keyIdentifier': null,
'keyCode': null,
'char': null,
'triggerType': 'idle',
'getSnapshotContent': getSnapshotContent
});
handleSmartContentChange(me);
}, this.sccIdle);
}
},
/**
* Get a snapshot of the active editable as a HTML string
* @hide
* @return snapshot of the editable
*/
getSnapshotContent: function () {
var ret = this.snapshotContent;
this.snapshotContent = this.getContents();
return ret;
}
});
/**
* Sets the content serializer function.
*
* The default content serializer will just call the jQuery.html()
* function on the editable element (which gets the innerHTML property).
*
* This method is a static class method and will affect the result
* of editable.getContents() for all editables that have been or
* will be constructed.
*
* @param {!Function} serializerFunction
* A function that accepts a DOM element and returns the serialized
* XHTML of the element contents (excluding the start and end tag of
* the passed element).
* @api
*/
Aloha.Editable.setContentSerializer = function (serializerFunction) {
contentSerializer = serializerFunction;
};
/**
* Gets the content serializer function.
*
* @see Aloha.Editable.setContentSerializer()
* @api
* @return {!Function}
* The serializer function.
*/
Aloha.Editable.getContentSerializer = function () {
return contentSerializer;
};
});