{I"
class:ETI"BundledAsset; FI"logical_path; TI"blocky/summernote.js; FI"
pathname; TI"Y/Users/brian/github/codelation/blocky/vendor/assets/javascripts/blocky/summernote.js; FI"content_type; TI"application/javascript; TI"
mtime; Tl+ไySI"length; TiฃไI"digest; TI"%be701c31cef9643139b6f85fcaad7104; FI"source; TI"ฃไ/**
* Super simple wysiwyg editor on Bootstrap v0.5.2
* http://hackerwins.github.io/summernote/
*
* summernote.js
* Copyright 2013 Alan Hong. and outher contributors
* summernote may be freely distributed under the MIT license./
*
* Date: 2014-05-16T11:28Z
*/
(function (factory) {
/* global define */
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery', 'codemirror'], factory);
} else {
// Browser globals: jQuery, CodeMirror
factory(window.jQuery, window.CodeMirror);
}
}(function ($, CodeMirror) {
if ('function' !== typeof Array.prototype.reduce) {
/**
* Array.prototype.reduce fallback
*
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
*/
Array.prototype.reduce = function (callback, optInitialValue) {
var idx, value, length = this.length >>> 0, isValueSet = false;
if (1 < arguments.length) {
value = optInitialValue;
isValueSet = true;
}
for (idx = 0; length > idx; ++idx) {
if (this.hasOwnProperty(idx)) {
if (isValueSet) {
value = callback(value, this[idx], idx, this);
} else {
value = this[idx];
isValueSet = true;
}
}
}
if (!isValueSet) {
throw new TypeError('Reduce of empty array with no initial value');
}
return value;
};
}
/**
* Object which check platform and agent
*/
var agent = {
bMac: navigator.appVersion.indexOf('Mac') > -1,
bMSIE: navigator.userAgent.indexOf('MSIE') > -1 || navigator.userAgent.indexOf('Trident') > -1,
bFF: navigator.userAgent.indexOf('Firefox') > -1,
jqueryVersion: parseFloat($.fn.jquery),
bCodeMirror: !!CodeMirror
};
/**
* func utils (for high-order func's arg)
*/
var func = (function () {
var eq = function (elA) {
return function (elB) {
return elA === elB;
};
};
var eq2 = function (elA, elB) {
return elA === elB;
};
var ok = function () {
return true;
};
var fail = function () {
return false;
};
var not = function (f) {
return function () {
return !f.apply(f, arguments);
};
};
var self = function (a) {
return a;
};
var idCounter = 0;
/**
* generate a globally-unique id
*
* @param {String} [prefix]
*/
var uniqueId = function (prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
/**
* returns bnd (bounds) from rect
*
* - IE Compatability Issue: http://goo.gl/sRLOAo
* - Scroll Issue: http://goo.gl/sNjUc
*
* @param {Rect} rect
* @return {Object} bounds
* @return {Number} bounds.top
* @return {Number} bounds.left
* @return {Number} bounds.width
* @return {Number} bounds.height
*/
var rect2bnd = function (rect) {
var $document = $(document);
return {
top: rect.top + $document.scrollTop(),
left: rect.left + $document.scrollLeft(),
width: rect.right - rect.left,
height: rect.bottom - rect.top
};
};
/**
* returns a copy of the object where the keys have become the values and the values the keys.
* @param {Object} obj
* @return {Object}
*/
var invertObject = function (obj) {
var inverted = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
inverted[obj[key]] = key;
}
}
return inverted;
};
return {
eq: eq,
eq2: eq2,
ok: ok,
fail: fail,
not: not,
self: self,
uniqueId: uniqueId,
rect2bnd: rect2bnd,
invertObject: invertObject
};
})();
/**
* list utils
*/
var list = (function () {
/**
* returns the first element of an array.
* @param {Array} array
*/
var head = function (array) {
return array[0];
};
/**
* returns the last element of an array.
* @param {Array} array
*/
var last = function (array) {
return array[array.length - 1];
};
/**
* returns everything but the last entry of the array.
* @param {Array} array
*/
var initial = function (array) {
return array.slice(0, array.length - 1);
};
/**
* returns the rest of the elements in an array.
* @param {Array} array
*/
var tail = function (array) {
return array.slice(1);
};
/**
* returns next item.
* @param {Array} array
*/
var next = function (array, item) {
var idx = array.indexOf(item);
if (idx === -1) { return null; }
return array[idx + 1];
};
/**
* returns prev item.
* @param {Array} array
*/
var prev = function (array, item) {
var idx = array.indexOf(item);
if (idx === -1) { return null; }
return array[idx - 1];
};
/**
* get sum from a list
* @param {Array} array - array
* @param {Function} fn - iterator
*/
var sum = function (array, fn) {
fn = fn || func.self;
return array.reduce(function (memo, v) {
return memo + fn(v);
}, 0);
};
/**
* returns a copy of the collection with array type.
* @param {Collection} collection - collection eg) node.childNodes, ...
*/
var from = function (collection) {
var result = [], idx = -1, length = collection.length;
while (++idx < length) {
result[idx] = collection[idx];
}
return result;
};
/**
* cluster elements by predicate function.
* @param {Array} array - array
* @param {Function} fn - predicate function for cluster rule
* @param {Array[]}
*/
var clusterBy = function (array, fn) {
if (array.length === 0) { return []; }
var aTail = tail(array);
return aTail.reduce(function (memo, v) {
var aLast = last(memo);
if (fn(last(aLast), v)) {
aLast[aLast.length] = v;
} else {
memo[memo.length] = [v];
}
return memo;
}, [[head(array)]]);
};
/**
* returns a copy of the array with all falsy values removed
* @param {Array} array - array
* @param {Function} fn - predicate function for cluster rule
*/
var compact = function (array) {
var aResult = [];
for (var idx = 0, sz = array.length; idx < sz; idx ++) {
if (array[idx]) { aResult.push(array[idx]); }
}
return aResult;
};
return { head: head, last: last, initial: initial, tail: tail,
prev: prev, next: next, sum: sum, from: from,
compact: compact, clusterBy: clusterBy };
})();
/**
* Dom functions
*/
var dom = (function () {
/**
* returns whether node is `note-editable` or not.
*
* @param {Element} node
* @return {Boolean}
*/
var isEditable = function (node) {
return node && $(node).hasClass('note-editable');
};
var isControlSizing = function (node) {
return node && $(node).hasClass('note-control-sizing');
};
/**
* build layoutInfo from $editor(.note-editor)
*
* @param {jQuery} $editor
* @return {Object}
*/
var buildLayoutInfo = function ($editor) {
var makeFinder;
// air mode
if ($editor.hasClass('note-air-editor')) {
var id = list.last($editor.attr('id').split('-'));
makeFinder = function (sIdPrefix) {
return function () { return $(sIdPrefix + id); };
};
return {
editor: function () { return $editor; },
editable: function () { return $editor; },
popover: makeFinder('#note-popover-'),
handle: makeFinder('#note-handle-'),
dialog: makeFinder('#note-dialog-')
};
// frame mode
} else {
makeFinder = function (sClassName) {
return function () { return $editor.find(sClassName); };
};
return {
editor: function () { return $editor; },
dropzone: makeFinder('.note-dropzone'),
toolbar: makeFinder('.note-toolbar'),
editable: makeFinder('.note-editable'),
codable: makeFinder('.note-codable'),
statusbar: makeFinder('.note-statusbar'),
popover: makeFinder('.note-popover'),
handle: makeFinder('.note-handle'),
dialog: makeFinder('.note-dialog')
};
}
};
/**
* returns predicate which judge whether nodeName is same
* @param {String} sNodeName
*/
var makePredByNodeName = function (sNodeName) {
// nodeName is always uppercase.
return function (node) {
return node && node.nodeName === sNodeName;
};
};
var isPara = function (node) {
// Chrome(v31.0), FF(v25.0.1) use DIV for paragraph
return node && /^DIV|^P|^LI|^H[1-7]/.test(node.nodeName);
};
var isList = function (node) {
return node && /^UL|^OL/.test(node.nodeName);
};
var isCell = function (node) {
return node && /^TD|^TH/.test(node.nodeName);
};
/**
* find nearest ancestor predicate hit
*
* @param {Element} node
* @param {Function} pred - predicate function
*/
var ancestor = function (node, pred) {
while (node) {
if (pred(node)) { return node; }
if (isEditable(node)) { break; }
node = node.parentNode;
}
return null;
};
/**
* returns new array of ancestor nodes (until predicate hit).
*
* @param {Element} node
* @param {Function} [optional] pred - predicate function
*/
var listAncestor = function (node, pred) {
pred = pred || func.fail;
var aAncestor = [];
ancestor(node, function (el) {
aAncestor.push(el);
return pred(el);
});
return aAncestor;
};
/**
* returns common ancestor node between two nodes.
*
* @param {Element} nodeA
* @param {Element} nodeB
*/
var commonAncestor = function (nodeA, nodeB) {
var aAncestor = listAncestor(nodeA);
for (var n = nodeB; n; n = n.parentNode) {
if ($.inArray(n, aAncestor) > -1) { return n; }
}
return null; // difference document area
};
/**
* listing all Nodes between two nodes.
* FIXME: nodeA and nodeB must be sorted, use comparePoints later.
*
* @param {Element} nodeA
* @param {Element} nodeB
*/
var listBetween = function (nodeA, nodeB) {
var aNode = [];
var bStart = false, bEnd = false;
// DFS(depth first search) with commonAcestor.
(function fnWalk(node) {
if (!node) { return; } // traverse fisnish
if (node === nodeA) { bStart = true; } // start point
if (bStart && !bEnd) { aNode.push(node); } // between
if (node === nodeB) { bEnd = true; return; } // end point
for (var idx = 0, sz = node.childNodes.length; idx < sz; idx++) {
fnWalk(node.childNodes[idx]);
}
})(commonAncestor(nodeA, nodeB));
return aNode;
};
/**
* listing all previous siblings (until predicate hit).
* @param {Element} node
* @param {Function} [optional] pred - predicate function
*/
var listPrev = function (node, pred) {
pred = pred || func.fail;
var aNext = [];
while (node) {
aNext.push(node);
if (pred(node)) { break; }
node = node.previousSibling;
}
return aNext;
};
/**
* listing next siblings (until predicate hit).
*
* @param {Element} node
* @param {Function} [pred] - predicate function
*/
var listNext = function (node, pred) {
pred = pred || func.fail;
var aNext = [];
while (node) {
aNext.push(node);
if (pred(node)) { break; }
node = node.nextSibling;
}
return aNext;
};
/**
* listing descendant nodes
*
* @param {Element} node
* @param {Function} [pred] - predicate function
*/
var listDescendant = function (node, pred) {
var aDescendant = [];
pred = pred || func.ok;
// start DFS(depth first search) with node
(function fnWalk(current) {
if (node !== current && pred(current)) {
aDescendant.push(current);
}
for (var idx = 0, sz = current.childNodes.length; idx < sz; idx++) {
fnWalk(current.childNodes[idx]);
}
})(node);
return aDescendant;
};
/**
* insert node after preceding
*
* @param {Element} node
* @param {Element} preceding - predicate function
*/
var insertAfter = function (node, preceding) {
var next = preceding.nextSibling, parent = preceding.parentNode;
if (next) {
parent.insertBefore(node, next);
} else {
parent.appendChild(node);
}
return node;
};
/**
* append elements.
*
* @param {Element} node
* @param {Collection} aChild
*/
var appends = function (node, aChild) {
$.each(aChild, function (idx, child) {
node.appendChild(child);
});
return node;
};
var isText = makePredByNodeName('#text');
/**
* returns #text's text size or element's childNodes size
*
* @param {Element} node
*/
var length = function (node) {
if (isText(node)) { return node.nodeValue.length; }
return node.childNodes.length;
};
/**
* returns offset from parent.
*
* @param {Element} node
*/
var position = function (node) {
var offset = 0;
while ((node = node.previousSibling)) { offset += 1; }
return offset;
};
/**
* return offsetPath(array of offset) from ancestor
*
* @param {Element} ancestor - ancestor node
* @param {Element} node
*/
var makeOffsetPath = function (ancestor, node) {
var aAncestor = list.initial(listAncestor(node, func.eq(ancestor)));
return $.map(aAncestor, position).reverse();
};
/**
* return element from offsetPath(array of offset)
*
* @param {Element} ancestor - ancestor node
* @param {array} aOffset - offsetPath
*/
var fromOffsetPath = function (ancestor, aOffset) {
var current = ancestor;
for (var i = 0, sz = aOffset.length; i < sz; i++) {
current = current.childNodes[aOffset[i]];
}
return current;
};
/**
* split element or #text
*
* @param {Element} node
* @param {Number} offset
*/
var splitData = function (node, offset) {
if (offset === 0) { return node; }
if (offset >= length(node)) { return node.nextSibling; }
// splitText
if (isText(node)) { return node.splitText(offset); }
// splitElement
var child = node.childNodes[offset];
node = insertAfter(node.cloneNode(false), node);
return appends(node, listNext(child));
};
/**
* split dom tree by boundaryPoint(pivot and offset)
*
* @param {Element} root
* @param {Element} pivot - this will be boundaryPoint's node
* @param {Number} offset - this will be boundaryPoint's offset
*/
var split = function (root, pivot, offset) {
var aAncestor = listAncestor(pivot, func.eq(root));
if (aAncestor.length === 1) { return splitData(pivot, offset); }
return aAncestor.reduce(function (node, parent) {
var clone = parent.cloneNode(false);
insertAfter(clone, parent);
if (node === pivot) {
node = splitData(node, offset);
}
appends(clone, listNext(node));
return clone;
});
};
/**
* remove node, (bRemoveChild: remove child or not)
* @param {Element} node
* @param {Boolean} bRemoveChild
*/
var remove = function (node, bRemoveChild) {
if (!node || !node.parentNode) { return; }
if (node.removeNode) { return node.removeNode(bRemoveChild); }
var elParent = node.parentNode;
if (!bRemoveChild) {
var aNode = [];
var i, sz;
for (i = 0, sz = node.childNodes.length; i < sz; i++) {
aNode.push(node.childNodes[i]);
}
for (i = 0, sz = aNode.length; i < sz; i++) {
elParent.insertBefore(aNode[i], node);
}
}
elParent.removeChild(node);
};
var html = function ($node) {
return dom.isTextarea($node[0]) ? $node.val() : $node.html();
};
return {
blank: agent.bMSIE ? ' ' : '
',
emptyPara: '