mirror of
https://github.com/UnickSoft/graphonline.git
synced 2026-04-23 16:36:38 +00:00
first commit
This commit is contained in:
59
lib/ckeditor4/plugins/lite/css/lite.css
Executable file
59
lib/ckeditor4/plugins/lite/css/lite.css
Executable file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
Copyright 2013 LoopIndex, This file is part of the Track Changes plugin for CKEditor.
|
||||
|
||||
The track changes plugin is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation.
|
||||
This program 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 as the file gpl-2.0.txt. If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
Written by (David *)Frenkiel - https://github.com/imdfl
|
||||
**/
|
||||
.ice-del {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ice-ins {
|
||||
white-space:pre-wrap;
|
||||
}
|
||||
|
||||
.ICE-Tracking .ice-ins,
|
||||
.ICE-Tracking .ice-del {
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
color: #000;
|
||||
padding: 1px 0 2px;
|
||||
}
|
||||
|
||||
.ICE-Tracking .ice-ins, .ICE-Tracking .ice-ins p {
|
||||
background-color: #e5ffcd !important;
|
||||
}
|
||||
|
||||
.ICE-Tracking .ice-ins.ice-cts-1, .ICE-Tracking .ice-ins.ice-cts-1 p {
|
||||
background-color: #e5ffcd !important;
|
||||
}
|
||||
|
||||
.ICE-Tracking .ice-ins.ice-cts-2, .ICE-Tracking .ice-ins.ice-cts-2 p {
|
||||
background-color: #e3ffff !important;
|
||||
}
|
||||
|
||||
.ICE-Tracking .ice-ins.ice-cts-3, .ICE-Tracking .ice-ins.ice-cts-3 p {
|
||||
background-color: #d1eeee !important;
|
||||
}
|
||||
|
||||
|
||||
.ICE-Tracking .ice-del {
|
||||
display: inline;
|
||||
text-decoration: line-through;
|
||||
color: #555;
|
||||
background-color: #faf1f1 !important;
|
||||
}
|
||||
|
||||
.ICE-Tracking .ice-del.ice-cts-1 {
|
||||
background-color: #faf1f1 !important;
|
||||
}
|
||||
|
||||
.ICE-Tracking .ice-del.ice-cts-2 {
|
||||
background-color: #ffe1e1 !important;
|
||||
}
|
||||
|
||||
.ICE-Tracking .ice-del.ice-cts-3 {
|
||||
background-color: #ffdddd !important;
|
||||
}
|
||||
BIN
lib/ckeditor4/plugins/lite/icons/accept_all.png
Executable file
BIN
lib/ckeditor4/plugins/lite/icons/accept_all.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
lib/ckeditor4/plugins/lite/icons/accept_one.png
Executable file
BIN
lib/ckeditor4/plugins/lite/icons/accept_one.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
BIN
lib/ckeditor4/plugins/lite/icons/reject_all.png
Executable file
BIN
lib/ckeditor4/plugins/lite/icons/reject_all.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
lib/ckeditor4/plugins/lite/icons/reject_one.png
Executable file
BIN
lib/ckeditor4/plugins/lite/icons/reject_one.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
BIN
lib/ckeditor4/plugins/lite/icons/show_hide.png
Executable file
BIN
lib/ckeditor4/plugins/lite/icons/show_hide.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
lib/ckeditor4/plugins/lite/icons/track_changes_on_off.png
Executable file
BIN
lib/ckeditor4/plugins/lite/icons/track_changes_on_off.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
15
lib/ckeditor4/plugins/lite/js/LICENSE
Executable file
15
lib/ckeditor4/plugins/lite/js/LICENSE
Executable file
@@ -0,0 +1,15 @@
|
||||
Copyright (c) The New York Times, CMS Group, Matthew DeLambo
|
||||
Modifications Copyright (c) Loopindex.com, (David *)Frenkiel
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2, as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program 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 as the file license.txt. If not, see
|
||||
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>
|
||||
53
lib/ckeditor4/plugins/lite/js/NOTICE
Executable file
53
lib/ckeditor4/plugins/lite/js/NOTICE
Executable file
@@ -0,0 +1,53 @@
|
||||
ice includes and was inspired by the following software:
|
||||
--------------------------------------------------------
|
||||
|
||||
Rangy
|
||||
-----
|
||||
|
||||
The MIT License
|
||||
Copyright (c) 2010 Tim Down
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Viper
|
||||
-----
|
||||
|
||||
Squiz Pty Ltd <products@squiz.net>
|
||||
2010 Squiz Pty Ltd (ACN 084 670 600)
|
||||
|
||||
Viper is a WYSIWYG that does not require the use of editable regions. Instead
|
||||
it utilises the browser's range API to edit the content directly in the browser.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2, as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program 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 as the file license.txt. If not, see
|
||||
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>
|
||||
|
||||
128
lib/ckeditor4/plugins/lite/js/README.md
Executable file
128
lib/ckeditor4/plugins/lite/js/README.md
Executable file
@@ -0,0 +1,128 @@
|
||||
# ice.js
|
||||
|
||||
Ice is a track changes implementation, built in javascript, for anything that is `contenteditable` on the web. Conceived by the CMS Group at The New York Times, ice has been piloting successfully for articles written in the newsroom.
|
||||
|
||||
## Demo
|
||||
|
||||
[Check it out!](http://NYTimes.github.com/ice/demo/)
|
||||
|
||||
## Download
|
||||
|
||||
[v0.4.2](http://nytimes.github.com/ice/downloads/ice_0.4.2.zip)
|
||||
|
||||
## Features
|
||||
|
||||
- Track multi-user inserts and deletes with the option to turn on and off tracking or highlighting.
|
||||
- A robust API to accept and reject changes, get clean content, and add a lot of configuration.
|
||||
- Plugins for tinymce and wordpress.
|
||||
- Optional plugins to track copy-cut-pasting, convert smart quotes, and create em-dashes.
|
||||
|
||||
## Get Started
|
||||
|
||||
***
|
||||
|
||||
**_Contenteditable initialization_** - If you are comfortable with maintaining your own text editing utilities, then you can initialize ice on any block element:
|
||||
```javascript
|
||||
var tracker = new ice.InlineChangeEditor({
|
||||
// element to track - ice will make it contenteditable
|
||||
element: document.getElementById('mytextelement'),
|
||||
// tell ice to setup/handle events on the `element`
|
||||
handleEvents: true,
|
||||
// set a user object to associate with each change
|
||||
currentUser: { id: 1, name: 'Miss T' }
|
||||
});
|
||||
// setup and start event handling for track changes
|
||||
tracker.startTracking();
|
||||
```
|
||||
Additional options:
|
||||
```javascript
|
||||
var tracker = new ice.InlineChangeEditor({
|
||||
element: document.getElementById('mytextelement'),
|
||||
handleEvents: true,
|
||||
currentUser: { id: 1, name: 'Miss T' },
|
||||
// optional plugins
|
||||
plugins: [
|
||||
// Add title attributes to changes for hover info
|
||||
'IceAddTitlePlugin',
|
||||
// Two successively typed dashes get converted into an em-dash
|
||||
'IceEmdashPlugin',
|
||||
// Track content that is cut and pasted
|
||||
{
|
||||
name: 'IceCopyPastePlugin',
|
||||
settings: {
|
||||
// List of tags and attributes to preserve when cleaning a paste
|
||||
preserve: 'p,a[href],span[id,class]em,strong'
|
||||
}
|
||||
}
|
||||
]
|
||||
}).startTracking();
|
||||
```
|
||||
***
|
||||
|
||||
**_Useful utilities in the API:_**
|
||||
|
||||
**acceptChange, rejectChange**
|
||||
```javascript
|
||||
// Accept/Reject the change at the current range/cursor position or at the given `optionalNode`
|
||||
tracker.acceptChange(optionalNode);
|
||||
tracker.rejectChange(optionalNode);
|
||||
```
|
||||
**acceptAll, rejectAll**
|
||||
```javascript
|
||||
// Accept/Reject all of the changes in the editable region.
|
||||
tracker.acceptAll();
|
||||
tracker.rejectAll();
|
||||
```
|
||||
**getCleanContent**
|
||||
```javascript
|
||||
// Returns a clean version, without tracking tags, of the content in the editable element or
|
||||
// out of the optional `body` param. After cleaning, the `optionalCallback` param is called
|
||||
// which should further modify and return the body.
|
||||
tracker.getCleanContent(optionalBody, optionalCallback);
|
||||
```
|
||||
**setCurrentUser**
|
||||
```javascript
|
||||
// Set the desired user to track. A user object has the following properties: { `id`, `name` }.
|
||||
tracker.setCurrentUser({id: 2, name: 'Miss T'});
|
||||
```
|
||||
**getChanges**
|
||||
```javascript
|
||||
// Get the internal list of change objects which are modeled from all of the change tracking
|
||||
// nodes in the DOM. This might be useful to add a more sophisticated change tracking UI/UX.
|
||||
// The list is key'ed with the unique change ids (`cid attribute`) and points to an object
|
||||
// with metadata for a change: [changeid] => {`type`, `time`, `userid`, `username`}
|
||||
var changes = tracker.getChanges();
|
||||
```
|
||||
***
|
||||
|
||||
**_Tinymce initialization_** - Add the ice plugin to your tinymce plugins directory and include the following in your tinymce init:
|
||||
```javascript
|
||||
tinymce.init({
|
||||
plugins: 'ice',
|
||||
theme_advanced_buttons1: 'ice_togglechanges,ice_toggleshowchanges,iceacceptall,icerejectall,iceaccept,icereject',
|
||||
ice: {
|
||||
user: { name: 'Miss T', id: 1},
|
||||
preserveOnPaste: 'p,a[href],i,em,strong',
|
||||
// Optional param - defaults to the css found in the plugin directory
|
||||
css: 'http://example.com/custom.css'
|
||||
},
|
||||
...
|
||||
});
|
||||
```
|
||||
***
|
||||
|
||||
**_Wordpress initialization_**
|
||||
|
||||
In testing - more to come soon.
|
||||
|
||||
***
|
||||
|
||||
## Limitations/Dependencies
|
||||
|
||||
- ice needs to be initialized after the DOM ready event fires.
|
||||
- ice was originally created for the simple markup behind nytimes.com articles (`p`, `a`, `em`, `strong`). As such, it requires that all text editing takes place in a common root block element, and that there are no other blocks found in the editor. Any type of inline elements are ok, inside of the common root blocks.
|
||||
- Unfortunately, we haven't been able to test this across all browsers and versions. We know that it tests well in modern Firefox (5+) and Webkit browsers, and "seems to work" in IE7+. We intend to do more testing and get a better idea about what ice can support across browsers.
|
||||
|
||||
## License
|
||||
|
||||
[GPL 2.0](https://github.com/NYTimes/ice/blob/master/LICENSE)
|
||||
166
lib/ckeditor4/plugins/lite/js/bookmark.js
Executable file
166
lib/ckeditor4/plugins/lite/js/bookmark.js
Executable file
@@ -0,0 +1,166 @@
|
||||
(function() {
|
||||
|
||||
var exports = this, Bookmark;
|
||||
|
||||
Bookmark = function(env, range, keepOldBookmarks) {
|
||||
|
||||
this.env = env;
|
||||
this.element = env.element;
|
||||
this.selection = this.env.selection;
|
||||
|
||||
// Remove all bookmarks?
|
||||
if (!keepOldBookmarks) {
|
||||
this.removeBookmarks(this.element);
|
||||
}
|
||||
|
||||
var currRange = range || this.selection.getRangeAt(0);
|
||||
range = currRange.cloneRange();
|
||||
var startContainer = range.startContainer;
|
||||
var endContainer = range.endContainer;
|
||||
var startOffset = range.startOffset;
|
||||
var endOffset = range.endOffset;
|
||||
var tmp;
|
||||
|
||||
// Collapse to the end of range.
|
||||
range.collapse(false);
|
||||
|
||||
var endBookmark = this.env.document.createElement('span');
|
||||
endBookmark.style.display = 'none';
|
||||
ice.dom.setHtml(endBookmark, ' ');
|
||||
ice.dom.addClass(endBookmark, 'iceBookmark iceBookmark_end');
|
||||
endBookmark.setAttribute('iceBookmark', 'end');
|
||||
range.insertNode(endBookmark);
|
||||
if(!ice.dom.isChildOf(endBookmark, this.element)) {
|
||||
this.element.appendChild(endBookmark);
|
||||
}
|
||||
|
||||
// Move the range to where it was before.
|
||||
range.setStart(startContainer, startOffset);
|
||||
range.collapse(true);
|
||||
|
||||
// Create the start bookmark.
|
||||
var startBookmark = this.env.document.createElement('span');
|
||||
startBookmark.style.display = 'none';
|
||||
ice.dom.addClass(startBookmark, 'iceBookmark iceBookmark_start');
|
||||
ice.dom.setHtml(startBookmark, ' ');
|
||||
startBookmark.setAttribute('iceBookmark', 'start');
|
||||
try {
|
||||
range.insertNode(startBookmark);
|
||||
|
||||
// Make sure start and end are in correct position.
|
||||
if (startBookmark.previousSibling === endBookmark) {
|
||||
// Reverse..
|
||||
tmp = startBookmark;
|
||||
startBookmark = endBookmark;
|
||||
endBookmark = tmp;
|
||||
}
|
||||
} catch (e) {
|
||||
// NS_ERROR_UNEXPECTED: I believe this is a Firefox bug.
|
||||
// It seems like if the range is collapsed and the text node is empty
|
||||
// (i.e. length = 0) then Firefox tries to split the node for no reason and fails...
|
||||
ice.dom.insertBefore(endBookmark, startBookmark);
|
||||
}
|
||||
|
||||
if (ice.dom.isChildOf(startBookmark, this.element) === false) {
|
||||
if (this.element.firstChild) {
|
||||
ice.dom.insertBefore(this.element.firstChild, startBookmark);
|
||||
} else {
|
||||
// Should not happen...
|
||||
this.element.appendChild(startBookmark);
|
||||
}
|
||||
}
|
||||
|
||||
if (!endBookmark.previousSibling) {
|
||||
tmp = this.env.document.createTextNode('');
|
||||
ice.dom.insertBefore(endBookmark, tmp);
|
||||
}
|
||||
|
||||
// The original range object must be changed.
|
||||
if (!startBookmark.nextSibling) {
|
||||
tmp = this.env.document.createTextNode('');
|
||||
ice.dom.insertAfter(startBookmark, tmp);
|
||||
}
|
||||
|
||||
currRange.setStart(startBookmark.nextSibling, 0);
|
||||
currRange.setEnd(endBookmark.previousSibling, (endBookmark.previousSibling.length || 0));
|
||||
|
||||
this.start = startBookmark;
|
||||
this.end = endBookmark;
|
||||
};
|
||||
|
||||
Bookmark.prototype = {
|
||||
|
||||
selectBookmark: function() {
|
||||
var range = this.selection.getRangeAt(0);
|
||||
var startPos = null;
|
||||
var endPos = null;
|
||||
var startOffset = 0;
|
||||
var endOffset = null;
|
||||
if (this.start.nextSibling === this.end || ice.dom.getElementsBetween(this.start, this.end).length === 0) {
|
||||
// Bookmark is collapsed.
|
||||
if (this.end.nextSibling) {
|
||||
startPos = ice.dom.getFirstChild(this.end.nextSibling);
|
||||
} else if (this.start.previousSibling) {
|
||||
startPos = ice.dom.getFirstChild(this.start.previousSibling);
|
||||
if (startPos.nodeType === ice.dom.TEXT_NODE) {
|
||||
startOffset = startPos.length;
|
||||
}
|
||||
} else {
|
||||
// Create a text node in parent.
|
||||
this.end.parentNode.appendChild(this.env.document.createTextNode(''));
|
||||
startPos = ice.dom.getFirstChild(this.end.nextSibling);
|
||||
}
|
||||
} else {
|
||||
if (this.start.nextSibling) {
|
||||
startPos = ice.dom.getFirstChild(this.start.nextSibling);
|
||||
} else {
|
||||
if (!this.start.previousSibling) {
|
||||
var tmp = this.env.document.createTextNode('');
|
||||
ice.dom.insertBefore(this.start, tmp);
|
||||
}
|
||||
|
||||
startPos = ice.dom.getLastChild(this.start.previousSibling);
|
||||
startOffset = startPos.length;
|
||||
}
|
||||
|
||||
if (this.end.previousSibling) {
|
||||
endPos = ice.dom.getLastChild(this.end.previousSibling);
|
||||
} else {
|
||||
endPos = ice.dom.getFirstChild(this.end.nextSibling || this.end);
|
||||
endOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ice.dom.remove([this.start, this.end]);
|
||||
|
||||
if (endPos === null) {
|
||||
range.setEnd(startPos, startOffset);
|
||||
range.collapse(false);
|
||||
} else {
|
||||
range.setStart(startPos, startOffset);
|
||||
if (endOffset === null) {
|
||||
endOffset = (endPos.length || 0);
|
||||
}
|
||||
range.setEnd(endPos, endOffset);
|
||||
}
|
||||
|
||||
try {
|
||||
this.selection.addRange(range);
|
||||
} catch (e) {
|
||||
// IE may throw exception for hidden elements..
|
||||
}
|
||||
},
|
||||
|
||||
getBookmark: function(parent, type) {
|
||||
var elem = ice.dom.getClass('iceBookmark_' + type, parent)[0];
|
||||
return elem;
|
||||
},
|
||||
|
||||
removeBookmarks: function(elem) {
|
||||
ice.dom.remove(ice.dom.getClass('iceBookmark', elem, 'span'));
|
||||
}
|
||||
};
|
||||
|
||||
exports.Bookmark = Bookmark;
|
||||
|
||||
}).call(this.ice);
|
||||
1035
lib/ckeditor4/plugins/lite/js/dom.js
Executable file
1035
lib/ckeditor4/plugins/lite/js/dom.js
Executable file
File diff suppressed because it is too large
Load Diff
1924
lib/ckeditor4/plugins/lite/js/ice.js
Executable file
1924
lib/ckeditor4/plugins/lite/js/ice.js
Executable file
File diff suppressed because it is too large
Load Diff
6
lib/ckeditor4/plugins/lite/js/ice.min.js
vendored
Executable file
6
lib/ckeditor4/plugins/lite/js/ice.min.js
vendored
Executable file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Load the ice dev files
|
||||
*/
|
||||
(function() {
|
||||
// Taken care of in the ice_on_mce.html page...
|
||||
})();
|
||||
6848
lib/ckeditor4/plugins/lite/js/iceIncludes.js
Executable file
6848
lib/ckeditor4/plugins/lite/js/iceIncludes.js
Executable file
File diff suppressed because it is too large
Load Diff
39
lib/ckeditor4/plugins/lite/js/icePlugin.js
Executable file
39
lib/ckeditor4/plugins/lite/js/icePlugin.js
Executable file
@@ -0,0 +1,39 @@
|
||||
(function() {
|
||||
|
||||
var exports = this;
|
||||
|
||||
var IcePlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
};
|
||||
|
||||
IcePlugin.prototype = {
|
||||
|
||||
start: function() {},
|
||||
clicked: function(e) {
|
||||
return true;
|
||||
},
|
||||
mouseDown: function(e) {
|
||||
return true;
|
||||
},
|
||||
keyDown: function(e) {
|
||||
return true;
|
||||
},
|
||||
keyPress: function(e) {
|
||||
return true;
|
||||
},
|
||||
selectionChanged: function(range) {},
|
||||
setEnabled: function(enabled) {},
|
||||
setDisabled: function(enabled) {},
|
||||
caretUpdated: function() {},
|
||||
nodeInserted: function(node, range) {},
|
||||
nodeCreated: function(node, options) {},
|
||||
caretPositioned: function() {},
|
||||
remove: function() {
|
||||
this._ice.removeKeyPressListener(this);
|
||||
},
|
||||
setSettings: function(settings) {}
|
||||
};
|
||||
|
||||
exports.IcePlugin = IcePlugin;
|
||||
|
||||
}).call(this.ice);
|
||||
336
lib/ckeditor4/plugins/lite/js/icePluginManager.js
Executable file
336
lib/ckeditor4/plugins/lite/js/icePluginManager.js
Executable file
@@ -0,0 +1,336 @@
|
||||
(function() {
|
||||
|
||||
var exports = this;
|
||||
|
||||
var IcePluginManager = function(ice_instance) {
|
||||
|
||||
this.plugins = {},
|
||||
this.pluginConstructors = {},
|
||||
this.keyPressListeners = {},
|
||||
this.activePlugin = null,
|
||||
this.pluginSets = {},
|
||||
this.activePluginSet = null,
|
||||
|
||||
this._ice = ice_instance;
|
||||
};
|
||||
|
||||
IcePluginManager.prototype = {
|
||||
|
||||
getPluginNames: function() {
|
||||
var plugins = [];
|
||||
for (var name in this.plugins) {
|
||||
plugins.push(name);
|
||||
}
|
||||
return plugins;
|
||||
},
|
||||
|
||||
addPluginObject: function(pluginName, pluginObj) {
|
||||
this.plugins[pluginName] = pluginObj;
|
||||
},
|
||||
|
||||
addPlugin: function(name, pluginConstructor) {
|
||||
if (typeof pluginConstructor !== 'function') {
|
||||
throw Error('IcePluginException: plugin must be a constructor function');
|
||||
}
|
||||
|
||||
if (ice.dom.isset(this.pluginConstructors[name]) === false) {
|
||||
this.pluginConstructors[name] = pluginConstructor;
|
||||
}
|
||||
},
|
||||
|
||||
loadPlugins: function(plugins, callback) {
|
||||
if (plugins.length === 0) {
|
||||
callback.call(this);
|
||||
} else {
|
||||
var plugin = plugins.shift();
|
||||
if (typeof plugin === 'object') {
|
||||
plugin = plugin.name;
|
||||
}
|
||||
|
||||
if (ice.dom.isset(ice._plugin[plugin]) === true) {
|
||||
this.addPlugin(plugin, ice._plugin[plugin]);
|
||||
this.loadPlugins(plugins, callback);
|
||||
} else {
|
||||
throw new Error('plugin was not included in the page: ' + plugin);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_enableSet: function(name) {
|
||||
this.activePluginSet = name;
|
||||
var pSetLen = this.pluginSets[name].length;
|
||||
for (var i = 0; i < pSetLen; i++) {
|
||||
var plugin = this.pluginSets[name][i];
|
||||
var pluginName = '';
|
||||
if (typeof plugin === 'object') {
|
||||
pluginName = plugin.name;
|
||||
} else {
|
||||
pluginName = plugin;
|
||||
}
|
||||
|
||||
var pluginConstructor = this.pluginConstructors[pluginName];
|
||||
if (pluginConstructor) {
|
||||
var pluginObj = new pluginConstructor(this._ice);
|
||||
this.plugins[pluginName] = pluginObj;
|
||||
|
||||
if (ice.dom.isset(plugin.settings) === true) {
|
||||
pluginObj.setSettings(plugin.settings);
|
||||
}
|
||||
|
||||
pluginObj.start();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setActivePlugin: function(name) {
|
||||
this.activePlugin = name;
|
||||
},
|
||||
|
||||
getActivePlugin: function() {
|
||||
return this.activePlugin;
|
||||
},
|
||||
|
||||
_getPluginName: function(pluginConstructor) {
|
||||
var fn = pluginConstructor.toString();
|
||||
var start = 'function '.length;
|
||||
var name = fn.substr(start, (fn.indexOf('(') - start));
|
||||
return name;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes specified plugin.
|
||||
*/
|
||||
removePlugin: function(plugin) {
|
||||
if (this.plugins[plugin]) {
|
||||
// Call the remove fn of the plugin incase it needs to do cleanup.
|
||||
this.plugins[plugin].remove();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the plugin object for specified plugin name.
|
||||
*/
|
||||
getPlugin: function(name) {
|
||||
return this.plugins[name];
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new set of plugins.
|
||||
*/
|
||||
usePlugins: function(name, plugins, callback) {
|
||||
var self = this;
|
||||
if (ice.dom.isset(plugins) === true) {
|
||||
this.pluginSets[name] = plugins;
|
||||
} else {
|
||||
this.pluginSets[name] = [];
|
||||
}
|
||||
var clone = this.pluginSets[name].concat([]);
|
||||
this.loadPlugins(clone, function() {
|
||||
self._enableSet(name);
|
||||
if(callback) callback.call(this);
|
||||
});
|
||||
},
|
||||
|
||||
disablePlugin: function(name) {
|
||||
this.plugins[name].disable();
|
||||
},
|
||||
|
||||
isPluginElement: function(element) {
|
||||
for (var i in this.plugins) {
|
||||
if (this.plugins[i].isPluginElement) {
|
||||
if (this.plugins[i].isPluginElement(element) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
fireKeyPressed: function(e) {
|
||||
if (this._fireKeyPressFns(e, 'all_keys') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var eKeys = [];
|
||||
if (e.ctrlKey === true || e.metaKey === true) {
|
||||
eKeys.push('ctrl');
|
||||
}
|
||||
|
||||
if (e.shiftKey === true) {
|
||||
eKeys.push('shift');
|
||||
}
|
||||
|
||||
if (e.altKey === true) {
|
||||
eKeys.push('alt');
|
||||
}
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 13:
|
||||
eKeys.push('enter');
|
||||
break;
|
||||
|
||||
case ice.dom.DOM_VK_LEFT:
|
||||
eKeys.push('left');
|
||||
break;
|
||||
|
||||
case ice.dom.DOM_VK_RIGHT:
|
||||
eKeys.push('right');
|
||||
break;
|
||||
|
||||
case ice.dom.DOM_VK_UP:
|
||||
eKeys.push('up');
|
||||
break;
|
||||
|
||||
case ice.dom.DOM_VK_DOWN:
|
||||
eKeys.push('down');
|
||||
break;
|
||||
|
||||
case 9:
|
||||
eKeys.push('tab');
|
||||
break;
|
||||
|
||||
case ice.dom.DOM_VK_DELETE:
|
||||
eKeys.push('delete');
|
||||
break;
|
||||
|
||||
default:
|
||||
var code;
|
||||
if (e.keyCode) {
|
||||
code = e.keyCode;
|
||||
} else if (e.which) {
|
||||
code = e.which;
|
||||
}
|
||||
|
||||
// Other characters (a-z0-9..).
|
||||
if (code) {
|
||||
eKeys.push(String.fromCharCode(code).toLowerCase());
|
||||
}
|
||||
break;
|
||||
}//end switch
|
||||
|
||||
var eKeysStr = eKeys.sort().join('+');
|
||||
|
||||
return this._fireKeyPressFns(e, eKeysStr);
|
||||
|
||||
},
|
||||
|
||||
_fireKeyPressFns: function(e, eKeysStr) {
|
||||
if (this.keyPressListeners[eKeysStr]) {
|
||||
var ln = this.keyPressListeners[eKeysStr].length;
|
||||
for (var i = 0; i < ln; i++) {
|
||||
var listener = this.keyPressListeners[eKeysStr][i];
|
||||
var eventFn = listener.fn;
|
||||
var plugin = listener.plugin;
|
||||
var data = listener.data;
|
||||
|
||||
if (eventFn) {
|
||||
if (ice.dom.isFn(eventFn) === true) {
|
||||
if (eventFn.call(plugin, e, data) === true) {
|
||||
ice.dom.preventDefault(e);
|
||||
return false;
|
||||
}
|
||||
} else if (plugin[eventFn] && plugin[eventFn].call(plugin, e, data) === true) {
|
||||
ice.dom.preventDefault(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
fireSelectionChanged: function(range) {
|
||||
for (var i in this.plugins) {
|
||||
this.plugins[i].selectionChanged(range);
|
||||
}
|
||||
},
|
||||
|
||||
fireNodeInserted: function(node, range) {
|
||||
for (var i in this.plugins) {
|
||||
if (this.plugins[i].nodeInserted(node, range) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fireNodeCreated: function(node, option) {
|
||||
for (var i in this.plugins) {
|
||||
if (this.plugins[i].nodeCreated(node, option) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fireCaretPositioned: function() {
|
||||
for (var i in this.plugins) {
|
||||
this.plugins[i].caretPositioned()
|
||||
}
|
||||
},
|
||||
|
||||
fireClicked: function(e) {
|
||||
var val = true;
|
||||
for (var i in this.plugins) {
|
||||
if (this.plugins[i].clicked(e) === false) {
|
||||
val = false;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
fireMouseDown: function(e) {
|
||||
var val = true;
|
||||
for (var i in this.plugins) {
|
||||
if (this.plugins[i].mouseDown(e) === false) {
|
||||
val = false;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
fireKeyDown: function(e) {
|
||||
var val = true;
|
||||
for (var i in this.plugins) {
|
||||
if (this.plugins[i].keyDown(e) === false) {
|
||||
val = false;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
fireKeyPress: function(e) {
|
||||
var val = true;
|
||||
for (var i in this.plugins) {
|
||||
if (this.plugins[i].keyPress(e) === false) {
|
||||
val = false;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
fireEnabled: function(enabled) {
|
||||
for (var i in this.plugins) {
|
||||
this.plugins[i].setEnabled(enabled);
|
||||
}
|
||||
},
|
||||
|
||||
fireDisabled: function(disabled) {
|
||||
for (var i in this.plugins) {
|
||||
this.plugins[i].setDisabled(disabled);
|
||||
}
|
||||
},
|
||||
|
||||
fireCaretUpdated: function() {
|
||||
for (var i in this.plugins) {
|
||||
if (this.plugins[i].caretUpdated) {
|
||||
this.plugins[i].caretUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports._plugin = {};
|
||||
exports.IcePluginManager = IcePluginManager;
|
||||
|
||||
}).call(this.ice);
|
||||
5
lib/ckeditor4/plugins/lite/js/jquery-1.9.1.min.js
vendored
Executable file
5
lib/ckeditor4/plugins/lite/js/jquery-1.9.1.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
498
lib/ckeditor4/plugins/lite/js/jquery-migrate-1.0.0.js
Executable file
498
lib/ckeditor4/plugins/lite/js/jquery-migrate-1.0.0.js
Executable file
@@ -0,0 +1,498 @@
|
||||
/*!
|
||||
* jQuery Migrate - v1.0.0 - 2013-01-14
|
||||
* https://github.com/jquery/jquery-migrate
|
||||
* Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT
|
||||
*/
|
||||
(function( jQuery, window, undefined ) {
|
||||
"use strict";
|
||||
|
||||
|
||||
var warnedAbout = {};
|
||||
|
||||
// List of warnings already given; public read only
|
||||
jQuery.migrateWarnings = [];
|
||||
|
||||
// Set to true to prevent console output; migrateWarnings still maintained
|
||||
// jQuery.migrateMute = false;
|
||||
|
||||
// Forget any warnings we've already given; public
|
||||
jQuery.migrateReset = function() {
|
||||
warnedAbout = {};
|
||||
jQuery.migrateWarnings.length = 0;
|
||||
};
|
||||
|
||||
function migrateWarn( msg) {
|
||||
if ( !warnedAbout[ msg ] ) {
|
||||
warnedAbout[ msg ] = true;
|
||||
jQuery.migrateWarnings.push( msg );
|
||||
if ( window.console && console.warn && !jQuery.migrateMute ) {
|
||||
console.warn( "JQMIGRATE: " + msg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function migrateWarnProp( obj, prop, value, msg ) {
|
||||
if ( Object.defineProperty ) {
|
||||
// On ES5 browsers (non-oldIE), warn if the code tries to get prop;
|
||||
// allow property to be overwritten in case some other plugin wants it
|
||||
try {
|
||||
Object.defineProperty( obj, prop, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
migrateWarn( msg );
|
||||
return value;
|
||||
},
|
||||
set: function( newValue ) {
|
||||
migrateWarn( msg );
|
||||
value = newValue;
|
||||
}
|
||||
});
|
||||
return;
|
||||
} catch( err ) {
|
||||
// IE8 is a dope about Object.defineProperty, can't warn there
|
||||
}
|
||||
}
|
||||
|
||||
// Non-ES5 (or broken) browser; just set the property
|
||||
jQuery._definePropertyBroken = true;
|
||||
obj[ prop ] = value;
|
||||
}
|
||||
|
||||
if ( document.compatMode === "BackCompat" ) {
|
||||
// jQuery has never supported or tested Quirks Mode
|
||||
migrateWarn( "jQuery is not compatible with Quirks Mode" );
|
||||
}
|
||||
|
||||
|
||||
var attrFn = {},
|
||||
attr = jQuery.attr,
|
||||
valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get ||
|
||||
function() { return null; },
|
||||
valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set ||
|
||||
function() { return undefined; },
|
||||
rnoType = /^(?:input|button)$/i,
|
||||
rnoAttrNodeType = /^[238]$/,
|
||||
rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
|
||||
ruseDefault = /^(?:checked|selected)$/i;
|
||||
|
||||
// jQuery.attrFn
|
||||
migrateWarnProp( jQuery, "attrFn", attrFn, "jQuery.attrFn is deprecated" );
|
||||
|
||||
jQuery.attr = function( elem, name, value, pass ) {
|
||||
var lowerName = name.toLowerCase(),
|
||||
nType = elem && elem.nodeType;
|
||||
|
||||
if ( pass ) {
|
||||
migrateWarn("jQuery.fn.attr( props, pass ) is deprecated");
|
||||
if ( elem && !rnoAttrNodeType.test( nType ) && jQuery.isFunction( jQuery.fn[ name ] ) ) {
|
||||
return jQuery( elem )[ name ]( value );
|
||||
}
|
||||
}
|
||||
|
||||
// Warn if user tries to set `type` since it breaks on IE 6/7/8
|
||||
if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) ) {
|
||||
migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8");
|
||||
}
|
||||
|
||||
// Restore boolHook for boolean property/attribute synchronization
|
||||
if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) {
|
||||
jQuery.attrHooks[ lowerName ] = {
|
||||
get: function( elem, name ) {
|
||||
// Align boolean attributes with corresponding properties
|
||||
// Fall back to attribute presence where some booleans are not supported
|
||||
var attrNode,
|
||||
property = jQuery.prop( elem, name );
|
||||
return property === true || typeof property !== "boolean" &&
|
||||
( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
|
||||
|
||||
name.toLowerCase() :
|
||||
undefined;
|
||||
},
|
||||
set: function( elem, value, name ) {
|
||||
var propName;
|
||||
if ( value === false ) {
|
||||
// Remove boolean attributes when set to false
|
||||
jQuery.removeAttr( elem, name );
|
||||
} else {
|
||||
// value is true since we know at this point it's type boolean and not false
|
||||
// Set boolean attributes to the same name and set the DOM property
|
||||
propName = jQuery.propFix[ name ] || name;
|
||||
if ( propName in elem ) {
|
||||
// Only set the IDL specifically if it already exists on the element
|
||||
elem[ propName ] = true;
|
||||
}
|
||||
|
||||
elem.setAttribute( name, name.toLowerCase() );
|
||||
}
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
// Warn only for attributes that can remain distinct from their properties post-1.9
|
||||
if ( ruseDefault.test( lowerName ) ) {
|
||||
migrateWarn( "jQuery.fn.attr(" + lowerName + ") may use property instead of attribute" );
|
||||
}
|
||||
}
|
||||
|
||||
return attr.call( jQuery, elem, name, value );
|
||||
};
|
||||
|
||||
// attrHooks: value
|
||||
jQuery.attrHooks.value = {
|
||||
get: function( elem, name ) {
|
||||
var nodeName = ( elem.nodeName || "" ).toLowerCase();
|
||||
if ( nodeName === "button" ) {
|
||||
return valueAttrGet.apply( this, arguments );
|
||||
}
|
||||
if ( nodeName !== "input" && nodeName !== "option" ) {
|
||||
migrateWarn("property-based jQuery.fn.attr('value') is deprecated");
|
||||
}
|
||||
return name in elem ?
|
||||
elem.value :
|
||||
null;
|
||||
},
|
||||
set: function( elem, value ) {
|
||||
var nodeName = ( elem.nodeName || "" ).toLowerCase();
|
||||
if ( nodeName === "button" ) {
|
||||
return valueAttrSet.apply( this, arguments );
|
||||
}
|
||||
if ( nodeName !== "input" && nodeName !== "option" ) {
|
||||
migrateWarn("property-based jQuery.fn.attr('value', val) is deprecated");
|
||||
}
|
||||
// Does not return so that setAttribute is also used
|
||||
elem.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var matched, browser,
|
||||
oldInit = jQuery.fn.init,
|
||||
// Note this does NOT include the # XSS fix from 1.7!
|
||||
rquickExpr = /^(?:.*(<[\w\W]+>)[^>]*|#([\w\-]*))$/;
|
||||
|
||||
// $(html) "looks like html" rule change
|
||||
jQuery.fn.init = function( selector, context, rootjQuery ) {
|
||||
var match;
|
||||
|
||||
if ( selector && typeof selector === "string" && !jQuery.isPlainObject( context ) &&
|
||||
(match = rquickExpr.exec( selector )) && match[1] ) {
|
||||
// This is an HTML string according to the "old" rules; is it still?
|
||||
if ( selector.charAt( 0 ) !== "<" ) {
|
||||
migrateWarn("$(html) HTML strings must start with '<' character");
|
||||
}
|
||||
// Now process using loose rules; let pre-1.8 play too
|
||||
if ( context && context.context ) {
|
||||
// jQuery object as context; parseHTML expects a DOM object
|
||||
context = context.context;
|
||||
}
|
||||
if ( jQuery.parseHTML ) {
|
||||
return oldInit.call( this, jQuery.parseHTML( jQuery.trim(selector), context, true ),
|
||||
context, rootjQuery );
|
||||
}
|
||||
}
|
||||
return oldInit.apply( this, arguments );
|
||||
};
|
||||
jQuery.fn.init.prototype = jQuery.fn;
|
||||
|
||||
jQuery.uaMatch = function( ua ) {
|
||||
ua = ua.toLowerCase();
|
||||
|
||||
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
|
||||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
|
||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
|
||||
/(msie) ([\w.]+)/.exec( ua ) ||
|
||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
|
||||
[];
|
||||
|
||||
return {
|
||||
browser: match[ 1 ] || "",
|
||||
version: match[ 2 ] || "0"
|
||||
};
|
||||
};
|
||||
|
||||
matched = jQuery.uaMatch( navigator.userAgent );
|
||||
browser = {};
|
||||
|
||||
if ( matched.browser ) {
|
||||
browser[ matched.browser ] = true;
|
||||
browser.version = matched.version;
|
||||
}
|
||||
|
||||
// Chrome is Webkit, but Webkit is also Safari.
|
||||
if ( browser.chrome ) {
|
||||
browser.webkit = true;
|
||||
} else if ( browser.webkit ) {
|
||||
browser.safari = true;
|
||||
}
|
||||
|
||||
jQuery.browser = browser;
|
||||
|
||||
// Warn if the code tries to get jQuery.browser
|
||||
migrateWarnProp( jQuery, "browser", browser, "jQuery.browser is deprecated" );
|
||||
|
||||
jQuery.sub = function() {
|
||||
function jQuerySub( selector, context ) {
|
||||
return new jQuerySub.fn.init( selector, context );
|
||||
}
|
||||
jQuery.extend( true, jQuerySub, this );
|
||||
jQuerySub.superclass = this;
|
||||
jQuerySub.fn = jQuerySub.prototype = this();
|
||||
jQuerySub.fn.constructor = jQuerySub;
|
||||
jQuerySub.sub = this.sub;
|
||||
jQuerySub.fn.init = function init( selector, context ) {
|
||||
if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
|
||||
context = jQuerySub( context );
|
||||
}
|
||||
|
||||
return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
|
||||
};
|
||||
jQuerySub.fn.init.prototype = jQuerySub.fn;
|
||||
var rootjQuerySub = jQuerySub(document);
|
||||
migrateWarn( "jQuery.sub() is deprecated" );
|
||||
return jQuerySub;
|
||||
};
|
||||
|
||||
|
||||
var oldFnData = jQuery.fn.data;
|
||||
|
||||
jQuery.fn.data = function( name ) {
|
||||
var ret, evt,
|
||||
elem = this[0];
|
||||
|
||||
// Handles 1.7 which has this behavior and 1.8 which doesn't
|
||||
if ( elem && name === "events" && arguments.length === 1 ) {
|
||||
ret = jQuery.data( elem, name );
|
||||
evt = jQuery._data( elem, name );
|
||||
if ( ( ret === undefined || ret === evt ) && evt !== undefined ) {
|
||||
migrateWarn("Use of jQuery.fn.data('events') is deprecated");
|
||||
return evt;
|
||||
}
|
||||
}
|
||||
return oldFnData.apply( this, arguments );
|
||||
};
|
||||
|
||||
|
||||
var rscriptType = /\/(java|ecma)script/i,
|
||||
oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack,
|
||||
oldFragment = jQuery.buildFragment;
|
||||
|
||||
jQuery.fn.andSelf = function() {
|
||||
migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()");
|
||||
return oldSelf.apply( this, arguments );
|
||||
};
|
||||
|
||||
// Since jQuery.clean is used internally on older versions, we only shim if it's missing
|
||||
if ( !jQuery.clean ) {
|
||||
jQuery.clean = function( elems, context, fragment, scripts ) {
|
||||
// Set context per 1.8 logic
|
||||
context = context || document;
|
||||
context = !context.nodeType && context[0] || context;
|
||||
context = context.ownerDocument || context;
|
||||
|
||||
migrateWarn("jQuery.clean() is deprecated");
|
||||
|
||||
var i, elem, handleScript, jsTags,
|
||||
ret = [];
|
||||
|
||||
jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes );
|
||||
|
||||
// Complex logic lifted directly from jQuery 1.8
|
||||
if ( fragment ) {
|
||||
// Special handling of each script element
|
||||
handleScript = function( elem ) {
|
||||
// Check if we consider it executable
|
||||
if ( !elem.type || rscriptType.test( elem.type ) ) {
|
||||
// Detach the script and store it in the scripts array (if provided) or the fragment
|
||||
// Return truthy to indicate that it has been handled
|
||||
return scripts ?
|
||||
scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
|
||||
fragment.appendChild( elem );
|
||||
}
|
||||
};
|
||||
|
||||
for ( i = 0; (elem = ret[i]) != null; i++ ) {
|
||||
// Check if we're done after handling an executable script
|
||||
if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
|
||||
// Append to fragment and handle embedded scripts
|
||||
fragment.appendChild( elem );
|
||||
if ( typeof elem.getElementsByTagName !== "undefined" ) {
|
||||
// handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
|
||||
jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
|
||||
|
||||
// Splice the scripts into ret after their former ancestor and advance our index beyond them
|
||||
ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
|
||||
i += jsTags.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
jQuery.buildFragment = function( elems, context, scripts, selection ) {
|
||||
var ret,
|
||||
warning = "jQuery.buildFragment() is deprecated";
|
||||
|
||||
// Set context per 1.8 logic
|
||||
context = context || document;
|
||||
context = !context.nodeType && context[0] || context;
|
||||
context = context.ownerDocument || context;
|
||||
|
||||
try {
|
||||
ret = oldFragment.call( jQuery, elems, context, scripts, selection );
|
||||
|
||||
// jQuery < 1.8 required arrayish context; jQuery 1.9 fails on it
|
||||
} catch( x ) {
|
||||
ret = oldFragment.call( jQuery, elems, context.nodeType ? [ context ] : context[ 0 ], scripts, selection );
|
||||
|
||||
// Success from tweaking context means buildFragment was called by the user
|
||||
migrateWarn( warning );
|
||||
}
|
||||
|
||||
// jQuery < 1.9 returned an object instead of the fragment itself
|
||||
if ( !ret.fragment ) {
|
||||
migrateWarnProp( ret, "fragment", ret, warning );
|
||||
migrateWarnProp( ret, "cacheable", false, warning );
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
var eventAdd = jQuery.event.add,
|
||||
eventRemove = jQuery.event.remove,
|
||||
eventTrigger = jQuery.event.trigger,
|
||||
oldToggle = jQuery.fn.toggle,
|
||||
oldLive = jQuery.fn.live,
|
||||
oldDie = jQuery.fn.die,
|
||||
ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",
|
||||
rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ),
|
||||
rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
|
||||
hoverHack = function( events ) {
|
||||
if ( typeof( events ) != "string" || jQuery.event.special.hover ) {
|
||||
return events;
|
||||
}
|
||||
if ( rhoverHack.test( events ) ) {
|
||||
migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'");
|
||||
}
|
||||
return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
|
||||
};
|
||||
|
||||
// Event props removed in 1.9, put them back if needed; no practical way to warn them
|
||||
if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) {
|
||||
jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" );
|
||||
}
|
||||
|
||||
// Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7
|
||||
migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated" );
|
||||
|
||||
// Support for 'hover' pseudo-event and ajax event warnings
|
||||
jQuery.event.add = function( elem, types, handler, data, selector ){
|
||||
if ( elem !== document && rajaxEvent.test( types ) ) {
|
||||
migrateWarn( "AJAX events should be attached to document: " + types );
|
||||
}
|
||||
eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector );
|
||||
};
|
||||
jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){
|
||||
eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes );
|
||||
};
|
||||
|
||||
jQuery.fn.error = function() {
|
||||
var args = Array.prototype.slice.call( arguments, 0);
|
||||
migrateWarn("jQuery.fn.error() is deprecated");
|
||||
args.splice( 0, 0, "error" );
|
||||
if ( arguments.length ) {
|
||||
return this.bind.apply( this, args );
|
||||
}
|
||||
// error event should not bubble to window, although it does pre-1.7
|
||||
this.triggerHandler.apply( this, args );
|
||||
return this;
|
||||
};
|
||||
|
||||
jQuery.fn.toggle = function( fn, fn2 ) {
|
||||
|
||||
// Don't mess with animation or css toggles
|
||||
if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) {
|
||||
return oldToggle.apply( this, arguments );
|
||||
}
|
||||
migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated");
|
||||
|
||||
// Save reference to arguments for access in closure
|
||||
var args = arguments,
|
||||
guid = fn.guid || jQuery.guid++,
|
||||
i = 0,
|
||||
toggler = function( event ) {
|
||||
// Figure out which function to execute
|
||||
var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
|
||||
jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
|
||||
|
||||
// Make sure that clicks stop
|
||||
event.preventDefault();
|
||||
|
||||
// and execute the function
|
||||
return args[ lastToggle ].apply( this, arguments ) || false;
|
||||
};
|
||||
|
||||
// link all the functions, so any of them can unbind this click handler
|
||||
toggler.guid = guid;
|
||||
while ( i < args.length ) {
|
||||
args[ i++ ].guid = guid;
|
||||
}
|
||||
|
||||
return this.click( toggler );
|
||||
};
|
||||
|
||||
jQuery.fn.live = function( types, data, fn ) {
|
||||
migrateWarn("jQuery.fn.live() is deprecated");
|
||||
if ( oldLive ) {
|
||||
return oldLive.apply( this, arguments );
|
||||
}
|
||||
jQuery( this.context ).on( types, this.selector, data, fn );
|
||||
return this;
|
||||
};
|
||||
|
||||
jQuery.fn.die = function( types, fn ) {
|
||||
migrateWarn("jQuery.fn.die() is deprecated");
|
||||
if ( oldDie ) {
|
||||
return oldDie.apply( this, arguments );
|
||||
}
|
||||
jQuery( this.context ).off( types, this.selector || "**", fn );
|
||||
return this;
|
||||
};
|
||||
|
||||
// Turn global events into document-triggered events
|
||||
jQuery.event.trigger = function( event, data, elem, onlyHandlers ){
|
||||
if ( !elem & !rajaxEvent.test( event ) ) {
|
||||
migrateWarn( "Global events are undocumented and deprecated" );
|
||||
}
|
||||
return eventTrigger.call( this, event, data, elem || document, onlyHandlers );
|
||||
};
|
||||
jQuery.each( ajaxEvents.split("|"),
|
||||
function( _, name ) {
|
||||
jQuery.event.special[ name ] = {
|
||||
setup: function() {
|
||||
var elem = this;
|
||||
|
||||
// The document needs no shimming; must be !== for oldIE
|
||||
if ( elem !== document ) {
|
||||
jQuery.event.add( document, name + "." + jQuery.guid, function() {
|
||||
jQuery.event.trigger( name, null, elem, true );
|
||||
});
|
||||
jQuery._data( this, name, jQuery.guid++ );
|
||||
}
|
||||
return false;
|
||||
},
|
||||
teardown: function() {
|
||||
if ( this !== document ) {
|
||||
jQuery.event.remove( document, name + "." + jQuery._data( this, name ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
})( jQuery, window );
|
||||
5
lib/ckeditor4/plugins/lite/js/jquery.min.js
vendored
Executable file
5
lib/ckeditor4/plugins/lite/js/jquery.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
19
lib/ckeditor4/plugins/lite/js/plugins/IceAddTitlePlugin/IceAddTitlePlugin.js
Executable file
19
lib/ckeditor4/plugins/lite/js/plugins/IceAddTitlePlugin/IceAddTitlePlugin.js
Executable file
@@ -0,0 +1,19 @@
|
||||
(function() {
|
||||
|
||||
var exports = this, IceAddTitlePlugin;
|
||||
|
||||
IceAddTitlePlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
};
|
||||
|
||||
IceAddTitlePlugin.prototype = {
|
||||
nodeCreated: function(node, option) {
|
||||
node.setAttribute('title', (option.action || 'Modified') + ' by ' + node.getAttribute(this._ice.userNameAttribute)
|
||||
+ ' - ' + ice.dom.date('m/d/Y h:ia', parseInt(node.getAttribute(this._ice.timeAttribute))));
|
||||
}
|
||||
};
|
||||
|
||||
ice.dom.noInclusionInherits(IceAddTitlePlugin, ice.IcePlugin);
|
||||
exports._plugin.IceAddTitlePlugin = IceAddTitlePlugin;
|
||||
|
||||
}).call(this.ice);
|
||||
510
lib/ckeditor4/plugins/lite/js/plugins/IceCopyPastePlugin/IceCopyPastePlugin.js
Executable file
510
lib/ckeditor4/plugins/lite/js/plugins/IceCopyPastePlugin/IceCopyPastePlugin.js
Executable file
@@ -0,0 +1,510 @@
|
||||
(function() {
|
||||
|
||||
var exports = this, IceCopyPastePlugin;
|
||||
|
||||
IceCopyPastePlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
this._tmpNode = null;
|
||||
this._tmpNodeTagName = 'icepaste';
|
||||
this._pasteId = 'icepastediv';
|
||||
var self = this;
|
||||
|
||||
// API
|
||||
|
||||
// 'formatted' - paste will be MS Word cleaned.
|
||||
// 'formattedClean' - paste will be MS Word cleaned, insert and
|
||||
// delete tags will be removed keeping insert content in place,
|
||||
// and tags not found in `preserve` will be stripped.
|
||||
this.pasteType = 'formattedClean';
|
||||
|
||||
// Subset of tags that will not be stripped when pasteType
|
||||
// is set to 'formattedClean'. Parameter is of type string with
|
||||
// comma delimited tag and attribute definitions. For example:
|
||||
// 'p,a[href],i[style|title],span[*]'
|
||||
// Would allow `p`, `a`, `i` and `span` tags. The attributes for
|
||||
// each one of these tags would be cleaned as follows: `p` tags
|
||||
// would have all attributes removed, `a` tags will have all but
|
||||
// `href` attributes removed, `i` tags will have all but `style`
|
||||
// and `title` attributes removed, and `span` tags will keep all attributes.
|
||||
this.preserve = 'p';
|
||||
|
||||
// Callback triggered before any paste cleaning happens
|
||||
this.beforePasteClean = function(body) { return body; };
|
||||
|
||||
// Callback triggered at the end of the paste cleaning
|
||||
this.afterPasteClean = function(body) { return body; };
|
||||
|
||||
// Event Listeners
|
||||
ice_instance.element.oncopy = function() { return self.handleCopy.apply(self); };
|
||||
// ice_instance.element.oncut = function() { return self.handleCut.apply(self); };
|
||||
|
||||
// We can't listen for `onpaste` unless we use an algorithm that temporarily switches
|
||||
// out the body and lets the browser paste there (found it hard to maintain in mce).
|
||||
// Instead, we'll watch the keydown event and handle paste with a typical temp element
|
||||
// algorithm, which means that pasting from the context menu won't work.
|
||||
};
|
||||
|
||||
IceCopyPastePlugin.prototype = {
|
||||
|
||||
setSettings: function(settings) {
|
||||
settings = settings || {};
|
||||
ice.dom.extend(this, settings);
|
||||
|
||||
this.preserve += ',' + this._tmpNodeTagName;
|
||||
this.setupPreserved();
|
||||
},
|
||||
|
||||
keyDown: function(e) {
|
||||
if (e.metaKey !== true && e.ctrlKey !== true) {
|
||||
return;
|
||||
}
|
||||
if(e.keyCode == 86) {
|
||||
this.handlePaste();
|
||||
}
|
||||
else if(e.keyCode == 88) {
|
||||
this.handleCut();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
keyPress: function(e){
|
||||
var c = null;
|
||||
if (e.which == null) {
|
||||
// IE.
|
||||
c = String.fromCharCode(e.keyCode);
|
||||
} else if (e.which > 0) {
|
||||
c = String.fromCharCode(e.which);
|
||||
}
|
||||
var self = this;
|
||||
if(this.cutElement && c === 'x'){
|
||||
if(ice.dom.isBrowser("webkit")){
|
||||
self.cutElement.focus();
|
||||
}
|
||||
} else if (c === 'v'){
|
||||
if(ice.dom.isBrowser("webkit")){
|
||||
var div = document.getElementById(self._pasteId);
|
||||
div.focus();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
handleCopy: function(e) {},
|
||||
|
||||
// Inserts a temporary placeholder for the current range and removes
|
||||
// the contents of the ice element body and calls a paste handler.
|
||||
handlePaste: function(e) {
|
||||
|
||||
var range = this._ice.getCurrentRange();
|
||||
|
||||
if(!range.collapsed) {
|
||||
if(this._ice.isTracking) {
|
||||
this._ice.deleteContents();
|
||||
range = range.cloneRange();
|
||||
} else {
|
||||
range.deleteContents();
|
||||
range.collapse(true);
|
||||
}
|
||||
}
|
||||
|
||||
if(this._ice.isTracking)
|
||||
this._ice._moveRangeToValidTrackingPos(range);
|
||||
|
||||
if(range.startContainer == this._ice.element) {
|
||||
// Fix a potentially empty body with a bad selection
|
||||
var firstBlock = ice.dom.find(this._ice.element, this._ice.blockEl)[0];
|
||||
if(!firstBlock) {
|
||||
firstBlock = ice.dom.create('<' + this._ice.blockEl + ' ><br/></' + this._ice.blockEl + '>');
|
||||
this._ice.element.appendChild(firstBlock);
|
||||
}
|
||||
range.setStart(firstBlock, 0);
|
||||
range.collapse(true);
|
||||
this._ice.env.selection.addRange(range);
|
||||
}
|
||||
|
||||
|
||||
this._tmpNode = this._ice.env.document.createElement(this._tmpNodeTagName);
|
||||
range.insertNode(this._tmpNode);
|
||||
|
||||
switch (this.pasteType) {
|
||||
case 'formatted':
|
||||
this.setupPaste();
|
||||
break;
|
||||
case 'formattedClean':
|
||||
this.setupPaste(true);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// Create a temporary div and set focus to it so that the browser can paste into it.
|
||||
// Set a timeout to push a paste handler on to the end of the execution stack.
|
||||
setupPaste: function(stripTags) {
|
||||
var div = this.createDiv(this._pasteId), self = this;
|
||||
|
||||
div.onpaste = function() {
|
||||
setTimeout(function(){
|
||||
self.handlePasteValue(stripTags);
|
||||
},0);
|
||||
};
|
||||
|
||||
if(this._ice.env.frame){
|
||||
if(ice.dom.isBrowser("webkit")){
|
||||
div.blur();
|
||||
setTimeout(function(){
|
||||
div.focus();
|
||||
}, 0);
|
||||
} else {
|
||||
div.focus();
|
||||
}
|
||||
}
|
||||
else{
|
||||
div.focus();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// By the time we get here, the pasted content will already be in the body. Extract the
|
||||
// paste, format it, remove any Microsoft or extraneous tags outside of `this.preserve`
|
||||
// and merge the pasted content into the original fragment body.
|
||||
handlePasteValue: function(stripTags) {
|
||||
// Get the pasted content.
|
||||
var html = ice.dom.getHtml(document.getElementById(this._pasteId));
|
||||
var childBlocks = ice.dom.children('<div>' + html + '</div>', this._ice.blockEl);
|
||||
if(childBlocks.length === 1 && ice.dom.getNodeTextContent('<div>' + html + '</div>') === ice.dom.getNodeTextContent(childBlocks)) {
|
||||
html = ice.dom.getHtml(html);
|
||||
}
|
||||
|
||||
html = this.beforePasteClean.call(this, html);
|
||||
|
||||
if(stripTags) {
|
||||
|
||||
// Strip out change tracking tags.
|
||||
html = this._ice.getCleanContent(html);
|
||||
html = this.stripPaste(html);
|
||||
}
|
||||
|
||||
html = this.afterPasteClean.call(this, html);
|
||||
html = ice.dom.trim(html);
|
||||
var range = this._ice.getCurrentRange();
|
||||
range.setStartAfter(this._tmpNode);
|
||||
range.collapse(true);
|
||||
|
||||
var innerBlock = null, lastEl = null, newEl = null;
|
||||
var fragment = range.createContextualFragment(html);
|
||||
var changeid = this._ice.startBatchChange();
|
||||
|
||||
// If fragment contains block level elements, most likely we will need to
|
||||
// do some splitting so we do not have P tags in P tags, etc. Split the
|
||||
// container from current selection and then insert paste contents after it.
|
||||
if(ice.dom.hasBlockChildren(fragment)) {
|
||||
// Split from current selection.
|
||||
var block = ice.dom.isChildOfTagName(this._tmpNode, this._ice.blockEl);
|
||||
range.setEndAfter(block.lastChild);
|
||||
this._ice.selection.addRange(range);
|
||||
var contents = range.extractContents();
|
||||
var newblock = this._ice.env.document.createElement(this._ice.blockEl);
|
||||
newblock.appendChild(contents);
|
||||
ice.dom.insertAfter(block, newblock);
|
||||
|
||||
range.setStart(newblock, 0);
|
||||
range.collapse(true);
|
||||
this._ice.selection.addRange(range);
|
||||
var prevBlock = range.startContainer;
|
||||
|
||||
// Paste all of the children in the fragment.
|
||||
while(fragment.firstChild) {
|
||||
if(fragment.firstChild.nodeType === 3 && !jQuery.trim(fragment.firstChild.nodeValue)) {
|
||||
fragment.removeChild(fragment.firstChild);
|
||||
continue;
|
||||
}
|
||||
// We may have blocks with text nodes at the beginning or end. For example, this paste:
|
||||
// textnode <p>blocktext</p> <p>blocktext</p> moretext
|
||||
// In which case we wrap the leading or trailing text nodes in blocks.
|
||||
if(ice.dom.isBlockElement(fragment.firstChild)) {
|
||||
if(fragment.firstChild.textContent !== "") {
|
||||
innerBlock = null;
|
||||
var insert = null;
|
||||
if(this._ice.isTracking) {
|
||||
insert = this._ice.createIceNode('insertType');
|
||||
this._ice.addChange('insertType', [insert]);
|
||||
newEl = document.createElement(fragment.firstChild.tagName);
|
||||
insert.innerHTML = fragment.firstChild.innerHTML;
|
||||
newEl.appendChild(insert);
|
||||
} else {
|
||||
insert = newEl = document.createElement(fragment.firstChild.tagName);
|
||||
newEl.innerHTML = fragment.firstChild.innerHTML;
|
||||
}
|
||||
lastEl = insert;
|
||||
ice.dom.insertBefore(prevBlock, newEl);
|
||||
}
|
||||
fragment.removeChild(fragment.firstChild);
|
||||
} else {
|
||||
if(!innerBlock) {
|
||||
// Create a new block and append an insert
|
||||
newEl = document.createElement(this._ice.blockEl);
|
||||
ice.dom.insertBefore(prevBlock, newEl);
|
||||
if(this._ice.isTracking) {
|
||||
innerBlock = this._ice.createIceNode('insertType');
|
||||
this._ice.addChange('insertType', [innerBlock]);
|
||||
newEl.appendChild(innerBlock);
|
||||
} else {
|
||||
innerBlock = newEl;
|
||||
}
|
||||
}
|
||||
lastEl = innerBlock;
|
||||
innerBlock.appendChild(fragment.removeChild(fragment.firstChild));
|
||||
}
|
||||
}
|
||||
if (!newblock.textContent) {
|
||||
newblock.parentNode.removeChild(newblock);
|
||||
}
|
||||
|
||||
} else {
|
||||
if(this._ice.isTracking) {
|
||||
newEl = this._ice.createIceNode('insertType', fragment);
|
||||
this._ice.addChange('insertType', [newEl]);
|
||||
range.insertNode(newEl);
|
||||
lastEl = newEl;
|
||||
} else {
|
||||
var child;
|
||||
while((child = fragment.firstChild)) {
|
||||
range.insertNode(child);
|
||||
range.setStartAfter(child);
|
||||
range.collapse(true);
|
||||
lastEl = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._ice.endBatchChange(changeid);
|
||||
this._cleanup(lastEl);
|
||||
},
|
||||
|
||||
|
||||
createDiv: function(id) {
|
||||
var oldEl = ice.dom.getId(id);
|
||||
if(oldEl) {
|
||||
ice.dom.remove(oldEl);
|
||||
}
|
||||
|
||||
var div = this._ice.env.document.createElement('div');
|
||||
div.id = id;
|
||||
div.setAttribute('contentEditable', true);
|
||||
ice.dom.setStyle(div, 'width', '1px');
|
||||
ice.dom.setStyle(div, 'height', '1px');
|
||||
ice.dom.setStyle(div, 'overflow', 'hidden');
|
||||
ice.dom.setStyle(div, 'position', 'fixed');
|
||||
ice.dom.setStyle(div, 'top', '10px');
|
||||
ice.dom.setStyle(div, 'left', '10px');
|
||||
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
},
|
||||
|
||||
// Intercepts cut operation and handles by creating an editable div, copying the current selection
|
||||
// into it, deleting the current selection with track changes, and selecting the contents in the
|
||||
// editable div.
|
||||
handleCut: function() {
|
||||
this.cutElementId = 'icecut';
|
||||
this.cutElement = this.createDiv(this.cutElementId);
|
||||
var range = this._ice.getCurrentRange();
|
||||
if(range.collapsed) return;
|
||||
var html = range.getHTMLContents();
|
||||
if (this._ice.isTracking) this._ice.deleteContents();
|
||||
else range.deleteContents();
|
||||
// var crange = range.cloneRange();
|
||||
// var crange = rangy.createRange();
|
||||
var crange = document.createRange();
|
||||
// crange.collapse(true);
|
||||
this.cutElement.innerHTML = html;
|
||||
|
||||
crange.setStart(this.cutElement.firstChild, 0);
|
||||
crange.setEndAfter(this.cutElement.lastChild);
|
||||
var self = this;
|
||||
|
||||
// this.cutElement.blur();
|
||||
if(this._ice.env.frame){
|
||||
// TINYMCE
|
||||
setTimeout(function(){
|
||||
self.cutElement.focus();
|
||||
|
||||
// After the browser cuts out of the `cutElement`, reset the range and remove the cut element.
|
||||
setTimeout(function() {
|
||||
ice.dom.remove(self.cutElement);
|
||||
range.setStart(range.startContainer, range.startOffset);
|
||||
range.collapse(false);
|
||||
self._ice.env.selection.addRange(range);
|
||||
// Set focus back to ice element.
|
||||
if(self._ice.env.frame) {
|
||||
self._ice.env.frame.contentWindow.focus();
|
||||
} else {
|
||||
self._ice.element.focus();
|
||||
}
|
||||
}, 100);
|
||||
}, 0);
|
||||
} else {
|
||||
// Vanilla Div
|
||||
setTimeout(function(){
|
||||
self.cutElement.focus();
|
||||
|
||||
// After the browser cuts out of the `cutElement`, reset the range and remove the cut element.
|
||||
setTimeout(function() {
|
||||
range.setStart(range.startContainer, range.startOffset);
|
||||
range.collapse(false);
|
||||
self._ice.env.selection.addRange(range);
|
||||
// Set focus back to ice element.
|
||||
if(self._ice.env.frame) {
|
||||
self._ice.env.frame.contentWindow.focus();
|
||||
} else {
|
||||
self._ice.element.focus();
|
||||
}
|
||||
}, 100);
|
||||
}, 0);
|
||||
|
||||
if(ice.dom.getWebkitType() === "chrome"){
|
||||
self.cutElement.focus();
|
||||
}
|
||||
}
|
||||
self._ice.env.selection.addRange(crange);
|
||||
},
|
||||
|
||||
|
||||
// Strips ice change tracking tags, Microsoft Word styling/content, and any
|
||||
// tags and attributes not found in `preserve` from the given `content`.
|
||||
stripPaste: function(content) {
|
||||
// Clean word stuff out and strip tags that are not in `this.preserve`.
|
||||
content = this._cleanWordPaste(content);
|
||||
content = this.cleanPreserved(content);
|
||||
return content;
|
||||
},
|
||||
|
||||
// Parses `preserve` to setup `_tags` with a comma delimited list of all of the
|
||||
// defined tags, and the `_attributesMap` with a mapping between the allowed tags and
|
||||
// an array of their allowed attributes. For example, given this value:
|
||||
// `preserve` = 'p,a[href|class],span[*]'
|
||||
// The following will result:
|
||||
// `_tags` = 'p,a,span'
|
||||
// `_attributesMap` = ['p' => [], 'a' => ['href', 'class'], 'span' => ['*']]
|
||||
setupPreserved: function() {
|
||||
var self = this;
|
||||
this._tags = '';
|
||||
this._attributesMap = [];
|
||||
|
||||
ice.dom.each(this.preserve.split(','), function(i, tagAttr) {
|
||||
// Extract the tag and attributes list
|
||||
tagAttr.match(/(\w+)(\[(.+)\])?/);
|
||||
var tag = RegExp.$1;
|
||||
var attr = RegExp.$3;
|
||||
|
||||
if(self._tags) self._tags += ',';
|
||||
self._tags += tag.toLowerCase();
|
||||
self._attributesMap[tag] = attr.split('|');
|
||||
});
|
||||
},
|
||||
|
||||
// Cleans the given `body` by removing any tags not found in `_tags` and replacing them with
|
||||
// their inner contents, and removes attributes from any tags that aren't mapped in `_attributesMap`.
|
||||
cleanPreserved: function(body) {
|
||||
var self = this;
|
||||
var bodyel = this._ice.env.document.createElement('div');
|
||||
bodyel.innerHTML = body;
|
||||
|
||||
// Strip out any tags not found in `this._tags`, replacing the tags with their inner contents.
|
||||
bodyel = ice.dom.stripEnclosingTags(bodyel, this._tags);
|
||||
|
||||
// Strip out any attributes from the allowed set of tags that don't match what is in the `_attributesMap`
|
||||
ice.dom.each(ice.dom.find(bodyel, this._tags), function(i, el) {
|
||||
if (ice.dom.hasClass(el, 'skip-clean')) {
|
||||
return true;
|
||||
}
|
||||
var tag = el.tagName.toLowerCase();
|
||||
var attrMatches = self._attributesMap[tag];
|
||||
|
||||
// Kleene star - keep all of the attributes for this tag.
|
||||
if(attrMatches[0] && attrMatches[0] === '*')
|
||||
return true;
|
||||
|
||||
// Remove any foreign attributes that do not match the map.
|
||||
if(el.hasAttributes()) {
|
||||
var attributes = el.attributes;
|
||||
for(var i = attributes.length - 1; i >= 0; i--) {
|
||||
if(!ice.dom.inArray(attributes[i].name, attrMatches)) {
|
||||
el.removeAttribute(attributes[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return bodyel.innerHTML;
|
||||
},
|
||||
|
||||
_cleanWordPaste: function(content) {
|
||||
// Meta and link tags.
|
||||
content = content.replace(/<(meta|link)[^>]+>/g, "");
|
||||
|
||||
// Comments.
|
||||
content = content.replace(/<!--(.|\s)*?-->/g, '');
|
||||
|
||||
// Remove style tags.
|
||||
content = content.replace(/<style>[\s\S]*?<\/style>/g, '');
|
||||
|
||||
// Remove span and o:p etc. tags.
|
||||
//content = content.replace(/<\/?span[^>]*>/gi, "");
|
||||
content = content.replace(/<\/?\w+:[^>]*>/gi, '' );
|
||||
|
||||
// Remove XML tags.
|
||||
content = content.replace(/<\\?\?xml[^>]*>/gi, '');
|
||||
|
||||
// Generic cleanup.
|
||||
content = this._cleanPaste(content);
|
||||
|
||||
// Remove class, lang and style attributes.
|
||||
content = content.replace(/<(\w[^>]*) (lang)=([^ |>]*)([^>]*)/gi, "<$1$4");
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
_cleanPaste: function(content) {
|
||||
// Some generic content cleanup. Change all b/i tags to strong/em.
|
||||
content = content.replace(/<b(\s+|>)/g, "<strong$1");
|
||||
content = content.replace(/<\/b(\s+|>)/g, "</strong$1");
|
||||
content = content.replace(/<i(\s+|>)/g, "<em$1");
|
||||
content = content.replace(/<\/i(\s+|>)/g, "</em$1");
|
||||
return content;
|
||||
},
|
||||
|
||||
_cleanup: function(moveTo) {
|
||||
try {
|
||||
moveTo = moveTo && moveTo.lastChild || moveTo || this._tmpNode;
|
||||
// Move the range to the end of moveTo so that the cursor will be at the end of the paste.
|
||||
var range = this._ice.getCurrentRange();
|
||||
range.setStartAfter(moveTo);
|
||||
range.collapse(true);
|
||||
this._ice.selection.addRange(range);
|
||||
// Set focus back to ice element.
|
||||
if(this._ice.env.frame) {
|
||||
this._ice.env.frame.contentWindow.focus();
|
||||
} else {
|
||||
this._ice.element.focus();
|
||||
}
|
||||
// Kill the tmp node.
|
||||
this._tmpNode.parentNode.removeChild(this._tmpNode);
|
||||
this._tmpNode = null;
|
||||
// Kill any empty change nodes.
|
||||
var ins = this._ice.env.document.getElementsByClassName(this._ice.changeTypes['insertType'].alias);
|
||||
for(var i = 0; i < ins.length; i++) {
|
||||
if(!ins[i].textContent) {
|
||||
if(ins[i].parentNode) {
|
||||
ins[i].parentNode.removeChild(ins[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
window.console && console.error(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ice.dom.noInclusionInherits(IceCopyPastePlugin, ice.IcePlugin);
|
||||
exports._plugin.IceCopyPastePlugin = IceCopyPastePlugin;
|
||||
|
||||
}).call(this.ice);
|
||||
76
lib/ckeditor4/plugins/lite/js/plugins/IceEmdashPlugin/IceEmdashPlugin.js
Executable file
76
lib/ckeditor4/plugins/lite/js/plugins/IceEmdashPlugin/IceEmdashPlugin.js
Executable file
@@ -0,0 +1,76 @@
|
||||
(function() {
|
||||
|
||||
var exports = this;
|
||||
|
||||
/**
|
||||
* When active, this plugin will convert two successively typed dashes, within
|
||||
* the ice block element, into an emdash.
|
||||
*/
|
||||
var IceEmdashPlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
};
|
||||
|
||||
IceEmdashPlugin.prototype = {
|
||||
|
||||
keyDown: function(e) {
|
||||
// Catch dashes.
|
||||
if(ice.dom.isBrowser('mozilla')) {
|
||||
var version = parseInt(ice.dom.browser().version);
|
||||
if ( (version > 14 && e.keyCode === 173) || (version <= 14 && e.keyCode === 109) ) {
|
||||
return this.convertEmdash(e);
|
||||
}
|
||||
} else if(e.keyCode === 189) {
|
||||
return this.convertEmdash(e);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
convertEmdash: function(e) {
|
||||
var range = this._ice.getCurrentRange();
|
||||
if(range.collapsed) {
|
||||
try {
|
||||
// Move the start back one character so we can enclose the range around the previous character to check if it is a dash
|
||||
range.moveStart(ice.dom.CHARACTER_UNIT, -1);
|
||||
// Get the parent block element for the start and end containers
|
||||
var startBlock = ice.dom.getParents(range.startContainer, this._ice.blockEl)[0];
|
||||
var endBlock = ice.dom.getParents(range.endContainer, this._ice.blockEl)[0];
|
||||
// Make sure that the start and end containers aren't in different blocks, or that the start isn't in a delete.
|
||||
if(startBlock === endBlock && !this._ice.getIceNode(range.startContainer, 'deleteType')) {
|
||||
// Get the last character and check to see if it is a dash.
|
||||
c = range.toHtml();
|
||||
if(c === '-') {
|
||||
// Extract the last character/dash and insert an emdash
|
||||
range.extractContents();
|
||||
range.collapse();
|
||||
var mdash = this._ice.env.document.createTextNode('\u2014');
|
||||
if (this._ice.isTracking) {
|
||||
this._ice._insertNode(mdash, range);
|
||||
} else {
|
||||
range.insertNode(mdash);
|
||||
/* TO be reverted once mozilla fixes FF 15 issue */
|
||||
range.setStart(mdash, 1);
|
||||
range.collapse(true);
|
||||
/* FINISH revert */
|
||||
}
|
||||
/* TO be reverted once mozilla fixes FF 15 issue
|
||||
range = this._ice.getCurrentRange();
|
||||
range.moveStart(ice.dom.CHARACTER_UNIT, 1);
|
||||
range.collapse(true);
|
||||
this._ice.env.selection.addRange(range);
|
||||
*/
|
||||
this._ice._preventKeyPress = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
range.collapse();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ice.dom.noInclusionInherits(IceEmdashPlugin, ice.IcePlugin);
|
||||
exports._plugin.IceEmdashPlugin = IceEmdashPlugin;
|
||||
|
||||
}).call(this.ice);
|
||||
@@ -0,0 +1,184 @@
|
||||
(function() {
|
||||
|
||||
var exports = this, ice = this.ice;
|
||||
|
||||
var IceSmartQuotesPlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
};
|
||||
|
||||
IceSmartQuotesPlugin.prototype = {
|
||||
|
||||
/**
|
||||
* Finds each block in `element` and converts quotes into smart quotes.
|
||||
*/
|
||||
convert: function(element) {
|
||||
var self = this;
|
||||
try {
|
||||
self._ice.placeholdDeletes();
|
||||
ice.dom.each(element.getElementsByTagName(this._ice.blockEl), function(i, el) {
|
||||
self._convertBlock(el);
|
||||
});
|
||||
} catch(e) {
|
||||
window.console && console.error(e);
|
||||
} finally {
|
||||
self._ice.revertDeletePlaceholders();
|
||||
}
|
||||
},
|
||||
|
||||
// Converts the quotes in the given element to smart quotes.
|
||||
_convertBlock: function(el) {
|
||||
|
||||
// If there are less than 2 characters, we don't have enough to go on.
|
||||
if (ice.dom.getNodeTextContent(el) < 2) return;
|
||||
|
||||
var previous, current, next, data, html = '', getNextChar,
|
||||
regularSingle = "'",
|
||||
regularDouble = '"',
|
||||
smartSingleLeft = String.fromCharCode(8216), // aka - open curly single quote
|
||||
smartSingleRight = String.fromCharCode(8217), // aka - close curly single quote
|
||||
smartDoubleLeft = String.fromCharCode(8220), // aka - open curly double quote
|
||||
smartDoubleRight = String.fromCharCode(8221), // aka - close curly double quote
|
||||
isDigit = function(c) { return /\d/.test(c); },
|
||||
isChar = function(c) { return /\w/.test(c); },
|
||||
isSpace = function(c) { return c === String.fromCharCode(160) || c === String.fromCharCode(32); },
|
||||
isStartChar = function(c) { return isSpace(c) || c === '('; },
|
||||
isEndChar = function(c) { return isSpace(c) || c == null || c === ';' || c === ')' || c == '.' || c === '!' || c === ',' || c === '?' || c === ':'; },
|
||||
isNonSpace = function(c) { return !isSpace(c); },
|
||||
isDouble = function(c) { return c === regularDouble || c === smartDoubleLeft || c === smartDoubleRight; },
|
||||
isSingle = function(c) { return c === regularSingle || c === smartSingleLeft || c === smartSingleRight; };
|
||||
|
||||
// Split the html into array allocations with the following criteria:
|
||||
// html tags: starts with "<" and ends with ">"
|
||||
// html entities: starts with "&" and ends with ";"
|
||||
// characters: any character outside of an html tag or entity
|
||||
// So the following html:
|
||||
// n&ce <b id="bold">test</b>
|
||||
// Would split into the following array:
|
||||
// ['n', '&', 'c', 'e', ' ', '<b id="bold">', 't', 'e', 's', 't', '</b>'];
|
||||
data = ice.dom.getHtml(el).match(/(<("[^"]*"|'[^']*'|[^'">])*>|&.*;|.)/g);
|
||||
|
||||
// Searches through the `data` array from the given index a given number of
|
||||
// characters forward or backward and returns the found character.
|
||||
getNextChar = function(data, fromIndex, nCharacters) {
|
||||
var dLength = data.length,
|
||||
addWith = nCharacters < 0 ? -1 : 1;
|
||||
|
||||
return (function getChar(data, fromIndex, nCharacters) {
|
||||
// Base case - did we move outside of the bounds of the data array?
|
||||
if (fromIndex < 0 || fromIndex >= dLength) return null;
|
||||
|
||||
var next = data[fromIndex + addWith];
|
||||
|
||||
// If we find a character and we have moved through the
|
||||
// nCharacters, then the recursion is done.
|
||||
if (next && next.length == 1) {
|
||||
nCharacters += (addWith * -1);
|
||||
if (!nCharacters) return next;
|
||||
}
|
||||
return getChar(data, fromIndex + addWith, nCharacters);
|
||||
|
||||
})(data, fromIndex, nCharacters);
|
||||
};
|
||||
|
||||
ice.dom.each(data, function(pos, val) {
|
||||
// Convert space entities so that they can be processed as normal characters.
|
||||
if (val == ' ') val = data[pos] = ' ';
|
||||
|
||||
// If the val is a character, then examine the surroundings
|
||||
// and convert smart quotes, if necessary.
|
||||
if (val.length == 1) {
|
||||
|
||||
// Keep convenience pointers to the previous, current and next characters.
|
||||
previous = getNextChar(data, pos, -1);
|
||||
current = val;
|
||||
next = getNextChar(data, pos, 1);
|
||||
|
||||
switch (current) {
|
||||
|
||||
/**
|
||||
* Conversion Rules:
|
||||
* ----------------
|
||||
*
|
||||
* START: assign smart left/open
|
||||
* [SPACE|START_PARENTHESIS]'word
|
||||
* [SPACE|START_PARENTHESIS]"word
|
||||
*
|
||||
* END: assign smart right/close
|
||||
* word'[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
|
||||
* word"[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
|
||||
*
|
||||
* PLURAL_CONTRACTION: assign smart right/close
|
||||
* Matt's
|
||||
* can't
|
||||
* O'Reilly
|
||||
*
|
||||
* YEAR_ABBREVIATION: assign smart right/close
|
||||
* [SPACE|NULL]'99[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
|
||||
*
|
||||
* NESTED_START: assign smart left/open
|
||||
* [SPACE|NULL]"[SPACE]'word
|
||||
*
|
||||
* NESTED_END: assign smart left/open
|
||||
* word'[SPACE]"[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
|
||||
*
|
||||
* Notes:
|
||||
* - The following will not be converted correctly - ...word 'Til Death - it should
|
||||
* get a right/close smart quote, but will get a left/open.
|
||||
* - Distinguishing between year abbreviation, '99, and when to use an open single quote
|
||||
* could fail if a single quoted region starts with a double digit number - '99 problems'
|
||||
* - Since they are a rare case and there are many permutations, measurements are not being
|
||||
* handled (6'8", 6' 8", 6', 8").
|
||||
*/
|
||||
|
||||
// Convert smart single quotes to non-smart quote and fall through to single quote
|
||||
// handling, in case the context has changed and we need to update the smart quote.
|
||||
case smartSingleLeft:
|
||||
case smartSingleRight:
|
||||
current = regularSingle;
|
||||
case regularSingle:
|
||||
// YEAR_ABBREVIATION - look 2 ahead to see if there are two digits in a row - not fool proof
|
||||
if ((previous == null || isSpace(previous)) && isDigit(next) && isDigit(getNextChar(data, pos, 2)) && isEndChar(getNextChar(data, pos, 3)))
|
||||
current = smartSingleRight;
|
||||
// START
|
||||
else if (previous == null || (isStartChar(previous) && isNonSpace(next)))
|
||||
current = smartSingleLeft;
|
||||
// END
|
||||
else if (next == null || (isNonSpace(previous) && isEndChar(next)))
|
||||
current = smartSingleRight;
|
||||
// PLURAL_CONTRACTION
|
||||
else if (isChar(previous) && isChar(next))
|
||||
current = smartSingleRight;
|
||||
break;
|
||||
|
||||
// Convert smart double quotes to non-smart quote and fall through to double quote
|
||||
// handling, in case the context has changed and we need to update the smart quote.
|
||||
case smartDoubleLeft:
|
||||
case smartDoubleRight:
|
||||
current = regularDouble;
|
||||
case regularDouble:
|
||||
// NESTED_END
|
||||
if (isEndChar(next) && isSpace(previous) && isSingle(getNextChar(data, pos, -2)))
|
||||
current = smartDoubleRight;
|
||||
// START
|
||||
else if (previous == null || (isStartChar(previous) && isNonSpace(next)))
|
||||
current = smartDoubleLeft;
|
||||
// END
|
||||
else if (next == null || (isNonSpace(previous) && isEndChar(next)))
|
||||
current = smartDoubleRight;
|
||||
// NESTED_START
|
||||
else if ((previous == null || isSpace(previous)) && (isSpace(next) && isSingle(getNextChar(data, pos, 1))))
|
||||
current = smartDoubleLeft;
|
||||
break;
|
||||
}
|
||||
if (current != null) data[pos] = current;
|
||||
}
|
||||
});
|
||||
|
||||
ice.dom.setHtml(el, data.join(''));
|
||||
}
|
||||
};
|
||||
|
||||
ice.dom.noInclusionInherits(IceSmartQuotesPlugin, ice.IcePlugin);
|
||||
exports.ice._plugin.IceSmartQuotesPlugin = IceSmartQuotesPlugin;
|
||||
|
||||
}).call(this);
|
||||
50
lib/ckeditor4/plugins/lite/js/polyfills.js
Executable file
50
lib/ckeditor4/plugins/lite/js/polyfills.js
Executable file
@@ -0,0 +1,50 @@
|
||||
// Add ECMA262-5 string trim if not supported natively
|
||||
//
|
||||
if (typeof String.prototype.trim !== 'function') {
|
||||
String.prototype.trim = function() {
|
||||
return this.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
// Add ECMA262-5 Array methods if not supported natively
|
||||
//
|
||||
if (!('indexOf' in Array.prototype)) {
|
||||
Array.prototype.indexOf= function(find, i /*opt*/) {
|
||||
if (i===undefined) i= 0;
|
||||
if (i<0) i+= this.length;
|
||||
if (i<0) i= 0;
|
||||
for (var n= this.length; i<n; i++)
|
||||
if (i in this && this[i]===find)
|
||||
return i;
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
if (!('lastIndexOf' in Array.prototype)) {
|
||||
Array.prototype.lastIndexOf= function(find, i /*opt*/) {
|
||||
if (i===undefined) i= this.length-1;
|
||||
if (i<0) i+= this.length;
|
||||
if (i>this.length-1) i= this.length-1;
|
||||
for (i++; i-->0;) /* i++ because from-argument is sadly inclusive */
|
||||
if (i in this && this[i]===find)
|
||||
return i;
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
if (!('map' in Array.prototype)) {
|
||||
Array.prototype.map= function(mapper, that /*opt*/) {
|
||||
var other= new Array(this.length);
|
||||
for (var i= 0, n= this.length; i<n; i++)
|
||||
if (i in this)
|
||||
other[i]= mapper.call(that, this[i], i, this);
|
||||
return other;
|
||||
};
|
||||
}
|
||||
if (!('filter' in Array.prototype)) {
|
||||
Array.prototype.filter= function(filter, that /*opt*/) {
|
||||
var other= [], v;
|
||||
for (var i=0, n= this.length; i<n; i++)
|
||||
if (i in this && filter.call(that, v= this[i], i, this))
|
||||
other.push(v);
|
||||
return other;
|
||||
};
|
||||
}
|
||||
3224
lib/ckeditor4/plugins/lite/js/rangy/rangy-core.js
Executable file
3224
lib/ckeditor4/plugins/lite/js/rangy/rangy-core.js
Executable file
File diff suppressed because it is too large
Load Diff
507
lib/ckeditor4/plugins/lite/js/selection.js
Executable file
507
lib/ckeditor4/plugins/lite/js/selection.js
Executable file
@@ -0,0 +1,507 @@
|
||||
(function () {
|
||||
|
||||
var exports = this,
|
||||
Selection;
|
||||
|
||||
Selection = function (env) {
|
||||
this._selection = null;
|
||||
this.env = env;
|
||||
|
||||
this._initializeRangeLibrary();
|
||||
this._getSelection();
|
||||
};
|
||||
|
||||
Selection.prototype = {
|
||||
|
||||
/**
|
||||
* Returns the selection object for the current browser.
|
||||
*/
|
||||
_getSelection: function () {
|
||||
if (this._selection) {
|
||||
this._selection.refresh();
|
||||
}
|
||||
else if (this.env.frame) {
|
||||
this._selection = rangy.getIframeSelection(this.env.frame);
|
||||
}
|
||||
else {
|
||||
this._selection = rangy.getSelection();
|
||||
}
|
||||
return this._selection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a range object.
|
||||
*/
|
||||
createRange: function () {
|
||||
return rangy.createRange(this.env.document);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the range object at the specified position. The current range object
|
||||
* is at position 0. Note - currently only setting single range in `addRange` so
|
||||
* position 0 will be the only allocation filled.
|
||||
*/
|
||||
getRangeAt: function (pos) {
|
||||
this._selection.refresh();
|
||||
try {
|
||||
return this._selection.getRangeAt(pos);
|
||||
}
|
||||
catch (e) {
|
||||
this._selection = null;
|
||||
return this._getSelection().getRangeAt(0);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the specified range to the current selection. Note - only supporting setting
|
||||
* a single range, so the previous range gets evicted.
|
||||
*/
|
||||
addRange: function (range) {
|
||||
this._selection || (this._selection = this._getSelection());
|
||||
this._selection.setSingleRange(range);
|
||||
this._selection.ranges = [range];
|
||||
return;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize and extend the `rangy` library with some custom functionality.
|
||||
*/
|
||||
_initializeRangeLibrary: function () {
|
||||
var self = this;
|
||||
|
||||
rangy.init();
|
||||
rangy.config.checkSelectionRanges = false;
|
||||
|
||||
var move = function (range, unitType, units, isStart) {
|
||||
if (units === 0) {
|
||||
throw Error('InvalidArgumentException: units cannot be 0');
|
||||
}
|
||||
|
||||
switch (unitType) {
|
||||
case ice.dom.CHARACTER_UNIT:
|
||||
if (units > 0) {
|
||||
range.moveCharRight(isStart, units);
|
||||
} else {
|
||||
range.moveCharLeft(isStart, units * -1);
|
||||
}
|
||||
break;
|
||||
|
||||
case ice.dom.WORD_UNIT:
|
||||
default:
|
||||
// Removed. TODO: possibly refactor or re-implement.
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves the start of the range using the specified `unitType`, by the specified
|
||||
* number of `units`. Defaults to `CHARACTER_UNIT` and units of 1.
|
||||
*/
|
||||
rangy.rangePrototype.moveStart = function (unitType, units) {
|
||||
move(this, unitType, units, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves the end of the range using the specified `unitType`, by the specified
|
||||
* number of `units`.
|
||||
*/
|
||||
rangy.rangePrototype.moveEnd = function (unitType, units) {
|
||||
move(this, unitType, units, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Depending on the given `start` boolean, sets the start or end containers
|
||||
* to the given `container` with `offset` units.
|
||||
*/
|
||||
rangy.rangePrototype.setRange = function (start, container, offset) {
|
||||
if (start) {
|
||||
this.setStart(container, offset);
|
||||
} else {
|
||||
this.setEnd(container, offset);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Depending on the given `moveStart` boolean, moves the start or end containers
|
||||
* to the left by the given number of character `units`. Use the following
|
||||
* example as a demonstration for where the range will fall as it moves in and
|
||||
* out of tag boundaries (where "|" is the marked range):
|
||||
*
|
||||
* test <em>it</em> o|ut
|
||||
* test <em>it</em> |out
|
||||
* test <em>it</em>| out
|
||||
* test <em>i|t</em> out
|
||||
* test <em>|it</em> out
|
||||
* test| <em>it</em> out
|
||||
* tes|t <em>it</em> out
|
||||
*
|
||||
* A range could be mapped in one of two ways:
|
||||
*
|
||||
* (1) If a startContainer is a Node of type Text, Comment, or CDATASection, then startOffset
|
||||
* is the number of characters from the start of startNode. For example, the following
|
||||
* are the range properties for `<p>te|st</p>` (where "|" is the collapsed range):
|
||||
*
|
||||
* startContainer: <TEXT>test<TEXT>
|
||||
* startOffset: 2
|
||||
* endContainer: <TEXT>test<TEXT>
|
||||
* endOffset: 2
|
||||
*
|
||||
* (2) For other Node types, startOffset is the number of child nodes between the start of
|
||||
* the startNode. Take the following html fragment:
|
||||
*
|
||||
* `<p>some <span>test</span> text</p>`
|
||||
*
|
||||
* If we were working with the following range properties:
|
||||
*
|
||||
* startContainer: <p>
|
||||
* startOffset: 2
|
||||
* endContainer: <p>
|
||||
* endOffset: 2
|
||||
*
|
||||
* Since <p> is an Element node, the offsets are based on the offset in child nodes of <p> and
|
||||
* the range is selecting the second child - the <span> tag.
|
||||
*
|
||||
* <p><TEXT>some </TEXT><SPAN>test</SPAN><TEXT> text</TEXT></p>
|
||||
*/
|
||||
rangy.rangePrototype.moveCharLeft = function (moveStart, units) {
|
||||
var container, offset;
|
||||
|
||||
if (moveStart) {
|
||||
container = this.startContainer;
|
||||
offset = this.startOffset;
|
||||
} else {
|
||||
container = this.endContainer;
|
||||
offset = this.endOffset;
|
||||
}
|
||||
|
||||
// Handle the case where the range conforms to (2) (noted in the comment above).
|
||||
if (container.nodeType === ice.dom.ELEMENT_NODE) {
|
||||
if (container.hasChildNodes()) {
|
||||
container = container.childNodes[offset];
|
||||
|
||||
container = this.getPreviousTextNode(container);
|
||||
|
||||
// Get the previous text container that is not an empty text node.
|
||||
while (container && container.nodeType == ice.dom.TEXT_NODE && container.nodeValue === "") {
|
||||
container = this.getPreviousTextNode(container);
|
||||
}
|
||||
|
||||
offset = container.data.length - units;
|
||||
} else {
|
||||
offset = units * -1;
|
||||
}
|
||||
} else {
|
||||
offset -= units;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
// We need to move to a previous selectable container.
|
||||
while (offset < 0) {
|
||||
var skippedBlockElem = [];
|
||||
|
||||
container = this.getPreviousTextNode(container, skippedBlockElem);
|
||||
|
||||
// We are at the beginning/out of the editable - break.
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.nodeType === ice.dom.ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
offset += container.data.length;
|
||||
}
|
||||
}
|
||||
|
||||
this.setRange(moveStart, container, offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Depending on the given `moveStart` boolean, moves the start or end containers
|
||||
* to the right by the given number of character `units`. Use the following
|
||||
* example as a demonstration for where the range will fall as it moves in and
|
||||
* out of tag boundaries (where "|" is the marked range):
|
||||
*
|
||||
* tes|t <em>it</em> out
|
||||
* test| <em>it</em> out
|
||||
* test |<em>it</em> out
|
||||
* test <em>i|t</em> out
|
||||
* test <em>it|</em> out
|
||||
* test <em>it</em> |out
|
||||
*
|
||||
* A range could be mapped in one of two ways:
|
||||
*
|
||||
* (1) If a startContainer is a Node of type Text, Comment, or CDATASection, then startOffset
|
||||
* is the number of characters from the start of startNode. For example, the following
|
||||
* are the range properties for `<p>te|st</p>` (where "|" is the collapsed range):
|
||||
*
|
||||
* startContainer: <TEXT>test<TEXT>
|
||||
* startOffset: 2
|
||||
* endContainer: <TEXT>test<TEXT>
|
||||
* endOffset: 2
|
||||
*
|
||||
* (2) For other Node types, startOffset is the number of child nodes between the start of
|
||||
* the startNode. Take the following html fragment:
|
||||
*
|
||||
* `<p>some <span>test</span> text</p>`
|
||||
*
|
||||
* If we were working with the following range properties:
|
||||
*
|
||||
* startContainer: <p>
|
||||
* startOffset: 2
|
||||
* endContainer: <p>
|
||||
* endOffset: 2
|
||||
*
|
||||
* Since <p> is an Element node, the offsets are based on the offset in child nodes of <p> and
|
||||
* the range is selecting the second child - the <span> tag.
|
||||
*
|
||||
* <p><TEXT>some </TEXT><SPAN>test</SPAN><TEXT> text</TEXT></p>
|
||||
*/
|
||||
rangy.rangePrototype.moveCharRight = function (moveStart, units) {
|
||||
var container, offset;
|
||||
|
||||
if (moveStart) {
|
||||
container = this.startContainer;
|
||||
offset = this.startOffset;
|
||||
} else {
|
||||
container = this.endContainer;
|
||||
offset = this.endOffset;
|
||||
}
|
||||
|
||||
if (container.nodeType === ice.dom.ELEMENT_NODE) {
|
||||
container = container.childNodes[offset];
|
||||
if (container.nodeType !== ice.dom.TEXT_NODE) {
|
||||
container = this.getNextTextNode(container);
|
||||
}
|
||||
|
||||
offset = units;
|
||||
} else {
|
||||
offset += units;
|
||||
}
|
||||
|
||||
var diff = (offset - container.data.length);
|
||||
if (diff > 0) {
|
||||
var skippedBlockElem = [];
|
||||
// We need to move to the next selectable container.
|
||||
while (diff > 0) {
|
||||
container = this.getNextContainer(container, skippedBlockElem);
|
||||
|
||||
if (container.nodeType === ice.dom.ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (container.data.length >= diff) {
|
||||
// We found a container with enough content to select.
|
||||
break;
|
||||
} else if (container.data.length > 0) {
|
||||
// Container does not have enough content,
|
||||
// find the next one.
|
||||
diff -= container.data.length;
|
||||
}
|
||||
}
|
||||
|
||||
offset = diff;
|
||||
}
|
||||
|
||||
this.setRange(moveStart, container, offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the deepest next container that the range can be extended to.
|
||||
* For example, if the next container is an element that contains text nodes,
|
||||
* the the container's firstChild is returned.
|
||||
*/
|
||||
rangy.rangePrototype.getNextContainer = function (container, skippedBlockElem) {
|
||||
if (!container) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (container.nextSibling) {
|
||||
container = container.nextSibling;
|
||||
if (container.nodeType !== ice.dom.TEXT_NODE) {
|
||||
var child = this.getFirstSelectableChild(container);
|
||||
if (child !== null) {
|
||||
return child;
|
||||
}
|
||||
} else if (this.isSelectable(container) === true) {
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
// Look at parents next sibling.
|
||||
while (container && !container.nextSibling) {
|
||||
container = container.parentNode;
|
||||
}
|
||||
|
||||
if (!container) {
|
||||
return null;
|
||||
}
|
||||
|
||||
container = container.nextSibling;
|
||||
if (this.isSelectable(container) === true) {
|
||||
return container;
|
||||
} else if (skippedBlockElem && ice.dom.isBlockElement(container) === true) {
|
||||
skippedBlockElem.push(container);
|
||||
}
|
||||
|
||||
var selChild = this.getFirstSelectableChild(container);
|
||||
if (selChild !== null) {
|
||||
return selChild;
|
||||
}
|
||||
|
||||
return this.getNextContainer(container, skippedBlockElem);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the deepest previous container that the range can be extended to.
|
||||
* For example, if the previous container is an element that contains text nodes,
|
||||
* then the container's lastChild is returned.
|
||||
*/
|
||||
rangy.rangePrototype.getPreviousContainer = function (container, skippedBlockElem) {
|
||||
if (!container) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (container.previousSibling) {
|
||||
container = container.previousSibling;
|
||||
if (container.nodeType !== ice.dom.TEXT_NODE) {
|
||||
if (ice.dom.isStubElement(container) === true) {
|
||||
return container;
|
||||
} else {
|
||||
var child = this.getLastSelectableChild(container);
|
||||
if (child !== null) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
} else if (this.isSelectable(container) === true) {
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
// Look at parents next sibling.
|
||||
while (container && !container.previousSibling) {
|
||||
container = container.parentNode;
|
||||
}
|
||||
|
||||
if (!container) {
|
||||
return null;
|
||||
}
|
||||
|
||||
container = container.previousSibling;
|
||||
if (this.isSelectable(container) === true) {
|
||||
return container;
|
||||
} else if (skippedBlockElem && ice.dom.isBlockElement(container) === true) {
|
||||
skippedBlockElem.push(container);
|
||||
}
|
||||
|
||||
var selChild = this.getLastSelectableChild(container);
|
||||
if (selChild !== null) {
|
||||
return selChild;
|
||||
}
|
||||
return this.getPreviousContainer(container, skippedBlockElem);
|
||||
};
|
||||
|
||||
rangy.rangePrototype.getNextTextNode = function (container) {
|
||||
if (container.nodeType === ice.dom.ELEMENT_NODE) {
|
||||
if (container.childNodes.length !== 0) {
|
||||
return this.getFirstSelectableChild(container);
|
||||
}
|
||||
}
|
||||
|
||||
container = this.getNextContainer(container);
|
||||
if (container.nodeType === ice.dom.TEXT_NODE) {
|
||||
return container;
|
||||
}
|
||||
|
||||
return this.getNextTextNode(container);
|
||||
};
|
||||
|
||||
rangy.rangePrototype.getPreviousTextNode = function (container, skippedBlockEl) {
|
||||
container = this.getPreviousContainer(container, skippedBlockEl);
|
||||
if (container.nodeType === ice.dom.TEXT_NODE) {
|
||||
return container;
|
||||
}
|
||||
|
||||
return this.getPreviousTextNode(container, skippedBlockEl);
|
||||
};
|
||||
|
||||
rangy.rangePrototype.getFirstSelectableChild = function (element) {
|
||||
if (element) {
|
||||
if (element.nodeType !== ice.dom.TEXT_NODE) {
|
||||
var child = element.firstChild;
|
||||
while (child) {
|
||||
if (this.isSelectable(child) === true) {
|
||||
return child;
|
||||
} else if (child.firstChild) {
|
||||
// This node does have child nodes.
|
||||
var res = this.getFirstSelectableChild(child);
|
||||
if (res !== null) {
|
||||
return res;
|
||||
} else {
|
||||
child = child.nextSibling;
|
||||
}
|
||||
} else {
|
||||
child = child.nextSibling;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Given element is a text node so return it.
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
rangy.rangePrototype.getLastSelectableChild = function (element) {
|
||||
if (element) {
|
||||
if (element.nodeType !== ice.dom.TEXT_NODE) {
|
||||
var child = element.lastChild;
|
||||
while (child) {
|
||||
if (this.isSelectable(child) === true) {
|
||||
return child;
|
||||
} else if (child.lastChild) {
|
||||
// This node does have child nodes.
|
||||
var res = this.getLastSelectableChild(child);
|
||||
if (res !== null) {
|
||||
return res;
|
||||
} else {
|
||||
child = child.previousSibling;
|
||||
}
|
||||
} else {
|
||||
child = child.previousSibling;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Given element is a text node so return it.
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
rangy.rangePrototype.isSelectable = function (container) {
|
||||
if (container && container.nodeType === ice.dom.TEXT_NODE && container.data.length !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
rangy.rangePrototype.getHTMLContents = function (clonedSelection) {
|
||||
if (!clonedSelection) {
|
||||
clonedSelection = this.cloneContents();
|
||||
}
|
||||
var div = self.env.document.createElement('div');
|
||||
div.appendChild(clonedSelection.cloneNode(true));
|
||||
return div.innerHTML;
|
||||
};
|
||||
|
||||
rangy.rangePrototype.getHTMLContentsObj = function () {
|
||||
return this.cloneContents();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
exports.Selection = Selection;
|
||||
|
||||
}).call(this.ice);
|
||||
27
lib/ckeditor4/plugins/lite/lite_interface.js
Executable file
27
lib/ckeditor4/plugins/lite/lite_interface.js
Executable file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
Copyright 2013 LoopIndex, This file is part of the Track Changes plugin for CKEditor.
|
||||
|
||||
The track changes plugin is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License, version 2, as published by the Free Software Foundation.
|
||||
This program 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 Lesser General Public License along with this program as the file lgpl.txt. If not, see http://www.gnu.org/licenses/lgpl.html.
|
||||
|
||||
Written by (David *)Frenkiel - https://github.com/imdfl
|
||||
**/
|
||||
var LITE = {
|
||||
Events : {
|
||||
INIT : "lite:init",
|
||||
ACCEPT : "lite:accept",
|
||||
REJECT : "lite:reject",
|
||||
SHOW_HIDE : "lite:showHide",
|
||||
TRACKING : "lite:tracking"
|
||||
},
|
||||
|
||||
Commands : {
|
||||
TOGGLE_TRACKING : "lite.ToggleTracking",
|
||||
TOGGLE_SHOW : "lite.ToggleShow",
|
||||
ACCEPT_ALL : "lite.AcceptAll",
|
||||
REJECT_ALL : "lite.RejectAll",
|
||||
ACCEPT_ONE : "lite.AcceptOne",
|
||||
REJECT_ONE : "lite.RejectOne"
|
||||
}
|
||||
}
|
||||
651
lib/ckeditor4/plugins/lite/plugin.js
Executable file
651
lib/ckeditor4/plugins/lite/plugin.js
Executable file
@@ -0,0 +1,651 @@
|
||||
/**
|
||||
Copyright 2013 LoopIndex, This file is part of the Track Changes plugin for CKEditor.
|
||||
|
||||
The track changes plugin is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License, version 2, as published by the Free Software Foundation.
|
||||
This program 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 Lesser General Public License along with this program as the file lgpl.txt. If not, see http://www.gnu.org/licenses/lgpl.html.
|
||||
|
||||
Written by (David *)Frenkiel - https://github.com/imdfl
|
||||
**/
|
||||
|
||||
(function() {
|
||||
|
||||
var LITE = {
|
||||
Events : {
|
||||
INIT : "lite:init",
|
||||
ACCEPT : "lite:accept",
|
||||
REJECT : "lite:reject",
|
||||
SHOW_HIDE : "lite:showHide",
|
||||
TRACKING : "lite:tracking"
|
||||
},
|
||||
|
||||
Commands : {
|
||||
TOGGLE_TRACKING : "lite.ToggleTracking",
|
||||
TOGGLE_SHOW : "lite.ToggleShow",
|
||||
ACCEPT_ALL : "lite.AcceptAll",
|
||||
REJECT_ALL : "lite.RejectAll",
|
||||
ACCEPT_ONE : "lite.AcceptOne",
|
||||
REJECT_ONE : "lite.RejectOne"
|
||||
}
|
||||
}
|
||||
CKEDITOR.plugins.add( 'lite',
|
||||
{
|
||||
props : {
|
||||
deleteTag: 'span',
|
||||
insertTag: 'span',
|
||||
deleteClass: 'ice-del',
|
||||
insertClass: 'ice-ins',
|
||||
changeIdAttribute: 'data-cid',
|
||||
userIdAttribute: 'data-userid',
|
||||
userNameAttribute: 'data-username',
|
||||
changeDataAttribute: 'data-changedata',
|
||||
stylePrefix: 'ice-cts',
|
||||
timeAttribute: 'data-time',
|
||||
preserveOnPaste: 'p',
|
||||
user: { name: 'Unknown', id: 141414 },
|
||||
css: 'css/lite.css',
|
||||
titleTemplate : "Changed by %u %t"
|
||||
},
|
||||
|
||||
_domLoaded : false,
|
||||
_scriptsLoaded : false,
|
||||
_editor : null,
|
||||
_tracker : null,
|
||||
_isVisible : true, // changes are visible
|
||||
_liteCommandNames : [],
|
||||
_isTracking : false,
|
||||
_canAcceptReject : true, // enable state for accept reject overriding editor readonly
|
||||
|
||||
/**
|
||||
* Called by CKEditor to init the plugin
|
||||
* @param ed an instance of CKEditor
|
||||
*/
|
||||
init: function(ed) {
|
||||
if (this._inited) { // (CKEDITOR.ELEMENT_MODE_INLINE == ed.elementMode) {
|
||||
return;
|
||||
}
|
||||
this._inited = true;
|
||||
this._ieFix();
|
||||
ed.ui.addToolbarGroup('lite');
|
||||
this._setPluginFeatures(ed, this.props);
|
||||
|
||||
var liteConfig = ed.config.lite || {};
|
||||
|
||||
var allow = liteConfig.acceptRejectInReadOnly === true;
|
||||
var commandsMap = [
|
||||
{ command : LITE.Commands.TOGGLE_TRACKING,
|
||||
exec : this._onToggleTracking,
|
||||
title: "Toggle Tracking Changes",
|
||||
icon: "track_changes_on_off.png",
|
||||
trackingOnly : false
|
||||
},
|
||||
{
|
||||
command: LITE.Commands.TOGGLE_SHOW,
|
||||
exec: this._onToggleShow,
|
||||
title: "Toggle Tracking Changes",
|
||||
icon: "show_hide.png",
|
||||
readOnly : true
|
||||
},
|
||||
{
|
||||
command:LITE.Commands.ACCEPT_ALL,
|
||||
exec:this._onAcceptAll,
|
||||
title:"Accept all changes",
|
||||
icon:"accept_all.png",
|
||||
readOnly : allow
|
||||
},
|
||||
{
|
||||
command:LITE.Commands.REJECT_ALL,
|
||||
exec: this._onRejectAll,
|
||||
title: "Reject all changes",
|
||||
icon:"reject_all.png",
|
||||
readOnly : allow
|
||||
},
|
||||
{
|
||||
command:LITE.Commands.ACCEPT_ONE,
|
||||
exec:this._onAcceptOne,
|
||||
title:"Accept Change",
|
||||
icon:"accept_one.png",
|
||||
readOnly : allow
|
||||
},
|
||||
{
|
||||
command:LITE.Commands.REJECT_ONE,
|
||||
exec:this._onRejectOne,
|
||||
title:"Reject Change",
|
||||
icon:"reject_one.png",
|
||||
readOnly : allow
|
||||
}
|
||||
];
|
||||
|
||||
this._editor = ed;
|
||||
this._isVisible = true;
|
||||
this._isTracking = true;
|
||||
this._eventsBounds = false;
|
||||
|
||||
ed.on("contentDom", (function(dom) {
|
||||
this._onDomLoaded(dom);
|
||||
}).bind(this));
|
||||
var path = this.path;
|
||||
|
||||
var jQueryPath = liteConfig.jQueryPath || "js/jquery.min.js";
|
||||
|
||||
var commands = liteConfig.commands || [LITE.Commands.TOGGLE_TRACKING, LITE.Commands.TOGGLE_SHOW, LITE.Commands.ACCEPT_ALL, LITE.Commands.REJECT_ALL, LITE.Commands.ACCEPT_ONE, LITE.Commands.REJECT_ONE];
|
||||
var scripts = liteConfig.includes || ["js/rangy/rangy-core.js", "js/ice.js", "js/dom.js", "js/selection.js", "js/bookmark.js",
|
||||
"js/icePluginManager.js", "js/icePlugin.js", "lite_interface.js"];
|
||||
|
||||
|
||||
var self = this;
|
||||
|
||||
function add1(rec) {
|
||||
var cmd = ed.addCommand(rec.command, {
|
||||
exec : rec.exec.bind(self),
|
||||
readOnly: rec.readOnly || false
|
||||
});
|
||||
|
||||
if (commands.indexOf(rec.command) >= 0) { // configuration doens't include this command
|
||||
var name = self._commandNameToUIName(rec.command);
|
||||
ed.ui.addButton(name, {
|
||||
label : rec.title,
|
||||
command : rec.command,
|
||||
icon : path + "icons/" + rec.icon,
|
||||
toolbar: "lite"
|
||||
});
|
||||
if (rec.trackingOnly !== false) {
|
||||
self._liteCommandNames.push(rec.command);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (var i = 0, len = commandsMap.length; i < len; ++i) {
|
||||
add1(commandsMap[i]);
|
||||
}
|
||||
|
||||
|
||||
for (var i = 0, len = scripts.length; i < len; ++i) {
|
||||
scripts[i] = path + scripts[i];
|
||||
}
|
||||
if (typeof(jQuery) == "undefined") {
|
||||
scripts.splice(0, 0, this.path + jQueryPath)
|
||||
}
|
||||
|
||||
var load1 = function(_scripts) {
|
||||
if (_scripts.length < 1) {
|
||||
self._onScriptsLoaded();
|
||||
}
|
||||
else {
|
||||
CKEDITOR.scriptLoader.load(_scripts.shift(), function() {load1(_scripts);}, self)
|
||||
}
|
||||
}
|
||||
|
||||
load1(scripts);
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the state of change tracking for the change editor associated with this plugin
|
||||
* @param track if bool, set the tracking state to this value, otherwise toggle the state
|
||||
* @param bNotify if not false, dispatch the TRACKING event
|
||||
*/
|
||||
toggleTracking: function(track, bNotify) {
|
||||
//console.log("plugin.toggleTracking", !!track);
|
||||
var tracking = (typeof(track) == "undefined") ? (! this._isTracking) : track;
|
||||
this._isTracking = tracking;
|
||||
|
||||
for (var i = this._liteCommandNames.length - 1; i >= 0; --i) {
|
||||
var cmd = this._editor.getCommand(this._liteCommandNames[i]);
|
||||
if (cmd) {
|
||||
if (tracking) {
|
||||
cmd.enable();
|
||||
}
|
||||
else {
|
||||
cmd.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tracking) {
|
||||
this._tracker.enableChangeTracking();
|
||||
this.toggleShow(true);
|
||||
}
|
||||
else {
|
||||
this._tracker.disableChangeTracking();
|
||||
this.toggleShow(false);
|
||||
}
|
||||
var ui = this._editor.ui.get(this._commandNameToUIName(LITE.Commands.TOGGLE_TRACKING));
|
||||
if (ui) {
|
||||
ui.setState(tracking ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF);
|
||||
this._setButtonTitle(ui, tracking ? 'Stop tracking changes' : 'Start tracking changes');
|
||||
}
|
||||
if (bNotify !== false) {
|
||||
this._editor.fire(LITE.Events.TRACKING, {tracking:tracking})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the visibility of tracked changes for the change editor associated with this plugin
|
||||
* @param show if bool, set the visibility state to this value, otherwise toggle the state
|
||||
* @param bNotify if not false, dispatch the TOGGLE_SHOW event
|
||||
*/
|
||||
toggleShow : function(show, bNotify) {
|
||||
var vis = (typeof(show) == "undefined") ? (! this._isVisible) : show;
|
||||
this._isVisible = vis;
|
||||
if (this._isTracking) {
|
||||
this._setCommandsState(LITE.Commands.TOGGLE_SHOW, vis ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF);
|
||||
}
|
||||
this._tracker.setShowChanges(vis && this._isTracking);
|
||||
var ui = this._editor.ui.get(this._commandNameToUIName(LITE.Commands.TOGGLE_SHOW));
|
||||
if (ui) {
|
||||
this._setButtonTitle(ui, vis ? 'Hide tracked changes' : 'Show tracked changes');
|
||||
}
|
||||
if (bNotify !== false) {
|
||||
this._editor.fire(LITE.Events.SHOW_HIDE, {show:vis});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Accept all tracked changes
|
||||
*/
|
||||
acceptAll : function(options) {
|
||||
this._tracker.acceptAll(options);
|
||||
this._cleanup();
|
||||
this._editor.fire(LITE.Events.ACCEPT, {options : options});
|
||||
},
|
||||
|
||||
/**
|
||||
* Reject all tracked changes
|
||||
*/
|
||||
rejectAll : function(options) {
|
||||
this._tracker.rejectAll(options);
|
||||
this._cleanup();
|
||||
this._editor.fire(LITE.Events.REJECT, {options : options});
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the name & id of the current user
|
||||
* @param info an object with the fields name, id
|
||||
*/
|
||||
setUserInfo : function(info) {
|
||||
info = info || {};
|
||||
if (this._tracker) {
|
||||
this._tracker.setCurrentUser({id: info.id || "", name : info.name || ""});
|
||||
}
|
||||
if (this._editor) {
|
||||
var lite = this._editor.config.lite || {};
|
||||
lite.userId = info.id;
|
||||
lite.userName = info.name;
|
||||
this._editor.config.lite = lite;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the count of pending changes
|
||||
* @param options optional list of user ids whose changes we include or exclude (only one of the two should be provided,
|
||||
* exclude has precdence).
|
||||
*/
|
||||
countChanges : function(options) {
|
||||
return ((this._tracker && this._tracker.countChanges(options)) || 0);
|
||||
},
|
||||
|
||||
enableAcceptReject : function(bEnable) {
|
||||
this._canAcceptReject = !!bEnable;
|
||||
this._onIceChange();
|
||||
},
|
||||
|
||||
/**
|
||||
* For the CKEditor content filtering system, not operational yet
|
||||
*/
|
||||
filterIceElement : function( e ) {
|
||||
if (! e) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
if (e.hasClass(this.props.insertClass) || e.hasClass(this.props.deleteClass)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
_onDomLoaded : function(dom) {
|
||||
this._domLoaded = true;
|
||||
this._editor = dom.editor;
|
||||
this._onReady();
|
||||
},
|
||||
|
||||
_onScriptsLoaded : function(completed, failed) {
|
||||
//console.log("ICE: scripts loaded");
|
||||
this._scriptsLoaded = true;
|
||||
this._onReady();
|
||||
},
|
||||
|
||||
_loadCSS : function(doc) {
|
||||
//console.log("plugin load CSS")
|
||||
var head = doc.getElementsByTagName("head")[0];
|
||||
var style = doc.createElement("link");
|
||||
style.setAttribute("rel", "stylesheet");
|
||||
style.setAttribute("type", "text/css");
|
||||
style.setAttribute("href", this.path + "css/lite.css");
|
||||
head.appendChild(style);
|
||||
},
|
||||
|
||||
_onReady : function() {
|
||||
if (! this._scriptsLoaded || ! this._domLoaded) {
|
||||
//console.log("ICE: cannot proceed");
|
||||
return;
|
||||
}
|
||||
// leave some time for initing, seems to help...
|
||||
setTimeout(this._afterReady.bind(this), 50);
|
||||
},
|
||||
|
||||
_getBody : function() {
|
||||
try {
|
||||
var mode = this._editor.elementMode;
|
||||
if (CKEDITOR.ELEMENT_MODE_INLINE == mode) {
|
||||
return this._editor.element.$;
|
||||
}
|
||||
|
||||
return this._editor.document.$.body;
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
_afterReady : function() {
|
||||
var e = this._editor;
|
||||
var doc = e.document.$;
|
||||
this._loadCSS(doc);
|
||||
var body = this._getBody();
|
||||
var props = this.props;
|
||||
|
||||
if (! this._eventsBounds) {
|
||||
this._eventsBounds = true;
|
||||
e.on("afterCommandExec", (function(event) {
|
||||
var name = this._tracker && event.data && event.data.name;
|
||||
if (name == "undo" || name == "redo") {
|
||||
this._tracker.reload();
|
||||
}
|
||||
}).bind(this));
|
||||
e.on("paste", this._onPaste.bind(this));
|
||||
}
|
||||
|
||||
if (this._tracker) {
|
||||
if (body != this._tracker.getContentElement()) {
|
||||
this._tracker.stopTracking(true);
|
||||
jQuery(this._tracker).unbind();
|
||||
this._tracker = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (null == this._tracker) {
|
||||
var config = e.config.lite || {};
|
||||
var iceprops = {
|
||||
element: body,
|
||||
isTracking: true,
|
||||
handleEvents : true,
|
||||
mergeBlocks : false,
|
||||
currentUser: {
|
||||
id: config.userId || "",
|
||||
name: config.userName || ""
|
||||
},
|
||||
plugins: [
|
||||
],
|
||||
changeTypes: {
|
||||
insertType: {tag: this.props.insertTag, alias: this.props.insertClass},
|
||||
deleteType: {tag: this.props.deleteTag, alias: this.props.deleteClass}
|
||||
}
|
||||
};
|
||||
jQuery.extend(iceprops, this.props);
|
||||
this._tracker = new ice.InlineChangeEditor(iceprops);
|
||||
try {
|
||||
this._tracker.startTracking();
|
||||
this.toggleTracking(true, false);
|
||||
jQuery(this._tracker).on("change", this._onIceChange.bind(this));
|
||||
e.on("selectionChange", this._onSelectionChanged.bind(this));
|
||||
e.fire(LITE.Events.INIT, {lite: this});
|
||||
this._onIceChange(null);
|
||||
}
|
||||
catch(e) {
|
||||
console && console.error && console.error("ICE plugin init:", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onToggleShow : function(event) {
|
||||
this.toggleShow();
|
||||
},
|
||||
|
||||
_onToggleTracking : function(event) {
|
||||
this.toggleTracking();
|
||||
},
|
||||
|
||||
_onRejectAll : function(event) {
|
||||
this.rejectAll();
|
||||
},
|
||||
|
||||
_onAcceptAll : function(event) {
|
||||
this.acceptAll();
|
||||
},
|
||||
|
||||
_onAcceptOne : function(event) {
|
||||
var node = this._tracker.currentChangeNode();
|
||||
if (node) {
|
||||
this._tracker.acceptChange(node);
|
||||
this._cleanup();
|
||||
this._editor.fire(LITE.Events.ACCEPT);
|
||||
this._onSelectionChanged(null);
|
||||
}
|
||||
},
|
||||
|
||||
_onRejectOne : function(event) {
|
||||
var node = this._tracker.currentChangeNode();
|
||||
if (node) {
|
||||
this._tracker.rejectChange(node);
|
||||
this._cleanup();
|
||||
this._editor.fire(LITE.Events.REJECT);
|
||||
this._onSelectionChanged(null);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean up empty ICE elements
|
||||
*/
|
||||
_cleanup : function() {
|
||||
var body = this._getBody();
|
||||
empty = jQuery(body).find(self.insertSelector + ':empty,' + self.deleteSelector + ':empty');
|
||||
empty.remove();
|
||||
this._onSelectionChanged(null);
|
||||
},
|
||||
|
||||
_setButtonTitle : function(button, title) {
|
||||
var e = jQuery('#' + button._.id);
|
||||
e.attr('title', title);
|
||||
},
|
||||
|
||||
/**
|
||||
* Paste the content of the clipboard through ICE
|
||||
*/
|
||||
_onPaste : function(evt){
|
||||
if (! this._tracker || ! this._isTracking) {
|
||||
return true;
|
||||
}
|
||||
var data = evt && evt.data;
|
||||
|
||||
if (data && 'html' == data.type && data.dataValue) {
|
||||
try {
|
||||
var doc = this._editor.document.$;
|
||||
var container = doc.createElement("div");
|
||||
container.innerHTML = String(data.dataValue);
|
||||
var childNode = container.firstChild;
|
||||
var newNode = childNode.cloneNode(true);
|
||||
this._tracker.insert(newNode);
|
||||
while (childNode = childNode.nextSibling) {
|
||||
var nextNode = childNode.cloneNode(true);
|
||||
newNode.parentNode.insertBefore(nextNode, newNode.nextSibling);
|
||||
newNode = nextNode;
|
||||
}
|
||||
evt.cancel();
|
||||
return false;
|
||||
}
|
||||
catch (e) {
|
||||
console && console.error && console.error("ice plugin paste:", e);
|
||||
};
|
||||
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the state of multiple commands
|
||||
* @param commands An array of command names or a comma separated string
|
||||
*/
|
||||
_setCommandsState: function(commands, state) {
|
||||
if (typeof(commands) == "string") {
|
||||
commands = commands.split(",");
|
||||
}
|
||||
for (var i = commands.length - 1; i >= 0; --i) {
|
||||
var cmd = this._editor.getCommand(commands[i]);
|
||||
if (cmd) {
|
||||
cmd.setState(state);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onSelectionChanged : function(event) {
|
||||
var inChange = this._isTracking && this._tracker && this._tracker.isInsideChange();
|
||||
var state = inChange && this._canAcceptReject ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
|
||||
this._setCommandsState([LITE.Commands.ACCEPT_ONE, LITE.Commands.REJECT_ONE], state);
|
||||
},
|
||||
|
||||
/**
|
||||
* called when ice fires a change event
|
||||
* @param e jquery event
|
||||
*/
|
||||
_onIceChange : function(e) {
|
||||
var hasChanges = this._isTracking && this._tracker && this._tracker.hasChanges();
|
||||
var state = hasChanges && this._canAcceptReject ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
|
||||
this._setCommandsState([LITE.Commands.ACCEPT_ALL, LITE.Commands.REJECT_ALL], state);
|
||||
this._onSelectionChanged();
|
||||
},
|
||||
|
||||
_commandNameToUIName : function(command) {
|
||||
return command.replace(".", "_");
|
||||
},
|
||||
|
||||
_setPluginFeatures : function(editor, props) {
|
||||
if (! editor || ! editor.filter || ! editor.filter.addFeature) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
function makeClasses(tag) {
|
||||
var classes = [props.deleteClass,props.insertClass];
|
||||
for (var i = 0; i < 10;++i) {
|
||||
classes.push(props.stylePrefix + "-" + i);
|
||||
}
|
||||
return tag + '(' + classes.join(',') + ')';
|
||||
}
|
||||
|
||||
function makeAttributes(tag) {
|
||||
// allowedContent:'span[data-cid,data-time,data-userdata,data-userid,data-username,title]'
|
||||
var attrs = ['title'];
|
||||
for (var key in props) {
|
||||
if (props.hasOwnProperty(key)) {
|
||||
var value = props[key];
|
||||
if ((typeof value == "string") && value.indexOf("data-") == 0) {
|
||||
attrs.push(value);
|
||||
};
|
||||
};
|
||||
};
|
||||
return tag + '[' + attrs.join(',') + ']';
|
||||
}
|
||||
|
||||
var features = [];
|
||||
|
||||
if (props.insertTag) {
|
||||
features.push(makeClasses(props.insertTag));
|
||||
features.push(makeAttributes(props.insertTag));
|
||||
}
|
||||
if (props.deleteTag && props.deleteTag != props.insertTag) {
|
||||
features.push(makeClasses(props.deleteTag));
|
||||
features.push(makeAttributes(props.deleteTag));
|
||||
}
|
||||
|
||||
for (var i = 0; i < features.length; ++i) {
|
||||
editor.filter.addFeature({ allowedContent: features[i] });
|
||||
}
|
||||
|
||||
/* ed.filter.addFeature({
|
||||
allowedContent:'span(ice-ins,ice-del,cts-1,cts-2,cts-3)'
|
||||
});
|
||||
ed.filter.addFeature({
|
||||
allowedContent:'span[data-cid,data-time,data-userdata,data-userid,data-username,title]'
|
||||
}); */
|
||||
}
|
||||
catch (e){
|
||||
console && console.error && console.error(e);
|
||||
}
|
||||
},
|
||||
|
||||
_ieFix : function() {
|
||||
/* Begin fixes for IE */
|
||||
Function.prototype.bind = Function.prototype.bind || function () {
|
||||
"use strict";
|
||||
var fn = this, args = Array.prototype.slice.call(arguments),
|
||||
object = args.shift();
|
||||
return function () {
|
||||
return fn.apply(object,
|
||||
args.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
};
|
||||
|
||||
/* Mozilla fix for MSIE indexOf */
|
||||
Array.prototype.indexOf = Array.prototype.indexOf || function (searchElement /*, fromIndex */) {
|
||||
"use strict";
|
||||
if (this == null) {
|
||||
throw new TypeError();
|
||||
}
|
||||
var t = Object(this);
|
||||
var len = t.length >>> 0;
|
||||
if (len === 0) {
|
||||
return -1;
|
||||
}
|
||||
var n = 0;
|
||||
if (arguments.length > 1) {
|
||||
n = Number(arguments[1]);
|
||||
if (n != n) { // shortcut for verifying if it's NaN
|
||||
n = 0;
|
||||
} else if (n != 0 && n != Infinity && n != -Infinity) {
|
||||
n = (n > 0 || -1) * Math.floo1r(Math.abs(n));
|
||||
}
|
||||
}
|
||||
if (n >= len) {
|
||||
return -1;
|
||||
}
|
||||
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
|
||||
for (; k < len; k++) {
|
||||
if (k in t && t[k] === searchElement) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
Array.prototype.lastIndexOf = Array.prototype.indexOf || function (searchElement) {
|
||||
"use strict";
|
||||
if (this == null) {
|
||||
throw new TypeError();
|
||||
}
|
||||
var t = Object(this);
|
||||
var len = t.length >>> 0;
|
||||
while(--len >= 0) {
|
||||
if (len in t && t[len] === searchElement) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
});})();
|
||||
19
lib/ckeditor4/plugins/lite/plugins/IceAddTitlePlugin/IceAddTitlePlugin.js
Executable file
19
lib/ckeditor4/plugins/lite/plugins/IceAddTitlePlugin/IceAddTitlePlugin.js
Executable file
@@ -0,0 +1,19 @@
|
||||
(function() {
|
||||
|
||||
var exports = this, IceAddTitlePlugin;
|
||||
|
||||
IceAddTitlePlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
};
|
||||
|
||||
IceAddTitlePlugin.prototype = {
|
||||
nodeCreated: function(node, option) {
|
||||
node.setAttribute('title', (option.action || 'Modified') + ' by ' + node.getAttribute(this._ice.userNameAttribute)
|
||||
+ ' - ' + ice.dom.date('m/d/Y h:ia', parseInt(node.getAttribute(this._ice.timeAttribute))));
|
||||
}
|
||||
};
|
||||
|
||||
ice.dom.noInclusionInherits(IceAddTitlePlugin, ice.IcePlugin);
|
||||
exports._plugin.IceAddTitlePlugin = IceAddTitlePlugin;
|
||||
|
||||
}).call(this.ice);
|
||||
510
lib/ckeditor4/plugins/lite/plugins/IceCopyPastePlugin/IceCopyPastePlugin.js
Executable file
510
lib/ckeditor4/plugins/lite/plugins/IceCopyPastePlugin/IceCopyPastePlugin.js
Executable file
@@ -0,0 +1,510 @@
|
||||
(function() {
|
||||
|
||||
var exports = this, IceCopyPastePlugin;
|
||||
|
||||
IceCopyPastePlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
this._tmpNode = null;
|
||||
this._tmpNodeTagName = 'icepaste';
|
||||
this._pasteId = 'icepastediv';
|
||||
var self = this;
|
||||
|
||||
// API
|
||||
|
||||
// 'formatted' - paste will be MS Word cleaned.
|
||||
// 'formattedClean' - paste will be MS Word cleaned, insert and
|
||||
// delete tags will be removed keeping insert content in place,
|
||||
// and tags not found in `preserve` will be stripped.
|
||||
this.pasteType = 'formattedClean';
|
||||
|
||||
// Subset of tags that will not be stripped when pasteType
|
||||
// is set to 'formattedClean'. Parameter is of type string with
|
||||
// comma delimited tag and attribute definitions. For example:
|
||||
// 'p,a[href],i[style|title],span[*]'
|
||||
// Would allow `p`, `a`, `i` and `span` tags. The attributes for
|
||||
// each one of these tags would be cleaned as follows: `p` tags
|
||||
// would have all attributes removed, `a` tags will have all but
|
||||
// `href` attributes removed, `i` tags will have all but `style`
|
||||
// and `title` attributes removed, and `span` tags will keep all attributes.
|
||||
this.preserve = 'p';
|
||||
|
||||
// Callback triggered before any paste cleaning happens
|
||||
this.beforePasteClean = function(body) { return body; };
|
||||
|
||||
// Callback triggered at the end of the paste cleaning
|
||||
this.afterPasteClean = function(body) { return body; };
|
||||
|
||||
// Event Listeners
|
||||
ice_instance.element.oncopy = function() { return self.handleCopy.apply(self); };
|
||||
// ice_instance.element.oncut = function() { return self.handleCut.apply(self); };
|
||||
|
||||
// We can't listen for `onpaste` unless we use an algorithm that temporarily switches
|
||||
// out the body and lets the browser paste there (found it hard to maintain in mce).
|
||||
// Instead, we'll watch the keydown event and handle paste with a typical temp element
|
||||
// algorithm, which means that pasting from the context menu won't work.
|
||||
};
|
||||
|
||||
IceCopyPastePlugin.prototype = {
|
||||
|
||||
setSettings: function(settings) {
|
||||
settings = settings || {};
|
||||
ice.dom.extend(this, settings);
|
||||
|
||||
this.preserve += ',' + this._tmpNodeTagName;
|
||||
this.setupPreserved();
|
||||
},
|
||||
|
||||
keyDown: function(e) {
|
||||
if (e.metaKey !== true && e.ctrlKey !== true) {
|
||||
return;
|
||||
}
|
||||
if(e.keyCode == 86) {
|
||||
this.handlePaste();
|
||||
}
|
||||
else if(e.keyCode == 88) {
|
||||
this.handleCut();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
keyPress: function(e){
|
||||
var c = null;
|
||||
if (e.which == null) {
|
||||
// IE.
|
||||
c = String.fromCharCode(e.keyCode);
|
||||
} else if (e.which > 0) {
|
||||
c = String.fromCharCode(e.which);
|
||||
}
|
||||
var self = this;
|
||||
if(this.cutElement && c === 'x'){
|
||||
if(ice.dom.isBrowser("webkit")){
|
||||
self.cutElement.focus();
|
||||
}
|
||||
} else if (c === 'v'){
|
||||
if(ice.dom.isBrowser("webkit")){
|
||||
var div = document.getElementById(self._pasteId);
|
||||
div.focus();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
handleCopy: function(e) {},
|
||||
|
||||
// Inserts a temporary placeholder for the current range and removes
|
||||
// the contents of the ice element body and calls a paste handler.
|
||||
handlePaste: function(e) {
|
||||
|
||||
var range = this._ice.getCurrentRange();
|
||||
|
||||
if(!range.collapsed) {
|
||||
if(this._ice.isTracking) {
|
||||
this._ice.deleteContents();
|
||||
range = range.cloneRange();
|
||||
} else {
|
||||
range.deleteContents();
|
||||
range.collapse(true);
|
||||
}
|
||||
}
|
||||
|
||||
if(this._ice.isTracking)
|
||||
this._ice._moveRangeToValidTrackingPos(range);
|
||||
|
||||
if(range.startContainer == this._ice.element) {
|
||||
// Fix a potentially empty body with a bad selection
|
||||
var firstBlock = ice.dom.find(this._ice.element, this._ice.blockEl)[0];
|
||||
if(!firstBlock) {
|
||||
firstBlock = ice.dom.create('<' + this._ice.blockEl + ' ><br/></' + this._ice.blockEl + '>');
|
||||
this._ice.element.appendChild(firstBlock);
|
||||
}
|
||||
range.setStart(firstBlock, 0);
|
||||
range.collapse(true);
|
||||
this._ice.env.selection.addRange(range);
|
||||
}
|
||||
|
||||
|
||||
this._tmpNode = this._ice.env.document.createElement(this._tmpNodeTagName);
|
||||
range.insertNode(this._tmpNode);
|
||||
|
||||
switch (this.pasteType) {
|
||||
case 'formatted':
|
||||
this.setupPaste();
|
||||
break;
|
||||
case 'formattedClean':
|
||||
this.setupPaste(true);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// Create a temporary div and set focus to it so that the browser can paste into it.
|
||||
// Set a timeout to push a paste handler on to the end of the execution stack.
|
||||
setupPaste: function(stripTags) {
|
||||
var div = this.createDiv(this._pasteId), self = this;
|
||||
|
||||
div.onpaste = function() {
|
||||
setTimeout(function(){
|
||||
self.handlePasteValue(stripTags);
|
||||
},0);
|
||||
};
|
||||
|
||||
if(this._ice.env.frame){
|
||||
if(ice.dom.isBrowser("webkit")){
|
||||
div.blur();
|
||||
setTimeout(function(){
|
||||
div.focus();
|
||||
}, 0);
|
||||
} else {
|
||||
div.focus();
|
||||
}
|
||||
}
|
||||
else{
|
||||
div.focus();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// By the time we get here, the pasted content will already be in the body. Extract the
|
||||
// paste, format it, remove any Microsoft or extraneous tags outside of `this.preserve`
|
||||
// and merge the pasted content into the original fragment body.
|
||||
handlePasteValue: function(stripTags) {
|
||||
// Get the pasted content.
|
||||
var html = ice.dom.getHtml(document.getElementById(this._pasteId));
|
||||
var childBlocks = ice.dom.children('<div>' + html + '</div>', this._ice.blockEl);
|
||||
if(childBlocks.length === 1 && ice.dom.getNodeTextContent('<div>' + html + '</div>') === ice.dom.getNodeTextContent(childBlocks)) {
|
||||
html = ice.dom.getHtml(html);
|
||||
}
|
||||
|
||||
html = this.beforePasteClean.call(this, html);
|
||||
|
||||
if(stripTags) {
|
||||
|
||||
// Strip out change tracking tags.
|
||||
html = this._ice.getCleanContent(html);
|
||||
html = this.stripPaste(html);
|
||||
}
|
||||
|
||||
html = this.afterPasteClean.call(this, html);
|
||||
html = ice.dom.trim(html);
|
||||
var range = this._ice.getCurrentRange();
|
||||
range.setStartAfter(this._tmpNode);
|
||||
range.collapse(true);
|
||||
|
||||
var innerBlock = null, lastEl = null, newEl = null;
|
||||
var fragment = range.createContextualFragment(html);
|
||||
var changeid = this._ice.startBatchChange();
|
||||
|
||||
// If fragment contains block level elements, most likely we will need to
|
||||
// do some splitting so we do not have P tags in P tags, etc. Split the
|
||||
// container from current selection and then insert paste contents after it.
|
||||
if(ice.dom.hasBlockChildren(fragment)) {
|
||||
// Split from current selection.
|
||||
var block = ice.dom.isChildOfTagName(this._tmpNode, this._ice.blockEl);
|
||||
range.setEndAfter(block.lastChild);
|
||||
this._ice.selection.addRange(range);
|
||||
var contents = range.extractContents();
|
||||
var newblock = this._ice.env.document.createElement(this._ice.blockEl);
|
||||
newblock.appendChild(contents);
|
||||
ice.dom.insertAfter(block, newblock);
|
||||
|
||||
range.setStart(newblock, 0);
|
||||
range.collapse(true);
|
||||
this._ice.selection.addRange(range);
|
||||
var prevBlock = range.startContainer;
|
||||
|
||||
// Paste all of the children in the fragment.
|
||||
while(fragment.firstChild) {
|
||||
if(fragment.firstChild.nodeType === 3 && !jQuery.trim(fragment.firstChild.nodeValue)) {
|
||||
fragment.removeChild(fragment.firstChild);
|
||||
continue;
|
||||
}
|
||||
// We may have blocks with text nodes at the beginning or end. For example, this paste:
|
||||
// textnode <p>blocktext</p> <p>blocktext</p> moretext
|
||||
// In which case we wrap the leading or trailing text nodes in blocks.
|
||||
if(ice.dom.isBlockElement(fragment.firstChild)) {
|
||||
if(fragment.firstChild.textContent !== "") {
|
||||
innerBlock = null;
|
||||
var insert = null;
|
||||
if(this._ice.isTracking) {
|
||||
insert = this._ice.createIceNode('insertType');
|
||||
this._ice.addChange('insertType', [insert]);
|
||||
newEl = document.createElement(fragment.firstChild.tagName);
|
||||
insert.innerHTML = fragment.firstChild.innerHTML;
|
||||
newEl.appendChild(insert);
|
||||
} else {
|
||||
insert = newEl = document.createElement(fragment.firstChild.tagName);
|
||||
newEl.innerHTML = fragment.firstChild.innerHTML;
|
||||
}
|
||||
lastEl = insert;
|
||||
ice.dom.insertBefore(prevBlock, newEl);
|
||||
}
|
||||
fragment.removeChild(fragment.firstChild);
|
||||
} else {
|
||||
if(!innerBlock) {
|
||||
// Create a new block and append an insert
|
||||
newEl = document.createElement(this._ice.blockEl);
|
||||
ice.dom.insertBefore(prevBlock, newEl);
|
||||
if(this._ice.isTracking) {
|
||||
innerBlock = this._ice.createIceNode('insertType');
|
||||
this._ice.addChange('insertType', [innerBlock]);
|
||||
newEl.appendChild(innerBlock);
|
||||
} else {
|
||||
innerBlock = newEl;
|
||||
}
|
||||
}
|
||||
lastEl = innerBlock;
|
||||
innerBlock.appendChild(fragment.removeChild(fragment.firstChild));
|
||||
}
|
||||
}
|
||||
if (!newblock.textContent) {
|
||||
newblock.parentNode.removeChild(newblock);
|
||||
}
|
||||
|
||||
} else {
|
||||
if(this._ice.isTracking) {
|
||||
newEl = this._ice.createIceNode('insertType', fragment);
|
||||
this._ice.addChange('insertType', [newEl]);
|
||||
range.insertNode(newEl);
|
||||
lastEl = newEl;
|
||||
} else {
|
||||
var child;
|
||||
while((child = fragment.firstChild)) {
|
||||
range.insertNode(child);
|
||||
range.setStartAfter(child);
|
||||
range.collapse(true);
|
||||
lastEl = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._ice.endBatchChange(changeid);
|
||||
this._cleanup(lastEl);
|
||||
},
|
||||
|
||||
|
||||
createDiv: function(id) {
|
||||
var oldEl = ice.dom.getId(id);
|
||||
if(oldEl) {
|
||||
ice.dom.remove(oldEl);
|
||||
}
|
||||
|
||||
var div = this._ice.env.document.createElement('div');
|
||||
div.id = id;
|
||||
div.setAttribute('contentEditable', true);
|
||||
ice.dom.setStyle(div, 'width', '1px');
|
||||
ice.dom.setStyle(div, 'height', '1px');
|
||||
ice.dom.setStyle(div, 'overflow', 'hidden');
|
||||
ice.dom.setStyle(div, 'position', 'fixed');
|
||||
ice.dom.setStyle(div, 'top', '10px');
|
||||
ice.dom.setStyle(div, 'left', '10px');
|
||||
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
},
|
||||
|
||||
// Intercepts cut operation and handles by creating an editable div, copying the current selection
|
||||
// into it, deleting the current selection with track changes, and selecting the contents in the
|
||||
// editable div.
|
||||
handleCut: function() {
|
||||
this.cutElementId = 'icecut';
|
||||
this.cutElement = this.createDiv(this.cutElementId);
|
||||
var range = this._ice.getCurrentRange();
|
||||
if(range.collapsed) return;
|
||||
var html = range.getHTMLContents();
|
||||
if (this._ice.isTracking) this._ice.deleteContents();
|
||||
else range.deleteContents();
|
||||
// var crange = range.cloneRange();
|
||||
// var crange = rangy.createRange();
|
||||
var crange = document.createRange();
|
||||
// crange.collapse(true);
|
||||
this.cutElement.innerHTML = html;
|
||||
|
||||
crange.setStart(this.cutElement.firstChild, 0);
|
||||
crange.setEndAfter(this.cutElement.lastChild);
|
||||
var self = this;
|
||||
|
||||
// this.cutElement.blur();
|
||||
if(this._ice.env.frame){
|
||||
// TINYMCE
|
||||
setTimeout(function(){
|
||||
self.cutElement.focus();
|
||||
|
||||
// After the browser cuts out of the `cutElement`, reset the range and remove the cut element.
|
||||
setTimeout(function() {
|
||||
ice.dom.remove(self.cutElement);
|
||||
range.setStart(range.startContainer, range.startOffset);
|
||||
range.collapse(false);
|
||||
self._ice.env.selection.addRange(range);
|
||||
// Set focus back to ice element.
|
||||
if(self._ice.env.frame) {
|
||||
self._ice.env.frame.contentWindow.focus();
|
||||
} else {
|
||||
self._ice.element.focus();
|
||||
}
|
||||
}, 100);
|
||||
}, 0);
|
||||
} else {
|
||||
// Vanilla Div
|
||||
setTimeout(function(){
|
||||
self.cutElement.focus();
|
||||
|
||||
// After the browser cuts out of the `cutElement`, reset the range and remove the cut element.
|
||||
setTimeout(function() {
|
||||
range.setStart(range.startContainer, range.startOffset);
|
||||
range.collapse(false);
|
||||
self._ice.env.selection.addRange(range);
|
||||
// Set focus back to ice element.
|
||||
if(self._ice.env.frame) {
|
||||
self._ice.env.frame.contentWindow.focus();
|
||||
} else {
|
||||
self._ice.element.focus();
|
||||
}
|
||||
}, 100);
|
||||
}, 0);
|
||||
|
||||
if(ice.dom.getWebkitType() === "chrome"){
|
||||
self.cutElement.focus();
|
||||
}
|
||||
}
|
||||
self._ice.env.selection.addRange(crange);
|
||||
},
|
||||
|
||||
|
||||
// Strips ice change tracking tags, Microsoft Word styling/content, and any
|
||||
// tags and attributes not found in `preserve` from the given `content`.
|
||||
stripPaste: function(content) {
|
||||
// Clean word stuff out and strip tags that are not in `this.preserve`.
|
||||
content = this._cleanWordPaste(content);
|
||||
content = this.cleanPreserved(content);
|
||||
return content;
|
||||
},
|
||||
|
||||
// Parses `preserve` to setup `_tags` with a comma delimited list of all of the
|
||||
// defined tags, and the `_attributesMap` with a mapping between the allowed tags and
|
||||
// an array of their allowed attributes. For example, given this value:
|
||||
// `preserve` = 'p,a[href|class],span[*]'
|
||||
// The following will result:
|
||||
// `_tags` = 'p,a,span'
|
||||
// `_attributesMap` = ['p' => [], 'a' => ['href', 'class'], 'span' => ['*']]
|
||||
setupPreserved: function() {
|
||||
var self = this;
|
||||
this._tags = '';
|
||||
this._attributesMap = [];
|
||||
|
||||
ice.dom.each(this.preserve.split(','), function(i, tagAttr) {
|
||||
// Extract the tag and attributes list
|
||||
tagAttr.match(/(\w+)(\[(.+)\])?/);
|
||||
var tag = RegExp.$1;
|
||||
var attr = RegExp.$3;
|
||||
|
||||
if(self._tags) self._tags += ',';
|
||||
self._tags += tag.toLowerCase();
|
||||
self._attributesMap[tag] = attr.split('|');
|
||||
});
|
||||
},
|
||||
|
||||
// Cleans the given `body` by removing any tags not found in `_tags` and replacing them with
|
||||
// their inner contents, and removes attributes from any tags that aren't mapped in `_attributesMap`.
|
||||
cleanPreserved: function(body) {
|
||||
var self = this;
|
||||
var bodyel = this._ice.env.document.createElement('div');
|
||||
bodyel.innerHTML = body;
|
||||
|
||||
// Strip out any tags not found in `this._tags`, replacing the tags with their inner contents.
|
||||
bodyel = ice.dom.stripEnclosingTags(bodyel, this._tags);
|
||||
|
||||
// Strip out any attributes from the allowed set of tags that don't match what is in the `_attributesMap`
|
||||
ice.dom.each(ice.dom.find(bodyel, this._tags), function(i, el) {
|
||||
if (ice.dom.hasClass(el, 'skip-clean')) {
|
||||
return true;
|
||||
}
|
||||
var tag = el.tagName.toLowerCase();
|
||||
var attrMatches = self._attributesMap[tag];
|
||||
|
||||
// Kleene star - keep all of the attributes for this tag.
|
||||
if(attrMatches[0] && attrMatches[0] === '*')
|
||||
return true;
|
||||
|
||||
// Remove any foreign attributes that do not match the map.
|
||||
if(el.hasAttributes()) {
|
||||
var attributes = el.attributes;
|
||||
for(var i = attributes.length - 1; i >= 0; i--) {
|
||||
if(!ice.dom.inArray(attributes[i].name, attrMatches)) {
|
||||
el.removeAttribute(attributes[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return bodyel.innerHTML;
|
||||
},
|
||||
|
||||
_cleanWordPaste: function(content) {
|
||||
// Meta and link tags.
|
||||
content = content.replace(/<(meta|link)[^>]+>/g, "");
|
||||
|
||||
// Comments.
|
||||
content = content.replace(/<!--(.|\s)*?-->/g, '');
|
||||
|
||||
// Remove style tags.
|
||||
content = content.replace(/<style>[\s\S]*?<\/style>/g, '');
|
||||
|
||||
// Remove span and o:p etc. tags.
|
||||
//content = content.replace(/<\/?span[^>]*>/gi, "");
|
||||
content = content.replace(/<\/?\w+:[^>]*>/gi, '' );
|
||||
|
||||
// Remove XML tags.
|
||||
content = content.replace(/<\\?\?xml[^>]*>/gi, '');
|
||||
|
||||
// Generic cleanup.
|
||||
content = this._cleanPaste(content);
|
||||
|
||||
// Remove class, lang and style attributes.
|
||||
content = content.replace(/<(\w[^>]*) (lang)=([^ |>]*)([^>]*)/gi, "<$1$4");
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
_cleanPaste: function(content) {
|
||||
// Some generic content cleanup. Change all b/i tags to strong/em.
|
||||
content = content.replace(/<b(\s+|>)/g, "<strong$1");
|
||||
content = content.replace(/<\/b(\s+|>)/g, "</strong$1");
|
||||
content = content.replace(/<i(\s+|>)/g, "<em$1");
|
||||
content = content.replace(/<\/i(\s+|>)/g, "</em$1");
|
||||
return content;
|
||||
},
|
||||
|
||||
_cleanup: function(moveTo) {
|
||||
try {
|
||||
moveTo = moveTo && moveTo.lastChild || moveTo || this._tmpNode;
|
||||
// Move the range to the end of moveTo so that the cursor will be at the end of the paste.
|
||||
var range = this._ice.getCurrentRange();
|
||||
range.setStartAfter(moveTo);
|
||||
range.collapse(true);
|
||||
this._ice.selection.addRange(range);
|
||||
// Set focus back to ice element.
|
||||
if(this._ice.env.frame) {
|
||||
this._ice.env.frame.contentWindow.focus();
|
||||
} else {
|
||||
this._ice.element.focus();
|
||||
}
|
||||
// Kill the tmp node.
|
||||
this._tmpNode.parentNode.removeChild(this._tmpNode);
|
||||
this._tmpNode = null;
|
||||
// Kill any empty change nodes.
|
||||
var ins = this._ice.env.document.getElementsByClassName(this._ice.changeTypes['insertType'].alias);
|
||||
for(var i = 0; i < ins.length; i++) {
|
||||
if(!ins[i].textContent) {
|
||||
if(ins[i].parentNode) {
|
||||
ins[i].parentNode.removeChild(ins[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
window.console && console.error(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ice.dom.noInclusionInherits(IceCopyPastePlugin, ice.IcePlugin);
|
||||
exports._plugin.IceCopyPastePlugin = IceCopyPastePlugin;
|
||||
|
||||
}).call(this.ice);
|
||||
76
lib/ckeditor4/plugins/lite/plugins/IceEmdashPlugin/IceEmdashPlugin.js
Executable file
76
lib/ckeditor4/plugins/lite/plugins/IceEmdashPlugin/IceEmdashPlugin.js
Executable file
@@ -0,0 +1,76 @@
|
||||
(function() {
|
||||
|
||||
var exports = this;
|
||||
|
||||
/**
|
||||
* When active, this plugin will convert two successively typed dashes, within
|
||||
* the ice block element, into an emdash.
|
||||
*/
|
||||
var IceEmdashPlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
};
|
||||
|
||||
IceEmdashPlugin.prototype = {
|
||||
|
||||
keyDown: function(e) {
|
||||
// Catch dashes.
|
||||
if(ice.dom.isBrowser('mozilla')) {
|
||||
var version = parseInt(ice.dom.browser().version);
|
||||
if ( (version > 14 && e.keyCode === 173) || (version <= 14 && e.keyCode === 109) ) {
|
||||
return this.convertEmdash(e);
|
||||
}
|
||||
} else if(e.keyCode === 189) {
|
||||
return this.convertEmdash(e);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
convertEmdash: function(e) {
|
||||
var range = this._ice.getCurrentRange();
|
||||
if(range.collapsed) {
|
||||
try {
|
||||
// Move the start back one character so we can enclose the range around the previous character to check if it is a dash
|
||||
range.moveStart(ice.dom.CHARACTER_UNIT, -1);
|
||||
// Get the parent block element for the start and end containers
|
||||
var startBlock = ice.dom.getParents(range.startContainer, this._ice.blockEl)[0];
|
||||
var endBlock = ice.dom.getParents(range.endContainer, this._ice.blockEl)[0];
|
||||
// Make sure that the start and end containers aren't in different blocks, or that the start isn't in a delete.
|
||||
if(startBlock === endBlock && !this._ice.getIceNode(range.startContainer, 'deleteType')) {
|
||||
// Get the last character and check to see if it is a dash.
|
||||
c = range.toHtml();
|
||||
if(c === '-') {
|
||||
// Extract the last character/dash and insert an emdash
|
||||
range.extractContents();
|
||||
range.collapse();
|
||||
var mdash = this._ice.env.document.createTextNode('\u2014');
|
||||
if (this._ice.isTracking) {
|
||||
this._ice._insertNode(mdash, range);
|
||||
} else {
|
||||
range.insertNode(mdash);
|
||||
/* TO be reverted once mozilla fixes FF 15 issue */
|
||||
range.setStart(mdash, 1);
|
||||
range.collapse(true);
|
||||
/* FINISH revert */
|
||||
}
|
||||
/* TO be reverted once mozilla fixes FF 15 issue
|
||||
range = this._ice.getCurrentRange();
|
||||
range.moveStart(ice.dom.CHARACTER_UNIT, 1);
|
||||
range.collapse(true);
|
||||
this._ice.env.selection.addRange(range);
|
||||
*/
|
||||
this._ice._preventKeyPress = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
range.collapse();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ice.dom.noInclusionInherits(IceEmdashPlugin, ice.IcePlugin);
|
||||
exports._plugin.IceEmdashPlugin = IceEmdashPlugin;
|
||||
|
||||
}).call(this.ice);
|
||||
184
lib/ckeditor4/plugins/lite/plugins/IceSmartQuotesPlugin/IceSmartQuotesPlugin.js
Executable file
184
lib/ckeditor4/plugins/lite/plugins/IceSmartQuotesPlugin/IceSmartQuotesPlugin.js
Executable file
@@ -0,0 +1,184 @@
|
||||
(function() {
|
||||
|
||||
var exports = this, ice = this.ice;
|
||||
|
||||
var IceSmartQuotesPlugin = function(ice_instance) {
|
||||
this._ice = ice_instance;
|
||||
};
|
||||
|
||||
IceSmartQuotesPlugin.prototype = {
|
||||
|
||||
/**
|
||||
* Finds each block in `element` and converts quotes into smart quotes.
|
||||
*/
|
||||
convert: function(element) {
|
||||
var self = this;
|
||||
try {
|
||||
self._ice.placeholdDeletes();
|
||||
ice.dom.each(element.getElementsByTagName(this._ice.blockEl), function(i, el) {
|
||||
self._convertBlock(el);
|
||||
});
|
||||
} catch(e) {
|
||||
window.console && console.error(e);
|
||||
} finally {
|
||||
self._ice.revertDeletePlaceholders();
|
||||
}
|
||||
},
|
||||
|
||||
// Converts the quotes in the given element to smart quotes.
|
||||
_convertBlock: function(el) {
|
||||
|
||||
// If there are less than 2 characters, we don't have enough to go on.
|
||||
if (ice.dom.getNodeTextContent(el) < 2) return;
|
||||
|
||||
var previous, current, next, data, html = '', getNextChar,
|
||||
regularSingle = "'",
|
||||
regularDouble = '"',
|
||||
smartSingleLeft = String.fromCharCode(8216), // aka - open curly single quote
|
||||
smartSingleRight = String.fromCharCode(8217), // aka - close curly single quote
|
||||
smartDoubleLeft = String.fromCharCode(8220), // aka - open curly double quote
|
||||
smartDoubleRight = String.fromCharCode(8221), // aka - close curly double quote
|
||||
isDigit = function(c) { return /\d/.test(c); },
|
||||
isChar = function(c) { return /\w/.test(c); },
|
||||
isSpace = function(c) { return c === String.fromCharCode(160) || c === String.fromCharCode(32); },
|
||||
isStartChar = function(c) { return isSpace(c) || c === '('; },
|
||||
isEndChar = function(c) { return isSpace(c) || c == null || c === ';' || c === ')' || c == '.' || c === '!' || c === ',' || c === '?' || c === ':'; },
|
||||
isNonSpace = function(c) { return !isSpace(c); },
|
||||
isDouble = function(c) { return c === regularDouble || c === smartDoubleLeft || c === smartDoubleRight; },
|
||||
isSingle = function(c) { return c === regularSingle || c === smartSingleLeft || c === smartSingleRight; };
|
||||
|
||||
// Split the html into array allocations with the following criteria:
|
||||
// html tags: starts with "<" and ends with ">"
|
||||
// html entities: starts with "&" and ends with ";"
|
||||
// characters: any character outside of an html tag or entity
|
||||
// So the following html:
|
||||
// n&ce <b id="bold">test</b>
|
||||
// Would split into the following array:
|
||||
// ['n', '&', 'c', 'e', ' ', '<b id="bold">', 't', 'e', 's', 't', '</b>'];
|
||||
data = ice.dom.getHtml(el).match(/(<("[^"]*"|'[^']*'|[^'">])*>|&.*;|.)/g);
|
||||
|
||||
// Searches through the `data` array from the given index a given number of
|
||||
// characters forward or backward and returns the found character.
|
||||
getNextChar = function(data, fromIndex, nCharacters) {
|
||||
var dLength = data.length,
|
||||
addWith = nCharacters < 0 ? -1 : 1;
|
||||
|
||||
return (function getChar(data, fromIndex, nCharacters) {
|
||||
// Base case - did we move outside of the bounds of the data array?
|
||||
if (fromIndex < 0 || fromIndex >= dLength) return null;
|
||||
|
||||
var next = data[fromIndex + addWith];
|
||||
|
||||
// If we find a character and we have moved through the
|
||||
// nCharacters, then the recursion is done.
|
||||
if (next && next.length == 1) {
|
||||
nCharacters += (addWith * -1);
|
||||
if (!nCharacters) return next;
|
||||
}
|
||||
return getChar(data, fromIndex + addWith, nCharacters);
|
||||
|
||||
})(data, fromIndex, nCharacters);
|
||||
};
|
||||
|
||||
ice.dom.each(data, function(pos, val) {
|
||||
// Convert space entities so that they can be processed as normal characters.
|
||||
if (val == ' ') val = data[pos] = ' ';
|
||||
|
||||
// If the val is a character, then examine the surroundings
|
||||
// and convert smart quotes, if necessary.
|
||||
if (val.length == 1) {
|
||||
|
||||
// Keep convenience pointers to the previous, current and next characters.
|
||||
previous = getNextChar(data, pos, -1);
|
||||
current = val;
|
||||
next = getNextChar(data, pos, 1);
|
||||
|
||||
switch (current) {
|
||||
|
||||
/**
|
||||
* Conversion Rules:
|
||||
* ----------------
|
||||
*
|
||||
* START: assign smart left/open
|
||||
* [SPACE|START_PARENTHESIS]'word
|
||||
* [SPACE|START_PARENTHESIS]"word
|
||||
*
|
||||
* END: assign smart right/close
|
||||
* word'[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
|
||||
* word"[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
|
||||
*
|
||||
* PLURAL_CONTRACTION: assign smart right/close
|
||||
* Matt's
|
||||
* can't
|
||||
* O'Reilly
|
||||
*
|
||||
* YEAR_ABBREVIATION: assign smart right/close
|
||||
* [SPACE|NULL]'99[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
|
||||
*
|
||||
* NESTED_START: assign smart left/open
|
||||
* [SPACE|NULL]"[SPACE]'word
|
||||
*
|
||||
* NESTED_END: assign smart left/open
|
||||
* word'[SPACE]"[SPACE|SEMICOLON|COLON|PERIOD|COMMA|EXCLAMATION_MARK|QUESTION_MARK|END_PARENTHESIS|NULL]
|
||||
*
|
||||
* Notes:
|
||||
* - The following will not be converted correctly - ...word 'Til Death - it should
|
||||
* get a right/close smart quote, but will get a left/open.
|
||||
* - Distinguishing between year abbreviation, '99, and when to use an open single quote
|
||||
* could fail if a single quoted region starts with a double digit number - '99 problems'
|
||||
* - Since they are a rare case and there are many permutations, measurements are not being
|
||||
* handled (6'8", 6' 8", 6', 8").
|
||||
*/
|
||||
|
||||
// Convert smart single quotes to non-smart quote and fall through to single quote
|
||||
// handling, in case the context has changed and we need to update the smart quote.
|
||||
case smartSingleLeft:
|
||||
case smartSingleRight:
|
||||
current = regularSingle;
|
||||
case regularSingle:
|
||||
// YEAR_ABBREVIATION - look 2 ahead to see if there are two digits in a row - not fool proof
|
||||
if ((previous == null || isSpace(previous)) && isDigit(next) && isDigit(getNextChar(data, pos, 2)) && isEndChar(getNextChar(data, pos, 3)))
|
||||
current = smartSingleRight;
|
||||
// START
|
||||
else if (previous == null || (isStartChar(previous) && isNonSpace(next)))
|
||||
current = smartSingleLeft;
|
||||
// END
|
||||
else if (next == null || (isNonSpace(previous) && isEndChar(next)))
|
||||
current = smartSingleRight;
|
||||
// PLURAL_CONTRACTION
|
||||
else if (isChar(previous) && isChar(next))
|
||||
current = smartSingleRight;
|
||||
break;
|
||||
|
||||
// Convert smart double quotes to non-smart quote and fall through to double quote
|
||||
// handling, in case the context has changed and we need to update the smart quote.
|
||||
case smartDoubleLeft:
|
||||
case smartDoubleRight:
|
||||
current = regularDouble;
|
||||
case regularDouble:
|
||||
// NESTED_END
|
||||
if (isEndChar(next) && isSpace(previous) && isSingle(getNextChar(data, pos, -2)))
|
||||
current = smartDoubleRight;
|
||||
// START
|
||||
else if (previous == null || (isStartChar(previous) && isNonSpace(next)))
|
||||
current = smartDoubleLeft;
|
||||
// END
|
||||
else if (next == null || (isNonSpace(previous) && isEndChar(next)))
|
||||
current = smartDoubleRight;
|
||||
// NESTED_START
|
||||
else if ((previous == null || isSpace(previous)) && (isSpace(next) && isSingle(getNextChar(data, pos, 1))))
|
||||
current = smartDoubleLeft;
|
||||
break;
|
||||
}
|
||||
if (current != null) data[pos] = current;
|
||||
}
|
||||
});
|
||||
|
||||
ice.dom.setHtml(el, data.join(''));
|
||||
}
|
||||
};
|
||||
|
||||
ice.dom.noInclusionInherits(IceSmartQuotesPlugin, ice.IcePlugin);
|
||||
exports.ice._plugin.IceSmartQuotesPlugin = IceSmartQuotesPlugin;
|
||||
|
||||
}).call(this);
|
||||
Reference in New Issue
Block a user