1
0
Fork 0
mirror of https://github.com/janickiy/yii2-nomer synced 2025-03-09 15:39:59 +00:00

add files to project

This commit is contained in:
janickiy 2020-02-05 06:34:26 +03:00
commit 5cac498444
3729 changed files with 836998 additions and 0 deletions

View file

@ -0,0 +1,139 @@
/*
Base.js, version 1.1a
Copyright 2006-2010, Dean Edwards
License: http://www.opensource.org/licenses/mit-license.php
*/
var Base = function() {
// dummy
};
Base.extend = function(_instance, _static) { // subclass
var extend = Base.prototype.extend;
// build the prototype
Base._prototyping = true;
var proto = new this;
extend.call(proto, _instance);
proto.base = function() {
// call this method from any other method to invoke that method's ancestor
};
delete Base._prototyping;
// create the wrapper for the constructor function
//var constructor = proto.constructor.valueOf(); //-dean
var constructor = proto.constructor;
var klass = proto.constructor = function() {
if (!Base._prototyping) {
if (this._constructing || this.constructor == klass) { // instantiation
this._constructing = true;
constructor.apply(this, arguments);
delete this._constructing;
} else if (arguments[0] != null) { // casting
return (arguments[0].extend || extend).call(arguments[0], proto);
}
}
};
// build the class interface
klass.ancestor = this;
klass.extend = this.extend;
klass.forEach = this.forEach;
klass.implement = this.implement;
klass.prototype = proto;
klass.toString = this.toString;
klass.valueOf = function(type) {
//return (type == "object") ? klass : constructor; //-dean
return (type == "object") ? klass : constructor.valueOf();
};
extend.call(klass, _static);
// class initialisation
if (typeof klass.init == "function") klass.init();
return klass;
};
Base.prototype = {
extend: function(source, value) {
if (arguments.length > 1) { // extending with a name/value pair
var ancestor = this[source];
if (ancestor && (typeof value == "function") && // overriding a method?
// the valueOf() comparison is to avoid circular references
(!ancestor.valueOf || ancestor.valueOf() != value.valueOf()) &&
/\bbase\b/.test(value)) {
// get the underlying method
var method = value.valueOf();
// override
value = function() {
var previous = this.base || Base.prototype.base;
this.base = ancestor;
var returnValue = method.apply(this, arguments);
this.base = previous;
return returnValue;
};
// point to the underlying method
value.valueOf = function(type) {
return (type == "object") ? value : method;
};
value.toString = Base.toString;
}
this[source] = value;
} else if (source) { // extending with an object literal
var extend = Base.prototype.extend;
// if this object has a customised extend method then use it
if (!Base._prototyping && typeof this != "function") {
extend = this.extend || extend;
}
var proto = {toSource: null};
// do the "toString" and other methods manually
var hidden = ["constructor", "toString", "valueOf"];
// if we are prototyping then include the constructor
var i = Base._prototyping ? 0 : 1;
while (key = hidden[i++]) {
if (source[key] != proto[key]) {
extend.call(this, key, source[key]);
}
}
// copy each of the source object's properties to this object
for (var key in source) {
if (!proto[key]) extend.call(this, key, source[key]);
}
}
return this;
}
};
// initialise
Base = Base.extend({
constructor: function() {
this.extend(arguments[0]);
}
}, {
ancestor: Object,
version: "1.1",
forEach: function(object, block, context) {
for (var key in object) {
if (this.prototype[key] === undefined) {
block.call(context, object[key], key, object);
}
}
},
implement: function() {
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == "function") {
// if it's a function, call it
arguments[i](this.prototype);
} else {
// add the interface using the extend method
this.prototype.extend(arguments[i]);
}
}
return this;
},
toString: function() {
return String(this.valueOf());
}
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,239 @@
/**
* Selection save and restore module for Rangy.
* Saves and restores user selections using marker invisible elements in the DOM.
*
* Part of Rangy, a cross-browser JavaScript range and selection library
* https://github.com/timdown/rangy
*
* Depends on Rangy core.
*
* Copyright 2015, Tim Down
* Licensed under the MIT license.
* Version: 1.3.1-dev
* Build date: 20 May 2015
*
* NOTE: UMD wrapper removed manually for bundling (Oliver Pulges)
*/
rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
var dom = api.dom;
var removeNode = dom.removeNode;
var isDirectionBackward = api.Selection.isDirectionBackward;
var markerTextChar = "\ufeff";
function gEBI(id, doc) {
return (doc || document).getElementById(id);
}
function insertRangeBoundaryMarker(range, atStart) {
var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
var markerEl;
var doc = dom.getDocument(range.startContainer);
// Clone the Range and collapse to the appropriate boundary point
var boundaryRange = range.cloneRange();
boundaryRange.collapse(atStart);
// Create the marker element containing a single invisible character using DOM methods and insert it
markerEl = doc.createElement("span");
markerEl.id = markerId;
markerEl.style.lineHeight = "0";
markerEl.style.display = "none";
markerEl.className = "rangySelectionBoundary";
markerEl.appendChild(doc.createTextNode(markerTextChar));
boundaryRange.insertNode(markerEl);
return markerEl;
}
function setRangeBoundary(doc, range, markerId, atStart) {
var markerEl = gEBI(markerId, doc);
if (markerEl) {
range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
removeNode(markerEl);
} else {
module.warn("Marker element has been removed. Cannot restore selection.");
}
}
function compareRanges(r1, r2) {
return r2.compareBoundaryPoints(r1.START_TO_START, r1);
}
function saveRange(range, direction) {
var startEl, endEl, doc = api.DomRange.getRangeDocument(range), text = range.toString();
var backward = isDirectionBackward(direction);
if (range.collapsed) {
endEl = insertRangeBoundaryMarker(range, false);
return {
document: doc,
markerId: endEl.id,
collapsed: true
};
} else {
endEl = insertRangeBoundaryMarker(range, false);
startEl = insertRangeBoundaryMarker(range, true);
return {
document: doc,
startMarkerId: startEl.id,
endMarkerId: endEl.id,
collapsed: false,
backward: backward,
toString: function() {
return "original text: '" + text + "', new text: '" + range.toString() + "'";
}
};
}
}
function restoreRange(rangeInfo, normalize) {
var doc = rangeInfo.document;
if (typeof normalize == "undefined") {
normalize = true;
}
var range = api.createRange(doc);
if (rangeInfo.collapsed) {
var markerEl = gEBI(rangeInfo.markerId, doc);
if (markerEl) {
markerEl.style.display = "inline";
var previousNode = markerEl.previousSibling;
// Workaround for issue 17
if (previousNode && previousNode.nodeType == 3) {
removeNode(markerEl);
range.collapseToPoint(previousNode, previousNode.length);
} else {
range.collapseBefore(markerEl);
removeNode(markerEl);
}
} else {
module.warn("Marker element has been removed. Cannot restore selection.");
}
} else {
setRangeBoundary(doc, range, rangeInfo.startMarkerId, true);
setRangeBoundary(doc, range, rangeInfo.endMarkerId, false);
}
if (normalize) {
range.normalizeBoundaries();
}
return range;
}
function saveRanges(ranges, direction) {
var rangeInfos = [], range, doc;
var backward = isDirectionBackward(direction);
// Order the ranges by position within the DOM, latest first, cloning the array to leave the original untouched
ranges = ranges.slice(0);
ranges.sort(compareRanges);
for (var i = 0, len = ranges.length; i < len; ++i) {
rangeInfos[i] = saveRange(ranges[i], backward);
}
// Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
// between its markers
for (i = len - 1; i >= 0; --i) {
range = ranges[i];
doc = api.DomRange.getRangeDocument(range);
if (range.collapsed) {
range.collapseAfter(gEBI(rangeInfos[i].markerId, doc));
} else {
range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
}
}
return rangeInfos;
}
function saveSelection(win) {
if (!api.isSelectionValid(win)) {
module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
return null;
}
var sel = api.getSelection(win);
var ranges = sel.getAllRanges();
var backward = (ranges.length == 1 && sel.isBackward());
var rangeInfos = saveRanges(ranges, backward);
// Ensure current selection is unaffected
if (backward) {
sel.setSingleRange(ranges[0], backward);
} else {
sel.setRanges(ranges);
}
return {
win: win,
rangeInfos: rangeInfos,
restored: false
};
}
function restoreRanges(rangeInfos) {
var ranges = [];
// Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
// normalization affecting previously restored ranges.
var rangeCount = rangeInfos.length;
for (var i = rangeCount - 1; i >= 0; i--) {
ranges[i] = restoreRange(rangeInfos[i], true);
}
return ranges;
}
function restoreSelection(savedSelection, preserveDirection) {
if (!savedSelection.restored) {
var rangeInfos = savedSelection.rangeInfos;
var sel = api.getSelection(savedSelection.win);
var ranges = restoreRanges(rangeInfos), rangeCount = rangeInfos.length;
if (rangeCount == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backward) {
sel.removeAllRanges();
sel.addRange(ranges[0], true);
} else {
sel.setRanges(ranges);
}
savedSelection.restored = true;
}
}
function removeMarkerElement(doc, markerId) {
var markerEl = gEBI(markerId, doc);
if (markerEl) {
removeNode(markerEl);
}
}
function removeMarkers(savedSelection) {
var rangeInfos = savedSelection.rangeInfos;
for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
rangeInfo = rangeInfos[i];
if (rangeInfo.collapsed) {
removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
} else {
removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
}
}
}
api.util.extend(api, {
saveRange: saveRange,
restoreRange: restoreRange,
saveRanges: saveRanges,
restoreRanges: restoreRanges,
saveSelection: saveSelection,
restoreSelection: restoreSelection,
removeMarkerElement: removeMarkerElement,
removeMarkers: removeMarkers
});
});

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,558 @@
/**
* Full HTML5 compatibility rule set
* These rules define which tags and CSS classes are supported and which tags should be specially treated.
*
* Examples based on this rule set:
*
* <a href="http://foobar.com">foo</a>
* ... becomes ...
* <a href="http://foobar.com" target="_blank" rel="nofollow">foo</a>
*
* <img align="left" src="http://foobar.com/image.png">
* ... becomes ...
* <img class="wysiwyg-float-left" src="http://foobar.com/image.png" alt="">
*
* <div>foo<script>alert(document.cookie)</script></div>
* ... becomes ...
* <div>foo</div>
*
* <marquee>foo</marquee>
* ... becomes ...
* <span>foo</span>
*
* foo <br clear="both"> bar
* ... becomes ...
* foo <br class="wysiwyg-clear-both"> bar
*
* <div>hello <iframe src="http://google.com"></iframe></div>
* ... becomes ...
* <div>hello </div>
*
* <center>hello</center>
* ... becomes ...
* <div class="wysiwyg-text-align-center">hello</div>
*/
var wysihtmlParserRules = {
/**
* CSS Class white-list
* Following CSS classes won't be removed when parsed by the wysihtml HTML parser
*/
"classes": {
"wysiwyg-clear-both": 1,
"wysiwyg-clear-left": 1,
"wysiwyg-clear-right": 1,
"wysiwyg-color-aqua": 1,
"wysiwyg-color-black": 1,
"wysiwyg-color-blue": 1,
"wysiwyg-color-fuchsia": 1,
"wysiwyg-color-gray": 1,
"wysiwyg-color-green": 1,
"wysiwyg-color-lime": 1,
"wysiwyg-color-maroon": 1,
"wysiwyg-color-navy": 1,
"wysiwyg-color-olive": 1,
"wysiwyg-color-purple": 1,
"wysiwyg-color-red": 1,
"wysiwyg-color-silver": 1,
"wysiwyg-color-teal": 1,
"wysiwyg-color-white": 1,
"wysiwyg-color-yellow": 1,
"wysiwyg-float-left": 1,
"wysiwyg-float-right": 1,
"wysiwyg-font-size-large": 1,
"wysiwyg-font-size-larger": 1,
"wysiwyg-font-size-medium": 1,
"wysiwyg-font-size-small": 1,
"wysiwyg-font-size-smaller": 1,
"wysiwyg-font-size-x-large": 1,
"wysiwyg-font-size-x-small": 1,
"wysiwyg-font-size-xx-large": 1,
"wysiwyg-font-size-xx-small": 1,
"wysiwyg-text-align-center": 1,
"wysiwyg-text-align-justify": 1,
"wysiwyg-text-align-left": 1,
"wysiwyg-text-align-right": 1
},
/**
* Tag list
*
* The following options are available:
*
* - add_class: converts and deletes the given HTML4 attribute (align, clear, ...) via the given method to a css class
* The following methods are implemented in wysihtml.dom.parse:
* - align_text: converts align attribute values (right/left/center/justify) to their corresponding css class "wysiwyg-text-align-*")
* <p align="center">foo</p> ... becomes ... <p class="wysiwyg-text-align-center">foo</p>
* - clear_br: converts clear attribute values left/right/all/both to their corresponding css class "wysiwyg-clear-*"
* <br clear="all"> ... becomes ... <br class="wysiwyg-clear-both">
* - align_img: converts align attribute values (right/left) on <img> to their corresponding css class "wysiwyg-float-*"
*
* - add_style: converts and deletes the given HTML4 attribute (align) via the given method to a css style
* The following methods are implemented in wysihtml.dom.parse:
* - align_text: converts align attribute values (right/left/center) to their corresponding css style)
* <p align="center">foo</p> ... becomes ... <p style="text-align:center">foo</p>
*
* - remove: removes the element and its content
*
* - unwrap removes element but leaves content
*
* - rename_tag: renames the element to the given tag
*
* - set_class: adds the given class to the element (note: make sure that the class is in the "classes" white list above)
*
* - set_attributes: sets/overrides the given attributes
*
* - check_attributes: checks the given HTML attribute via the given method
* - url: allows only valid urls (starting with http:// or https://)
* - src: allows something like "/foobar.jpg", "http://google.com", ...
* - href: allows something like "mailto:bert@foo.com", "http://google.com", "/foobar.jpg"
* - alt: strips unwanted characters. if the attribute is not set, then it gets set (to ensure valid and compatible HTML)
* - numbers: ensures that the attribute only contains numeric (integer) characters (no float values or units)
* - dimension: for with/height attributes where floating point numbrs and percentages are allowed
* - any: allows anything to pass
*/
"tags": {
"tr": {
"add_class": {
"align": "align_text"
}
},
"strike": {
"remove": 1
},
"form": {
"rename_tag": "div"
},
"rt": {
"rename_tag": "span"
},
"code": {},
"acronym": {
"rename_tag": "span"
},
"br": {
"add_class": {
"clear": "clear_br"
}
},
"details": {
"rename_tag": "div"
},
"h4": {
"add_class": {
"align": "align_text"
}
},
"em": {},
"title": {
"remove": 1
},
"multicol": {
"rename_tag": "div"
},
"figure": {
"rename_tag": "div"
},
"xmp": {
"rename_tag": "span"
},
"small": {
"rename_tag": "span",
"set_class": "wysiwyg-font-size-smaller"
},
"area": {
"remove": 1
},
"time": {
"rename_tag": "span"
},
"dir": {
"rename_tag": "ul"
},
"bdi": {
"rename_tag": "span"
},
"command": {
"remove": 1
},
"ul": {},
"progress": {
"rename_tag": "span"
},
"dfn": {
"rename_tag": "span"
},
"iframe": {
"remove": 1
},
"figcaption": {
"rename_tag": "div"
},
"a": {
"check_attributes": {
"target": "any",
"href": "url" // if you compiled master manually then change this from 'url' to 'href'
},
"set_attributes": {
"rel": "nofollow"
}
},
"img": {
"check_attributes": {
"width": "dimension",
"alt": "alt",
"src": "url", // if you compiled master manually then change this from 'url' to 'src'
"height": "dimension"
},
"add_class": {
"align": "align_img"
}
},
"rb": {
"rename_tag": "span"
},
"footer": {
"rename_tag": "div"
},
"noframes": {
"remove": 1
},
"abbr": {
"rename_tag": "span"
},
"u": {},
"bgsound": {
"remove": 1
},
"address": {
"rename_tag": "div"
},
"basefont": {
"remove": 1
},
"nav": {
"rename_tag": "div"
},
"h1": {
"add_class": {
"align": "align_text"
}
},
"head": {
"remove": 1
},
"tbody": {
"add_class": {
"align": "align_text"
}
},
"dd": {
"rename_tag": "div"
},
"s": {
"rename_tag": "span"
},
"li": {},
"td": {
"check_attributes": {
"rowspan": "numbers",
"colspan": "numbers"
},
"add_class": {
"align": "align_text"
}
},
"object": {
"remove": 1
},
"div": {
"add_class": {
"align": "align_text"
}
},
"option": {
"rename_tag": "span"
},
"select": {
"rename_tag": "span"
},
"i": {},
"track": {
"remove": 1
},
"wbr": {
"remove": 1
},
"fieldset": {
"rename_tag": "div"
},
"big": {
"rename_tag": "span",
"set_class": "wysiwyg-font-size-larger"
},
"button": {
"rename_tag": "span"
},
"noscript": {
"remove": 1
},
"svg": {
"remove": 1
},
"input": {
"remove": 1
},
"table": {},
"keygen": {
"remove": 1
},
"h5": {
"add_class": {
"align": "align_text"
}
},
"meta": {
"remove": 1
},
"map": {
"rename_tag": "div"
},
"isindex": {
"remove": 1
},
"mark": {
"rename_tag": "span"
},
"caption": {
"add_class": {
"align": "align_text"
}
},
"tfoot": {
"add_class": {
"align": "align_text"
}
},
"base": {
"remove": 1
},
"video": {
"remove": 1
},
"strong": {},
"canvas": {
"remove": 1
},
"output": {
"rename_tag": "span"
},
"marquee": {
"rename_tag": "span"
},
"b": {},
"q": {
"check_attributes": {
"cite": "url"
}
},
"applet": {
"remove": 1
},
"span": {},
"rp": {
"rename_tag": "span"
},
"spacer": {
"remove": 1
},
"source": {
"remove": 1
},
"aside": {
"rename_tag": "div"
},
"frame": {
"remove": 1
},
"section": {
"rename_tag": "div"
},
"body": {
"rename_tag": "div"
},
"ol": {},
"nobr": {
"rename_tag": "span"
},
"html": {
"rename_tag": "div"
},
"summary": {
"rename_tag": "span"
},
"var": {
"rename_tag": "span"
},
"del": {
"remove": 1
},
"blockquote": {
"check_attributes": {
"cite": "url"
}
},
"style": {
"remove": 1
},
"device": {
"remove": 1
},
"meter": {
"rename_tag": "span"
},
"h3": {
"add_class": {
"align": "align_text"
}
},
"textarea": {
"rename_tag": "span"
},
"embed": {
"remove": 1
},
"hgroup": {
"rename_tag": "div"
},
"font": {
"rename_tag": "span",
"add_class": {
"size": "size_font"
}
},
"tt": {
"rename_tag": "span"
},
"noembed": {
"remove": 1
},
"thead": {
"add_class": {
"align": "align_text"
}
},
"blink": {
"rename_tag": "span"
},
"plaintext": {
"rename_tag": "span"
},
"xml": {
"remove": 1
},
"h6": {
"add_class": {
"align": "align_text"
}
},
"param": {
"remove": 1
},
"th": {
"check_attributes": {
"rowspan": "numbers",
"colspan": "numbers"
},
"add_class": {
"align": "align_text"
}
},
"legend": {
"rename_tag": "span"
},
"hr": {},
"label": {
"rename_tag": "span"
},
"dl": {
"rename_tag": "div"
},
"kbd": {
"rename_tag": "span"
},
"listing": {
"rename_tag": "div"
},
"dt": {
"rename_tag": "span"
},
"nextid": {
"remove": 1
},
"pre": {},
"center": {
"rename_tag": "div",
"set_class": "wysiwyg-text-align-center"
},
"audio": {
"remove": 1
},
"datalist": {
"rename_tag": "span"
},
"samp": {
"rename_tag": "span"
},
"col": {
"remove": 1
},
"article": {
"rename_tag": "div"
},
"cite": {},
"link": {
"remove": 1
},
"script": {
"remove": 1
},
"bdo": {
"rename_tag": "span"
},
"menu": {
"rename_tag": "ul"
},
"colgroup": {
"remove": 1
},
"ruby": {
"rename_tag": "span"
},
"h2": {
"add_class": {
"align": "align_text"
}
},
"ins": {
"rename_tag": "span"
},
"p": {
"add_class": {
"align": "align_text"
}
},
"sub": {},
"comment": {
"remove": 1
},
"frameset": {
"remove": 1
},
"optgroup": {
"rename_tag": "span"
},
"header": {
"rename_tag": "div"
},
"sup": {}
}
};

View file

@ -0,0 +1,680 @@
/**
* Full HTML5 compatibility rule set
* Loosened and extended ruleset. Allows more freedom on user side
* These rules define which tags and CSS classes are supported and which tags should be specially treated.
*/
var wysihtmlParserRulesDefaults = {
"blockLevelEl": {
"keep_styles": {
"textAlign": /^((left)|(right)|(center)|(justify))$/i,
"float": 1
},
"add_style": {
"align": "align_text"
},
"check_attributes": {
"id": "any"
}
},
"makeDiv": {
"rename_tag": "div",
"one_of_type": {
"alignment_object": 1
},
"remove_action": "unwrap",
"keep_styles": {
"textAlign": 1,
"float": 1
},
"add_style": {
"align": "align_text"
},
"check_attributes": {
"id": "any"
}
}
};
var wysihtmlParserRules = {
/**
* CSS Class white-list
* Following CSS classes won't be removed when parsed by the wysihtml HTML parser
* If all classes should pass "any" as classes value. Ex: "classes": "any"
*/
"classes": "any",
/* blacklist of classes is only available if classes is set to any */
"classes_blacklist": {
"Apple-interchange-newline": 1,
"MsoNormal": 1,
"MsoPlainText": 1
},
"type_definitions": {
"alignment_object": {
"classes": {
"wysiwyg-text-align-center": 1,
"wysiwyg-text-align-justify": 1,
"wysiwyg-text-align-left": 1,
"wysiwyg-text-align-right": 1,
"wysiwyg-float-left": 1,
"wysiwyg-float-right": 1
},
"styles": {
"float": ["left", "right"],
"text-align": ["left", "right", "center"]
}
},
"valid_image_src": {
"attrs": {
"src": /^[^data\:]/i
}
},
"text_color_object": {
"styles": {
"color": true,
"background-color": true
}
},
"text_fontsize_object": {
"styles": {
"font-size": true
}
},
"text_formatting_object": {
"classes": {
"wysiwyg-color-aqua": 1,
"wysiwyg-color-black": 1,
"wysiwyg-color-blue": 1,
"wysiwyg-color-fuchsia": 1,
"wysiwyg-color-gray": 1,
"wysiwyg-color-green": 1,
"wysiwyg-color-lime": 1,
"wysiwyg-color-maroon": 1,
"wysiwyg-color-navy": 1,
"wysiwyg-color-olive": 1,
"wysiwyg-color-purple": 1,
"wysiwyg-color-red": 1,
"wysiwyg-color-silver": 1,
"wysiwyg-color-teal": 1,
"wysiwyg-color-white": 1,
"wysiwyg-color-yellow": 1,
"wysiwyg-font-size-large": 1,
"wysiwyg-font-size-larger": 1,
"wysiwyg-font-size-medium": 1,
"wysiwyg-font-size-small": 1,
"wysiwyg-font-size-smaller": 1,
"wysiwyg-font-size-x-large": 1,
"wysiwyg-font-size-x-small": 1,
"wysiwyg-font-size-xx-large": 1,
"wysiwyg-font-size-xx-small": 1
}
}
},
"comments": 1, // if set allows comments to pass
/**
* Tag list
*
* The following options are available:
*
* - add_class: converts and deletes the given HTML4 attribute (align, clear, ...) via the given method to a css class
* The following methods are implemented in wysihtml.dom.parse:
* - align_text: converts align attribute values (right/left/center/justify) to their corresponding css class "wysiwyg-text-align-*")
* <p align="center">foo</p> ... becomes ... <p> class="wysiwyg-text-align-center">foo</p>
* - clear_br: converts clear attribute values left/right/all/both to their corresponding css class "wysiwyg-clear-*"
* <br clear="all"> ... becomes ... <br class="wysiwyg-clear-both">
* - align_img: converts align attribute values (right/left) on <img> to their corresponding css class "wysiwyg-float-*"
*
* - remove: removes the element and its content
*
* - unwrap removes element but leaves content
*
* - rename_tag: renames the element to the given tag
*
* - set_class: adds the given class to the element (note: make sure that the class is in the "classes" white list above)
*
* - set_attributes: sets/overrides the given attributes
*
* - check_attributes: checks the given HTML attribute via the given method
* - url: allows only valid urls (starting with http:// or https://)
* - src: allows something like "/foobar.jpg", "http://google.com", ...
* - href: allows something like "mailto:bert@foo.com", "http://google.com", "/foobar.jpg"
* - alt: strips unwanted characters. if the attribute is not set, then it gets set (to ensure valid and compatible HTML)
* - numbers: ensures that the attribute only contains numeric (integer) characters (no float values or units)
* - dimension: for with/height attributes where floating point numbrs and percentages are allowed
* - any: allows anything to pass
*/
"tags": {
"tr": {
"add_style": {
"align": "align_text"
},
"check_attributes": {
"id": "any"
}
},
"strike": {
"unwrap": 1
},
"form": {
"unwrap": 1
},
"rt": {
"rename_tag": "span"
},
"code": {},
"acronym": {
"rename_tag": "span"
},
"br": {
"add_class": {
"clear": "clear_br"
}
},
"details": {
"unwrap": 1
},
"h4": wysihtmlParserRulesDefaults.blockLevelEl,
"em": {},
"title": {
"remove": 1
},
"multicol": {
"unwrap": 1
},
"figure": {
"unwrap": 1
},
"xmp": {
"unwrap": 1
},
"small": {
"rename_tag": "span",
"set_class": "wysiwyg-font-size-smaller"
},
"area": {
"remove": 1
},
"time": {
"unwrap": 1
},
"dir": {
"rename_tag": "ul"
},
"bdi": {
"unwrap": 1
},
"command": {
"unwrap": 1
},
"ul": {
"check_attributes": {
"id": "any"
}
},
"progress": {
"rename_tag": "span"
},
"dfn": {
"unwrap": 1
},
"iframe": {
"check_attributes": {
"src": "any",
"width": "any",
"height": "any",
"frameborder": "any",
"style": "any",
"id": "any"
}
},
"figcaption": {
"unwrap": 1
},
"a": {
"check_attributes": {
"href": "href", // if you compiled master manually then change this from 'url' to 'href'
"rel": "any",
"target": "any",
"id": "any"
}
},
"img": {
"one_of_type": {
"valid_image_src": 1
},
"check_attributes": {
"width": "dimension",
"alt": "alt",
"src": "src", // if you compiled master manually then change this from 'url' to 'src'
"height": "dimension",
"id": "any"
},
"add_class": {
"align": "align_img"
}
},
"rb": {
"unwrap": 1
},
"footer": wysihtmlParserRulesDefaults.makeDiv,
"noframes": {
"remove": 1
},
"abbr": {
"unwrap": 1
},
"u": {},
"bgsound": {
"remove": 1
},
"sup": {},
"address": {
"unwrap": 1
},
"basefont": {
"remove": 1
},
"nav": {
"unwrap": 1
},
"h1": wysihtmlParserRulesDefaults.blockLevelEl,
"head": {
"unwrap": 1
},
"tbody": wysihtmlParserRulesDefaults.blockLevelEl,
"dd": {
"unwrap": 1
},
"s": {
"unwrap": 1
},
"li": {},
"td": {
"check_attributes": {
"rowspan": "numbers",
"colspan": "numbers",
"valign": "any",
"align": "any",
"id": "any",
"class": "any"
},
"keep_styles": {
"backgroundColor": 1,
"width": 1,
"height": 1
},
"add_style": {
"align": "align_text"
}
},
"object": {
"remove": 1
},
"div": {
"one_of_type": {
"alignment_object": 1
},
"remove_action": "unwrap",
"keep_styles": {
"textAlign": 1,
"float": 1
},
"add_style": {
"align": "align_text"
},
"check_attributes": {
"id": "any",
"contenteditable": "any"
}
},
"option": {
"remove":1
},
"select": {
"remove":1
},
"i": {},
"track": {
"remove": 1
},
"wbr": {
"remove": 1
},
"fieldset": {
"unwrap": 1
},
"big": {
"rename_tag": "span",
"set_class": "wysiwyg-font-size-larger"
},
"button": {
"unwrap": 1
},
"noscript": {
"remove": 1
},
"svg": {
"remove": 1
},
"input": {
"remove": 1
},
"table": {
"keep_styles": {
"width": 1,
"textAlign": 1,
"float": 1
},
"check_attributes": {
"id": "any"
}
},
"keygen": {
"remove": 1
},
"h5": wysihtmlParserRulesDefaults.blockLevelEl,
"meta": {
"remove": 1
},
"map": {
"remove": 1
},
"isindex": {
"remove": 1
},
"mark": {
"unwrap": 1
},
"caption": wysihtmlParserRulesDefaults.blockLevelEl,
"tfoot": wysihtmlParserRulesDefaults.blockLevelEl,
"base": {
"remove": 1
},
"video": {
"remove": 1
},
"strong": {},
"canvas": {
"remove": 1
},
"output": {
"unwrap": 1
},
"marquee": {
"unwrap": 1
},
"b": {},
"q": {
"check_attributes": {
"cite": "url",
"id": "any"
}
},
"applet": {
"remove": 1
},
"span": {
"one_of_type": {
"text_formatting_object": 1,
"text_color_object": 1,
"text_fontsize_object": 1
},
"keep_styles": {
"color": 1,
"backgroundColor": 1,
"fontSize": 1
},
"remove_action": "unwrap",
"check_attributes": {
"id": "any"
}
},
"rp": {
"unwrap": 1
},
"spacer": {
"remove": 1
},
"source": {
"remove": 1
},
"aside": wysihtmlParserRulesDefaults.makeDiv,
"frame": {
"remove": 1
},
"section": wysihtmlParserRulesDefaults.makeDiv,
"body": {
"unwrap": 1
},
"ol": {},
"nobr": {
"unwrap": 1
},
"html": {
"unwrap": 1
},
"summary": {
"unwrap": 1
},
"var": {
"unwrap": 1
},
"del": {
"unwrap": 1
},
"blockquote": {
"keep_styles": {
"textAlign": 1,
"float": 1
},
"add_style": {
"align": "align_text"
},
"check_attributes": {
"cite": "url",
"id": "any"
}
},
"style": {
"check_attributes": {
"type": "any",
"src": "any",
"charset": "any"
}
},
"device": {
"remove": 1
},
"meter": {
"unwrap": 1
},
"h3": wysihtmlParserRulesDefaults.blockLevelEl,
"textarea": {
"unwrap": 1
},
"embed": {
"remove": 1
},
"hgroup": {
"unwrap": 1
},
"font": {
"rename_tag": "span",
"add_class": {
"size": "size_font"
}
},
"tt": {
"unwrap": 1
},
"noembed": {
"remove": 1
},
"thead": {
"add_style": {
"align": "align_text"
},
"check_attributes": {
"id": "any"
}
},
"blink": {
"unwrap": 1
},
"plaintext": {
"unwrap": 1
},
"xml": {
"remove": 1
},
"h6": wysihtmlParserRulesDefaults.blockLevelEl,
"param": {
"remove": 1
},
"th": {
"check_attributes": {
"rowspan": "numbers",
"colspan": "numbers",
"valign": "any",
"align": "any",
"id": "any"
},
"keep_styles": {
"backgroundColor": 1,
"width": 1,
"height": 1
},
"add_style": {
"align": "align_text"
}
},
"legend": {
"unwrap": 1
},
"hr": {},
"label": {
"unwrap": 1
},
"dl": {
"unwrap": 1
},
"kbd": {
"unwrap": 1
},
"listing": {
"unwrap": 1
},
"dt": {
"unwrap": 1
},
"nextid": {
"remove": 1
},
"pre": {},
"center": wysihtmlParserRulesDefaults.makeDiv,
"audio": {
"remove": 1
},
"datalist": {
"unwrap": 1
},
"samp": {
"unwrap": 1
},
"col": {
"remove": 1
},
"article": wysihtmlParserRulesDefaults.makeDiv,
"cite": {},
"link": {
"remove": 1
},
"script": {
"check_attributes": {
"type": "any",
"src": "any",
"charset": "any"
}
},
"bdo": {
"unwrap": 1
},
"menu": {
"rename_tag": "ul"
},
"colgroup": {
"remove": 1
},
"ruby": {
"unwrap": 1
},
"h2": wysihtmlParserRulesDefaults.blockLevelEl,
"ins": {
"unwrap": 1
},
"p": wysihtmlParserRulesDefaults.blockLevelEl,
"sub": {},
"comment": {
"remove": 1
},
"frameset": {
"remove": 1
},
"optgroup": {
"unwrap": 1
},
"header": wysihtmlParserRulesDefaults.makeDiv
}
};
(function() {
// Paste cleanup rules universal for all rules (also applied to content copied from editor)
var commonRules = wysihtml.lang.object(wysihtmlParserRules).clone(true);
commonRules.comments = false;
commonRules.selectors = { "a u": "unwrap"};
commonRules.tags.style = { "remove": 1 };
commonRules.tags.script = { "remove": 1 };
commonRules.tags.head = { "remove": 1 };
// Paste cleanup for unindentified source
var universalRules = wysihtml.lang.object(commonRules).clone(true);
universalRules.tags.div.one_of_type.alignment_object = 1;
universalRules.tags.div.remove_action = "unwrap";
universalRules.tags.div.check_attributes.style = false;
universalRules.tags.div.keep_styles = {
"textAlign": /^((left)|(right)|(center)|(justify))$/i,
"float": 1
};
universalRules.tags.span.keep_styles = false;
// Paste cleanup for MS Office
// TODO: should be extended to stricter ruleset, as current set will probably not cover all Office bizarreness
var msOfficeRules = wysihtml.lang.object(universalRules).clone(true);
msOfficeRules.classes = {};
window.wysihtmlParserPasteRulesets = [
{
condition: /<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument|class="OutlineElement|id="?docs\-internal\-guid\-/i,
set: msOfficeRules
},{
condition: /<meta name="copied-from" content="wysihtml">/i,
set: commonRules
},{
set: universalRules
}
];
})();

View file

@ -0,0 +1,663 @@
/**
* Full HTML5 compatibility rule set
* These rules define which tags and CSS classes are supported and which tags should be specially treated.
*
* Examples based on this rule set:
*
* <a href="http://foobar.com">foo</a>
* ... becomes ...
* <a href="http://foobar.com" target="_blank" rel="nofollow">foo</a>
*
* <img align="left" src="http://foobar.com/image.png">
* ... becomes ...
* <img class="wysiwyg-float-left" src="http://foobar.com/image.png" alt="">
*
* <div>foo<script>alert(document.cookie);</script></div>
* ... becomes ...
* <div>foo</div>
*
* <marquee>foo</marquee>
* ... becomes ...
* <span>foo</span>
*
* foo <br clear="both"> bar
* ... becomes ...
* foo <br class="wysiwyg-clear-both"> bar
*
* <div>hello <iframe src="http://google.com"></iframe></div>
* ... becomes ...
* <div>hello </div>
*
* <center>hello</center>
* ... becomes ...
* <div class="wysiwyg-text-align-center">hello</div>
*/
var wysihtmlParserRules = {
/**
* CSS Class white-list
* Following CSS classes won't be removed when parsed by the wysihtml HTML parser
* If all classes should pass "any" as classes value. Ex: "classes": "any"
*/
"classes": {
"wysiwyg-clear-both": 1,
"wysiwyg-clear-left": 1,
"wysiwyg-clear-right": 1,
"wysiwyg-color-aqua": 1,
"wysiwyg-color-black": 1,
"wysiwyg-color-blue": 1,
"wysiwyg-color-fuchsia": 1,
"wysiwyg-color-gray": 1,
"wysiwyg-color-green": 1,
"wysiwyg-color-lime": 1,
"wysiwyg-color-maroon": 1,
"wysiwyg-color-navy": 1,
"wysiwyg-color-olive": 1,
"wysiwyg-color-purple": 1,
"wysiwyg-color-red": 1,
"wysiwyg-color-silver": 1,
"wysiwyg-color-teal": 1,
"wysiwyg-color-white": 1,
"wysiwyg-color-yellow": 1,
"wysiwyg-float-left": 1,
"wysiwyg-float-right": 1,
"wysiwyg-font-size-large": 1,
"wysiwyg-font-size-larger": 1,
"wysiwyg-font-size-medium": 1,
"wysiwyg-font-size-small": 1,
"wysiwyg-font-size-smaller": 1,
"wysiwyg-font-size-x-large": 1,
"wysiwyg-font-size-x-small": 1,
"wysiwyg-font-size-xx-large": 1,
"wysiwyg-font-size-xx-small": 1,
"wysiwyg-text-align-center": 1,
"wysiwyg-text-align-justify": 1,
"wysiwyg-text-align-left": 1,
"wysiwyg-text-align-right": 1
},
"type_definitions": {
"visible_content_object": {
"methods": {
"has_visible_contet": 1
}
},
"alignment_object": {
"classes": {
"wysiwyg-text-align-center": 1,
"wysiwyg-text-align-justify": 1,
"wysiwyg-text-align-left": 1,
"wysiwyg-text-align-right": 1,
"wysiwyg-float-left": 1,
"wysiwyg-float-right": 1
},
"styles": {
"float": ["left", "right"],
"text-align": ["left", "right", "center"]
}
},
"valid_image_src": {
"attrs": {
"src": /^[^data\:]/i
}
},
"text_color_object": {
"styles": {
"color": true,
"background-color": true
}
},
"text_fontsize_object": {
"styles": {
"font-size": true
}
},
"text_formatting_object": {
"classes": {
"wysiwyg-color-aqua": 1,
"wysiwyg-color-black": 1,
"wysiwyg-color-blue": 1,
"wysiwyg-color-fuchsia": 1,
"wysiwyg-color-gray": 1,
"wysiwyg-color-green": 1,
"wysiwyg-color-lime": 1,
"wysiwyg-color-maroon": 1,
"wysiwyg-color-navy": 1,
"wysiwyg-color-olive": 1,
"wysiwyg-color-purple": 1,
"wysiwyg-color-red": 1,
"wysiwyg-color-silver": 1,
"wysiwyg-color-teal": 1,
"wysiwyg-color-white": 1,
"wysiwyg-color-yellow": 1,
"wysiwyg-font-size-large": 1,
"wysiwyg-font-size-larger": 1,
"wysiwyg-font-size-medium": 1,
"wysiwyg-font-size-small": 1,
"wysiwyg-font-size-smaller": 1,
"wysiwyg-font-size-x-large": 1,
"wysiwyg-font-size-x-small": 1,
"wysiwyg-font-size-xx-large": 1,
"wysiwyg-font-size-xx-small": 1
}
}
},
"comments": 1, // if set allows comments to pass
/**
* Tag list
*
* The following options are available:
*
* - add_class: converts and deletes the given HTML4 attribute (align, clear, ...) via the given method to a css class
* The following methods are implemented in wysihtml.dom.parse:
* - align_text: converts align attribute values (right/left/center/justify) to their corresponding css class "wysiwyg-text-align-*")
* <p align="center">foo</p> ... becomes ... <p class="wysiwyg-text-align-center">foo</p>
* - clear_br: converts clear attribute values left/right/all/both to their corresponding css class "wysiwyg-clear-*"
* <br clear="all"> ... becomes ... <br class="wysiwyg-clear-both">
* - align_img: converts align attribute values (right/left) on <img> to their corresponding css class "wysiwyg-float-*"
*
* - add_style: converts and deletes the given HTML4 attribute (align) via the given method to a css style
* The following methods are implemented in wysihtml.dom.parse:
* - align_text: converts align attribute values (right/left/center) to their corresponding css style)
* <p align="center">foo</p> ... becomes ... <p style="text-align:center">foo</p>
*
* - remove: removes the element and its content
*
* - unwrap removes element but leaves content
*
* - rename_tag: renames the element to the given tag
*
* - set_class: adds the given class to the element (note: make sure that the class is in the "classes" white list above)
*
* - set_attributes: sets/overrides the given attributes
*
* - check_attributes: checks the given HTML attribute via the given method
* - url: allows only valid urls (starting with http:// or https://)
* - src: allows something like "/foobar.jpg", "http://google.com", ...
* - href: allows something like "mailto:bert@foo.com", "http://google.com", "/foobar.jpg"
* - alt: strips unwanted characters. if the attribute is not set, then it gets set (to ensure valid and compatible HTML)
* - numbers: ensures that the attribute only contains numeric (integer) characters (no float values or units)
* - dimension: for with/height attributes where floating point numbrs and percentages are allowed
* - any: allows anything to pass
*/
"tags": {
"tr": {
"add_class": {
"align": "align_text"
}
},
"strike": {
"unwrap": 1
},
"form": {
"unwrap": 1
},
"rt": {
"rename_tag": "span"
},
"code": {},
"acronym": {
"rename_tag": "span"
},
"br": {
"add_class": {
"clear": "clear_br"
}
},
"details": {
"unwrap": 1
},
"h4": {
"add_class": {
"align": "align_text"
}
},
"em": {},
"title": {
"remove": 1
},
"multicol": {
"unwrap": 1
},
"figure": {
"unwrap": 1
},
"xmp": {
"unwrap": 1
},
"small": {
"rename_tag": "span",
"set_class": "wysiwyg-font-size-smaller"
},
"area": {
"remove": 1
},
"time": {
"unwrap": 1
},
"dir": {
"rename_tag": "ul"
},
"bdi": {
"unwrap": 1
},
"command": {
"unwrap": 1
},
"ul": {},
"progress": {
"rename_tag": "span"
},
"dfn": {
"unwrap": 1
},
"iframe": {
"remove": 1
},
"figcaption": {
"unwrap": 1
},
"a": {
"check_attributes": {
"href": "href", // if you compiled master manually then change this from 'url' to 'href'
"target": "any"
},
"set_attributes": {
"rel": "nofollow"
}
},
"img": {
"one_of_type": {
"valid_image_src": 1
},
"check_attributes": {
"width": "dimension",
"alt": "alt",
"src": "src", // if you compiled master manually then change this from 'url' to 'src'
"height": "dimension"
},
"add_class": {
"align": "align_img"
}
},
"rb": {
"unwrap": 1
},
"footer": {
"rename_tag": "div"
},
"noframes": {
"remove": 1
},
"abbr": {
"unwrap": 1
},
"u": {},
"bgsound": {
"remove": 1
},
"sup": {},
"address": {
"unwrap": 1
},
"basefont": {
"remove": 1
},
"nav": {
"unwrap": 1
},
"h1": {
"add_class": {
"align": "align_text"
}
},
"head": {
"unwrap": 1
},
"tbody": {
"add_class": {
"align": "align_text"
}
},
"dd": {
"unwrap": 1
},
"s": {
"unwrap": 1
},
"li": {},
"td": {
"check_attributes": {
"rowspan": "numbers",
"colspan": "numbers",
"valign": "any",
"align": "any"
},
"add_class": {
"align": "align_text"
}
},
"object": {
"remove": 1
},
"div": {
"one_of_type": {
"visible_content_object": 1
},
"remove_action": "unwrap",
"keep_styles": {
"textAlign": 1,
"float": 1
},
"add_class": {
"align": "align_text"
}
},
"option": {
"remove":1
},
"select": {
"remove":1
},
"i": {},
"track": {
"remove": 1
},
"wbr": {
"remove": 1
},
"fieldset": {
"unwrap": 1
},
"big": {
"rename_tag": "span",
"set_class": "wysiwyg-font-size-larger"
},
"button": {
"unwrap": 1
},
"noscript": {
"remove": 1
},
"svg": {
"remove": 1
},
"input": {
"remove": 1
},
"table": {},
"keygen": {
"remove": 1
},
"h5": {
"add_class": {
"align": "align_text"
}
},
"meta": {
"remove": 1
},
"map": {
"remove": 1
},
"isindex": {
"remove": 1
},
"mark": {
"unwrap": 1
},
"caption": {
"add_class": {
"align": "align_text"
}
},
"tfoot": {
"add_class": {
"align": "align_text"
}
},
"base": {
"remove": 1
},
"video": {
"remove": 1
},
"strong": {},
"canvas": {
"remove": 1
},
"output": {
"unwrap": 1
},
"marquee": {
"unwrap": 1
},
"b": {},
"q": {
"check_attributes": {
"cite": "url"
}
},
"applet": {
"remove": 1
},
"span": {
"one_of_type": {
"text_formatting_object": 1,
"text_color_object": 1,
"text_fontsize_object": 1
},
"keep_styles": {
"color": 1,
"backgroundColor": 1,
"fontSize": 1
},
"remove_action": "unwrap"
},
"rp": {
"unwrap": 1
},
"spacer": {
"remove": 1
},
"source": {
"remove": 1
},
"aside": {
"rename_tag": "div"
},
"frame": {
"remove": 1
},
"section": {
"rename_tag": "div"
},
"body": {
"unwrap": 1
},
"ol": {},
"nobr": {
"unwrap": 1
},
"html": {
"unwrap": 1
},
"summary": {
"unwrap": 1
},
"var": {
"unwrap": 1
},
"del": {
"unwrap": 1
},
"blockquote": {
"check_attributes": {
"cite": "url"
}
},
"style": {
"remove": 1
},
"device": {
"remove": 1
},
"meter": {
"unwrap": 1
},
"h3": {
"add_class": {
"align": "align_text"
}
},
"textarea": {
"unwrap": 1
},
"embed": {
"remove": 1
},
"hgroup": {
"unwrap": 1
},
"font": {
"rename_tag": "span",
"add_class": {
"size": "size_font"
}
},
"tt": {
"unwrap": 1
},
"noembed": {
"remove": 1
},
"thead": {
"add_class": {
"align": "align_text"
}
},
"blink": {
"unwrap": 1
},
"plaintext": {
"unwrap": 1
},
"xml": {
"remove": 1
},
"h6": {
"add_class": {
"align": "align_text"
}
},
"param": {
"remove": 1
},
"th": {
"check_attributes": {
"rowspan": "numbers",
"colspan": "numbers"
},
"add_class": {
"align": "align_text"
}
},
"legend": {
"unwrap": 1
},
"hr": {},
"label": {
"unwrap": 1
},
"dl": {
"unwrap": 1
},
"kbd": {
"unwrap": 1
},
"listing": {
"unwrap": 1
},
"dt": {
"unwrap": 1
},
"nextid": {
"remove": 1
},
"pre": {},
"center": {
"rename_tag": "div",
"set_class": "wysiwyg-text-align-center"
},
"audio": {
"remove": 1
},
"datalist": {
"unwrap": 1
},
"samp": {
"unwrap": 1
},
"col": {
"remove": 1
},
"article": {
"rename_tag": "div"
},
"cite": {},
"link": {
"remove": 1
},
"script": {
"remove": 1
},
"bdo": {
"unwrap": 1
},
"menu": {
"rename_tag": "ul"
},
"colgroup": {
"remove": 1
},
"ruby": {
"unwrap": 1
},
"h2": {
"add_class": {
"align": "align_text"
}
},
"ins": {
"unwrap": 1
},
"p": {
"add_class": {
"align": "align_text"
}
},
"sub": {},
"comment": {
"remove": 1
},
"frameset": {
"remove": 1
},
"optgroup": {
"unwrap": 1
},
"header": {
"rename_tag": "div"
}
}
};

View file

@ -0,0 +1,32 @@
/**
* Very simple basic rule set
*
* Allows
* <i>, <em>, <b>, <strong>, <p>, <div>, <a href="http://foo"></a>, <br>, <span>, <ol>, <ul>, <li>
*
* For a proper documentation of the format check advanced.js
*/
var wysihtmlParserRules = {
tags: {
strong: {},
b: {},
i: {},
em: {},
br: {},
p: {},
div: {},
span: {},
ul: {},
ol: {},
li: {},
a: {
set_attributes: {
target: "_blank",
rel: "nofollow"
},
check_attributes: {
href: "url" // important to avoid XSS
}
}
}
};

View file

@ -0,0 +1,602 @@
wysihtml.commands.alignCenterStyle = (function() {
var nodeOptions = {
styleProperty: "textAlign",
styleValue: "center",
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
wysihtml.commands.alignJustifyStyle = (function() {
var nodeOptions = {
styleProperty: "textAlign",
styleValue: "justify",
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
wysihtml.commands.alignLeftStyle = (function() {
var nodeOptions = {
styleProperty: "textAlign",
styleValue: "left",
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
wysihtml.commands.alignRightStyle = (function() {
var nodeOptions = {
styleProperty: "textAlign",
styleValue: "right",
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
/* Sets text background color by inline styles */
wysihtml.commands.bgColorStyle = (function() {
return {
exec: function(composer, command, color) {
var colorVals = wysihtml.quirks.styleParser.parseColor("background-color:" + (color.color || color), "background-color"),
colString;
if (colorVals) {
colString = (colorVals[3] === 1 ? "rgb(" + [colorVals[0], colorVals[1], colorVals[2]].join(', ') : "rgba(" + colorVals.join(', ')) + ')';
wysihtml.commands.formatInline.exec(composer, command, {styleProperty: 'backgroundColor', styleValue: colString});
}
},
state: function(composer, command, color) {
var colorVals = color ? wysihtml.quirks.styleParser.parseColor("background-color:" + (color.color || color), "background-color") : null,
colString;
if (colorVals) {
colString = (colorVals[3] === 1 ? "rgb(" + [colorVals[0], colorVals[1], colorVals[2]].join(', ') : "rgba(" + colorVals.join(', ')) + ')';
}
return wysihtml.commands.formatInline.state(composer, command, {styleProperty: 'backgroundColor', styleValue: colString});
},
remove: function(composer, command) {
return wysihtml.commands.formatInline.remove(composer, command, {styleProperty: 'backgroundColor'});
},
stateValue: function(composer, command, props) {
var st = this.state(composer, command),
colorStr,
val = false;
if (st && wysihtml.lang.object(st).isArray()) {
st = st[0];
}
if (st) {
colorStr = st.getAttribute('style');
if (colorStr) {
val = wysihtml.quirks.styleParser.parseColor(colorStr, "background-color");
return wysihtml.quirks.styleParser.unparseColor(val, props);
}
}
return false;
}
};
})();
wysihtml.commands.bold = (function() {
var nodeOptions = {
nodeName: "B",
toggle: true
};
return {
exec: function(composer, command) {
wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
}
};
})();
/* Formats block for as a <pre><code class="classname"></code></pre> block
* Useful in conjuction for sytax highlight utility: highlight.js
*
* Usage:
*
* editorInstance.composer.commands.exec("formatCode", "language-html");
*/
wysihtml.commands.formatCode = (function() {
return {
exec: function(composer, command, classname) {
var pre = this.state(composer)[0],
code, range, selectedNodes;
if (pre) {
// caret is already within a <pre><code>...</code></pre>
composer.selection.executeAndRestore(function() {
code = pre.querySelector("code");
wysihtml.dom.replaceWithChildNodes(pre);
if (code) {
wysihtml.dom.replaceWithChildNodes(code);
}
});
} else {
// Wrap in <pre><code>...</code></pre>
range = composer.selection.getRange();
selectedNodes = range.extractContents();
pre = composer.doc.createElement("pre");
code = composer.doc.createElement("code");
if (classname) {
code.className = classname;
}
pre.appendChild(code);
code.appendChild(selectedNodes);
range.insertNode(pre);
composer.selection.selectNode(pre);
}
},
state: function(composer) {
var selectedNode = composer.selection.getSelectedNode(), node;
if (selectedNode && selectedNode.nodeName && selectedNode.nodeName == "PRE"&&
selectedNode.firstChild && selectedNode.firstChild.nodeName && selectedNode.firstChild.nodeName == "CODE") {
return [selectedNode];
} else {
node = wysihtml.dom.getParentElement(selectedNode, { query: "pre code" });
return node ? [node.parentNode] : false;
}
}
};
})();
/**
* Inserts an <img>
* If selection is already an image link, it removes it
*
* @example
* // either ...
* wysihtml.commands.insertImage.exec(composer, "insertImage", "http://www.google.de/logo.jpg");
* // ... or ...
* wysihtml.commands.insertImage.exec(composer, "insertImage", { src: "http://www.google.de/logo.jpg", title: "foo" });
*/
wysihtml.commands.insertImage = (function() {
var NODE_NAME = "IMG";
return {
exec: function(composer, command, value) {
value = typeof(value) === "object" ? value : { src: value };
var doc = composer.doc,
image = this.state(composer),
textNode,
parent;
// If image is selected and src ie empty, set the caret before it and delete the image
if (image && !value.src) {
composer.selection.setBefore(image);
parent = image.parentNode;
parent.removeChild(image);
// and it's parent <a> too if it hasn't got any other relevant child nodes
wysihtml.dom.removeEmptyTextNodes(parent);
if (parent.nodeName === "A" && !parent.firstChild) {
composer.selection.setAfter(parent);
parent.parentNode.removeChild(parent);
}
// firefox and ie sometimes don't remove the image handles, even though the image got removed
wysihtml.quirks.redraw(composer.element);
return;
}
// If image selected change attributes accordingly
if (image) {
for (var key in value) {
if (value.hasOwnProperty(key)) {
image.setAttribute(key === "className" ? "class" : key, value[key]);
}
}
return;
}
// Otherwise lets create the image
image = doc.createElement(NODE_NAME);
for (var i in value) {
image.setAttribute(i === "className" ? "class" : i, value[i]);
}
composer.selection.insertNode(image);
if (wysihtml.browser.hasProblemsSettingCaretAfterImg()) {
textNode = doc.createTextNode(wysihtml.INVISIBLE_SPACE);
composer.selection.insertNode(textNode);
composer.selection.setAfter(textNode);
} else {
composer.selection.setAfter(image);
}
},
state: function(composer) {
var doc = composer.doc,
selectedNode,
text,
imagesInSelection;
if (!wysihtml.dom.hasElementWithTagName(doc, NODE_NAME)) {
return false;
}
selectedNode = composer.selection.getSelectedNode();
if (!selectedNode) {
return false;
}
if (selectedNode.nodeName === NODE_NAME) {
// This works perfectly in IE
return selectedNode;
}
if (selectedNode.nodeType !== wysihtml.ELEMENT_NODE) {
return false;
}
text = composer.selection.getText();
text = wysihtml.lang.string(text).trim();
if (text) {
return false;
}
imagesInSelection = composer.selection.getNodes(wysihtml.ELEMENT_NODE, function(node) {
return node.nodeName === "IMG";
});
if (imagesInSelection.length !== 1) {
return false;
}
return imagesInSelection[0];
}
};
})();
wysihtml.commands.fontSize = (function() {
var REG_EXP = /wysiwyg-font-size-[0-9a-z\-]+/g;
return {
exec: function(composer, command, size) {
wysihtml.commands.formatInline.exec(composer, command, {className: "wysiwyg-font-size-" + size, classRegExp: REG_EXP, toggle: true});
},
state: function(composer, command, size) {
return wysihtml.commands.formatInline.state(composer, command, {className: "wysiwyg-font-size-" + size});
}
};
})();
/* Set font size by inline style */
wysihtml.commands.fontSizeStyle = (function() {
return {
exec: function(composer, command, size) {
size = size.size || size;
if (!(/^\s*$/).test(size)) {
wysihtml.commands.formatInline.exec(composer, command, {styleProperty: "fontSize", styleValue: size, toggle: false});
}
},
state: function(composer, command, size) {
return wysihtml.commands.formatInline.state(composer, command, {styleProperty: "fontSize", styleValue: size || undefined});
},
remove: function(composer, command) {
return wysihtml.commands.formatInline.remove(composer, command, {styleProperty: "fontSize"});
},
stateValue: function(composer, command) {
var styleStr,
st = this.state(composer, command);
if (st && wysihtml.lang.object(st).isArray()) {
st = st[0];
}
if (st) {
styleStr = st.getAttribute("style");
if (styleStr) {
return wysihtml.quirks.styleParser.parseFontSize(styleStr);
}
}
return false;
}
};
})();
wysihtml.commands.foreColor = (function() {
var REG_EXP = /wysiwyg-color-[0-9a-z]+/g;
return {
exec: function(composer, command, color) {
wysihtml.commands.formatInline.exec(composer, command, {className: "wysiwyg-color-" + color, classRegExp: REG_EXP, toggle: true});
},
state: function(composer, command, color) {
return wysihtml.commands.formatInline.state(composer, command, {className: "wysiwyg-color-" + color});
}
};
})();
/* Sets text color by inline styles */
wysihtml.commands.foreColorStyle = (function() {
return {
exec: function(composer, command, color) {
var colorVals, colString;
if (!color) { return; }
colorVals = wysihtml.quirks.styleParser.parseColor("color:" + (color.color || color), "color");
if (colorVals) {
colString = (colorVals[3] === 1 ? "rgb(" + [colorVals[0], colorVals[1], colorVals[2]].join(", ") : "rgba(" + colorVals.join(', ')) + ')';
wysihtml.commands.formatInline.exec(composer, command, {styleProperty: "color", styleValue: colString});
}
},
state: function(composer, command, color) {
var colorVals = color ? wysihtml.quirks.styleParser.parseColor("color:" + (color.color || color), "color") : null,
colString;
if (colorVals) {
colString = (colorVals[3] === 1 ? "rgb(" + [colorVals[0], colorVals[1], colorVals[2]].join(", ") : "rgba(" + colorVals.join(', ')) + ')';
}
return wysihtml.commands.formatInline.state(composer, command, {styleProperty: "color", styleValue: colString});
},
remove: function(composer, command) {
return wysihtml.commands.formatInline.remove(composer, command, {styleProperty: "color"});
},
stateValue: function(composer, command, props) {
var st = this.state(composer, command),
colorStr,
val = false;
if (st && wysihtml.lang.object(st).isArray()) {
st = st[0];
}
if (st) {
colorStr = st.getAttribute("style");
if (colorStr) {
val = wysihtml.quirks.styleParser.parseColor(colorStr, "color");
return wysihtml.quirks.styleParser.unparseColor(val, props);
}
}
return false;
}
};
})();
wysihtml.commands.insertBlockQuote = (function() {
var nodeOptions = {
nodeName: "BLOCKQUOTE",
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
wysihtml.commands.insertOrderedList = (function() {
return {
exec: function(composer, command) {
wysihtml.commands.insertList.exec(composer, command, "OL");
},
state: function(composer, command) {
return wysihtml.commands.insertList.state(composer, command, "OL");
}
};
})();
wysihtml.commands.insertUnorderedList = (function() {
return {
exec: function(composer, command) {
wysihtml.commands.insertList.exec(composer, command, "UL");
},
state: function(composer, command) {
return wysihtml.commands.insertList.state(composer, command, "UL");
}
};
})();
wysihtml.commands.italic = (function() {
var nodeOptions = {
nodeName: "I",
toggle: true
};
return {
exec: function(composer, command) {
wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
}
};
})();
wysihtml.commands.justifyCenter = (function() {
var nodeOptions = {
className: "wysiwyg-text-align-center",
classRegExp: /wysiwyg-text-align-[0-9a-z]+/g,
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
wysihtml.commands.justifyFull = (function() {
var nodeOptions = {
className: "wysiwyg-text-align-justify",
classRegExp: /wysiwyg-text-align-[0-9a-z]+/g,
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
wysihtml.commands.justifyLeft = (function() {
var nodeOptions = {
className: "wysiwyg-text-align-left",
classRegExp: /wysiwyg-text-align-[0-9a-z]+/g,
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
wysihtml.commands.justifyRight = (function() {
var nodeOptions = {
className: "wysiwyg-text-align-right",
classRegExp: /wysiwyg-text-align-[0-9a-z]+/g,
toggle: true
};
return {
exec: function(composer, command) {
return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
}
};
})();
wysihtml.commands.subscript = (function() {
var nodeOptions = {
nodeName: "SUB",
toggle: true
};
return {
exec: function(composer, command) {
wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
}
};
})();
wysihtml.commands.superscript = (function() {
var nodeOptions = {
nodeName: "SUP",
toggle: true
};
return {
exec: function(composer, command) {
wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
}
};
})();
wysihtml.commands.underline = (function() {
var nodeOptions = {
nodeName: "U",
toggle: true
};
return {
exec: function(composer, command) {
wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
},
state: function(composer, command) {
return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
}
};
})();

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,850 @@
/**
* Toolbar Dialog
*
* @param {Element} link The toolbar link which causes the dialog to show up
* @param {Element} container The dialog container
*
* @example
* <!-- Toolbar link -->
* <a data-wysihtml-command="insertImage">insert an image</a>
*
* <!-- Dialog -->
* <div data-wysihtml-dialog="insertImage" style="display: none;">
* <label>
* URL: <input data-wysihtml-dialog-field="src" value="http://">
* </label>
* <label>
* Alternative text: <input data-wysihtml-dialog-field="alt" value="">
* </label>
* </div>
*
* <script>
* var dialog = new wysihtml.toolbar.Dialog(
* document.querySelector("[data-wysihtml-command='insertImage']"),
* document.querySelector("[data-wysihtml-dialog='insertImage']")
* );
* dialog.observe("save", function(attributes) {
* // do something
* });
* </script>
*/
(function(wysihtml) {
var dom = wysihtml.dom,
CLASS_NAME_OPENED = "wysihtml-command-dialog-opened",
SELECTOR_FORM_ELEMENTS = "input, select, textarea",
SELECTOR_FIELDS = "[data-wysihtml-dialog-field]",
ATTRIBUTE_FIELDS = "data-wysihtml-dialog-field";
wysihtml.toolbar.Dialog = wysihtml.lang.Dispatcher.extend(
/** @scope wysihtml.toolbar.Dialog.prototype */ {
constructor: function(link, container) {
this.link = link;
this.container = container;
},
_observe: function() {
if (this._observed) {
return;
}
var that = this,
callbackWrapper = function(event) {
var attributes = that._serialize();
that.fire("save", attributes);
that.hide();
event.preventDefault();
event.stopPropagation();
};
dom.observe(that.link, "click", function() {
if (dom.hasClass(that.link, CLASS_NAME_OPENED)) {
setTimeout(function() { that.hide(); }, 0);
}
});
dom.observe(this.container, "keydown", function(event) {
var keyCode = event.keyCode;
if (keyCode === wysihtml.ENTER_KEY) {
callbackWrapper(event);
}
if (keyCode === wysihtml.ESCAPE_KEY) {
that.cancel();
}
});
dom.delegate(this.container, "[data-wysihtml-dialog-action=save]", "click", callbackWrapper);
dom.delegate(this.container, "[data-wysihtml-dialog-action=cancel]", "click", function(event) {
that.cancel();
event.preventDefault();
event.stopPropagation();
});
this._observed = true;
},
/**
* Grabs all fields in the dialog and puts them in key=>value style in an object which
* then gets returned
*/
_serialize: function() {
var data = {},
fields = this.container.querySelectorAll(SELECTOR_FIELDS),
length = fields.length,
i = 0;
for (; i<length; i++) {
data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
}
return data;
},
/**
* Takes the attributes of the "elementToChange"
* and inserts them in their corresponding dialog input fields
*
* Assume the "elementToChange" looks like this:
* <a href="http://www.google.com" target="_blank">foo</a>
*
* and we have the following dialog:
* <input type="text" data-wysihtml-dialog-field="href" value="">
* <input type="text" data-wysihtml-dialog-field="target" value="">
*
* after calling _interpolate() the dialog will look like this
* <input type="text" data-wysihtml-dialog-field="href" value="http://www.google.com">
* <input type="text" data-wysihtml-dialog-field="target" value="_blank">
*
* Basically it adopted the attribute values into the corresponding input fields
*
*/
_interpolate: function(avoidHiddenFields) {
var field,
fieldName,
newValue,
focusedElement = document.querySelector(":focus"),
fields = this.container.querySelectorAll(SELECTOR_FIELDS),
length = fields.length,
i = 0;
for (; i<length; i++) {
field = fields[i];
// Never change elements where the user is currently typing in
if (field === focusedElement) {
continue;
}
// Don't update hidden fields
// See https://github.com/xing/wysihtml5/pull/14
if (avoidHiddenFields && field.type === "hidden") {
continue;
}
fieldName = field.getAttribute(ATTRIBUTE_FIELDS);
newValue = (this.elementToChange && typeof(this.elementToChange) !== 'boolean') ? (this.elementToChange.getAttribute(fieldName) || "") : field.defaultValue;
field.value = newValue;
}
},
update: function (elementToChange) {
this.elementToChange = elementToChange ? elementToChange : this.elementToChange;
this._interpolate();
},
/**
* Show the dialog element
*/
show: function(elementToChange) {
var firstField = this.container.querySelector(SELECTOR_FORM_ELEMENTS);
this._observe();
this.update(elementToChange);
dom.addClass(this.link, CLASS_NAME_OPENED);
this.container.style.display = "";
this.isOpen = true;
this.fire("show");
if (firstField && !elementToChange) {
try {
firstField.focus();
} catch(e) {}
}
},
/**
* Hide the dialog element
*/
_hide: function(focus) {
this.elementToChange = null;
dom.removeClass(this.link, CLASS_NAME_OPENED);
this.container.style.display = "none";
this.isOpen = false;
},
hide: function() {
this._hide();
this.fire("hide");
},
cancel: function() {
this._hide();
this.fire("cancel");
}
});
})(wysihtml); //jshint ignore:line
(function(wysihtml) {
var dom = wysihtml.dom,
SELECTOR_FIELDS = "[data-wysihtml-dialog-field]",
ATTRIBUTE_FIELDS = "data-wysihtml-dialog-field";
wysihtml.toolbar.Dialog_bgColorStyle = wysihtml.toolbar.Dialog.extend({
multiselect: true,
_serialize: function() {
var data = {},
fields = this.container.querySelectorAll(SELECTOR_FIELDS),
length = fields.length,
i = 0;
for (; i<length; i++) {
data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
}
return data;
},
_interpolate: function(avoidHiddenFields) {
var field,
fieldName,
newValue,
focusedElement = document.querySelector(":focus"),
fields = this.container.querySelectorAll(SELECTOR_FIELDS),
length = fields.length,
i = 0,
firstElement = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
colorStr = (firstElement) ? firstElement.getAttribute('style') : null,
color = (colorStr) ? wysihtml.quirks.styleParser.parseColor(colorStr, "background-color") : null;
for (; i<length; i++) {
field = fields[i];
// Never change elements where the user is currently typing in
if (field === focusedElement) {
continue;
}
// Don't update hidden fields3
if (avoidHiddenFields && field.type === "hidden") {
continue;
}
if (field.getAttribute(ATTRIBUTE_FIELDS) === "color") {
if (color) {
if (color[3] && color[3] != 1) {
field.value = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ");";
} else {
field.value = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ");";
}
} else {
field.value = "rgb(0,0,0);";
}
}
}
}
});
})(wysihtml);
(function(wysihtml) {
wysihtml.toolbar.Dialog_createTable = wysihtml.toolbar.Dialog.extend({
show: function(elementToChange) {
this.base(elementToChange);
}
});
})(wysihtml);
(function(wysihtml) {
var dom = wysihtml.dom,
SELECTOR_FIELDS = "[data-wysihtml-dialog-field]",
ATTRIBUTE_FIELDS = "data-wysihtml-dialog-field";
wysihtml.toolbar.Dialog_fontSizeStyle = wysihtml.toolbar.Dialog.extend({
multiselect: true,
_serialize: function() {
return {"size" : this.container.querySelector('[data-wysihtml-dialog-field="size"]').value};
},
_interpolate: function(avoidHiddenFields) {
var focusedElement = document.querySelector(":focus"),
field = this.container.querySelector("[data-wysihtml-dialog-field='size']"),
firstElement = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
styleStr = (firstElement) ? firstElement.getAttribute('style') : null,
size = (styleStr) ? wysihtml.quirks.styleParser.parseFontSize(styleStr) : null;
if (field && field !== focusedElement && size && !(/^\s*$/).test(size)) {
field.value = size;
}
}
});
})(wysihtml);
(function(wysihtml) {
var SELECTOR_FIELDS = "[data-wysihtml-dialog-field]",
ATTRIBUTE_FIELDS = "data-wysihtml-dialog-field";
wysihtml.toolbar.Dialog_foreColorStyle = wysihtml.toolbar.Dialog.extend({
multiselect: true,
_serialize: function() {
var data = {},
fields = this.container.querySelectorAll(SELECTOR_FIELDS),
length = fields.length,
i = 0;
for (; i<length; i++) {
data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
}
return data;
},
_interpolate: function(avoidHiddenFields) {
var field, colourMode,
styleParser = wysihtml.quirks.styleParser,
focusedElement = document.querySelector(":focus"),
fields = this.container.querySelectorAll(SELECTOR_FIELDS),
length = fields.length,
i = 0,
firstElement = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
colourStr = (firstElement) ? firstElement.getAttribute("style") : null,
colour = (colourStr) ? styleParser.parseColor(colourStr, "color") : null;
for (; i<length; i++) {
field = fields[i];
// Never change elements where the user is currently typing in
if (field === focusedElement) {
continue;
}
// Don't update hidden fields3
if (avoidHiddenFields && field.type === "hidden") {
continue;
}
if (field.getAttribute(ATTRIBUTE_FIELDS) === "color") {
colourMode = (field.dataset.colormode || "rgb").toLowerCase();
colourMode = colourMode === "hex" ? "hash" : colourMode;
if (colour) {
field.value = styleParser.unparseColor(colour, colourMode);
} else {
field.value = styleParser.unparseColor([0, 0, 0], colourMode);
}
}
}
}
});
})(wysihtml);
/**
* Converts speech-to-text and inserts this into the editor
* As of now (2011/03/25) this only is supported in Chrome >= 11
*
* Note that it sends the recorded audio to the google speech recognition api:
* http://stackoverflow.com/questions/4361826/does-chrome-have-buil-in-speech-recognition-for-input-type-text-x-webkit-speec
*
* Current HTML5 draft can be found here
* http://lists.w3.org/Archives/Public/public-xg-htmlspeech/2011Feb/att-0020/api-draft.html
*
* "Accessing Google Speech API Chrome 11"
* http://mikepultz.com/2011/03/accessing-google-speech-api-chrome-11/
*/
(function(wysihtml) {
var dom = wysihtml.dom;
var linkStyles = {
position: "relative"
};
var wrapperStyles = {
left: 0,
margin: 0,
opacity: 0,
overflow: "hidden",
padding: 0,
position: "absolute",
top: 0,
zIndex: 1
};
var inputStyles = {
cursor: "inherit",
fontSize: "50px",
height: "50px",
marginTop: "-25px",
outline: 0,
padding: 0,
position: "absolute",
right: "-4px",
top: "50%"
};
var inputAttributes = {
"x-webkit-speech": "",
"speech": ""
};
wysihtml.toolbar.Speech = function(parent, link) {
var input = document.createElement("input");
if (!wysihtml.browser.supportsSpeechApiOn(input)) {
link.style.display = "none";
return;
}
var lang = parent.editor.textarea.element.getAttribute("lang");
if (lang) {
inputAttributes.lang = lang;
}
var wrapper = document.createElement("div");
wysihtml.lang.object(wrapperStyles).merge({
width: link.offsetWidth + "px",
height: link.offsetHeight + "px"
});
dom.insert(input).into(wrapper);
dom.insert(wrapper).into(link);
dom.setStyles(inputStyles).on(input);
dom.setAttributes(inputAttributes).on(input);
dom.setStyles(wrapperStyles).on(wrapper);
dom.setStyles(linkStyles).on(link);
var eventName = "onwebkitspeechchange" in input ? "webkitspeechchange" : "speechchange";
dom.observe(input, eventName, function() {
parent.execCommand("insertText", input.value);
input.value = "";
});
dom.observe(input, "click", function(event) {
if (dom.hasClass(link, "wysihtml-command-disabled")) {
event.preventDefault();
}
event.stopPropagation();
});
};
})(wysihtml);
/**
* Toolbar
*
* @param {Object} parent Reference to instance of Editor instance
* @param {Element} container Reference to the toolbar container element
*
* @example
* <div id="toolbar">
* <a data-wysihtml-command="createLink">insert link</a>
* <a data-wysihtml-command="formatBlock" data-wysihtml-command-value="h1">insert h1</a>
* </div>
*
* <script>
* var toolbar = new wysihtml.toolbar.Toolbar(editor, document.getElementById("toolbar"));
* </script>
*/
(function(wysihtml) {
var CLASS_NAME_COMMAND_DISABLED = "wysihtml-command-disabled",
CLASS_NAME_COMMANDS_DISABLED = "wysihtml-commands-disabled",
CLASS_NAME_COMMAND_ACTIVE = "wysihtml-command-active",
CLASS_NAME_ACTION_ACTIVE = "wysihtml-action-active",
dom = wysihtml.dom;
wysihtml.toolbar.Toolbar = Base.extend(
/** @scope wysihtml.toolbar.Toolbar.prototype */ {
constructor: function(editor, container, showOnInit) {
this.editor = editor;
this.container = typeof(container) === "string" ? document.getElementById(container) : container;
this.composer = editor.composer;
this._getLinks("command");
this._getLinks("action");
this._observe();
if (showOnInit) { this.show(); }
if (editor.config.classNameCommandDisabled != null) {
CLASS_NAME_COMMAND_DISABLED = editor.config.classNameCommandDisabled;
}
if (editor.config.classNameCommandsDisabled != null) {
CLASS_NAME_COMMANDS_DISABLED = editor.config.classNameCommandsDisabled;
}
if (editor.config.classNameCommandActive != null) {
CLASS_NAME_COMMAND_ACTIVE = editor.config.classNameCommandActive;
}
if (editor.config.classNameActionActive != null) {
CLASS_NAME_ACTION_ACTIVE = editor.config.classNameActionActive;
}
var speechInputLinks = this.container.querySelectorAll("[data-wysihtml-command=insertSpeech]"),
length = speechInputLinks.length,
i = 0;
for (; i<length; i++) {
new wysihtml.toolbar.Speech(this, speechInputLinks[i]);
}
},
_getLinks: function(type) {
var links = this[type + "Links"] = wysihtml.lang.array(this.container.querySelectorAll("[data-wysihtml-" + type + "]")).get(),
length = links.length,
i = 0,
mapping = this[type + "Mapping"] = {},
link,
group,
name,
value,
dialog,
tracksBlankValue;
for (; i<length; i++) {
link = links[i];
name = link.getAttribute("data-wysihtml-" + type);
value = link.getAttribute("data-wysihtml-" + type + "-value");
tracksBlankValue = link.getAttribute("data-wysihtml-" + type + "-blank-value");
group = this.container.querySelector("[data-wysihtml-" + type + "-group='" + name + "']");
dialog = this._getDialog(link, name);
mapping[name + ":" + value] = {
link: link,
group: group,
name: name,
value: value,
tracksBlankValue: tracksBlankValue,
dialog: dialog,
state: false
};
}
},
_getDialog: function(link, command) {
var that = this,
dialogElement = this.container.querySelector("[data-wysihtml-dialog='" + command + "']"),
dialog, caretBookmark;
if (dialogElement) {
if (wysihtml.toolbar["Dialog_" + command]) {
dialog = new wysihtml.toolbar["Dialog_" + command](link, dialogElement);
} else {
dialog = new wysihtml.toolbar.Dialog(link, dialogElement);
}
dialog.on("show", function() {
caretBookmark = that.composer.selection.getBookmark();
that.editor.fire("show:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
});
dialog.on("save", function(attributes) {
if (caretBookmark) {
that.composer.selection.setBookmark(caretBookmark);
}
that._execCommand(command, attributes);
that.editor.fire("save:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
that._hideAllDialogs();
that._preventInstantFocus();
caretBookmark = undefined;
});
dialog.on("cancel", function() {
if (caretBookmark) {
that.composer.selection.setBookmark(caretBookmark);
}
that.editor.fire("cancel:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
caretBookmark = undefined;
that._preventInstantFocus();
});
dialog.on("hide", function() {
that.editor.fire("hide:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
caretBookmark = undefined;
});
}
return dialog;
},
/**
* @example
* var toolbar = new wysihtml.Toolbar();
* // Insert a <blockquote> element or wrap current selection in <blockquote>
* toolbar.execCommand("formatBlock", "blockquote");
*/
execCommand: function(command, commandValue) {
if (this.commandsDisabled) {
return;
}
this._execCommand(command, commandValue);
},
_execCommand: function(command, commandValue) {
// Make sure that composer is focussed (false => don't move caret to the end)
this.editor.focus(false);
this.composer.commands.exec(command, commandValue);
this._updateLinkStates();
},
execAction: function(action) {
var editor = this.editor;
if (action === "change_view") {
if (editor.currentView === editor.textarea || editor.currentView === "source") {
editor.fire("change_view", "composer");
} else {
editor.fire("change_view", "textarea");
}
}
if (action == "showSource") {
editor.fire("showSource");
}
},
_observe: function() {
var that = this,
editor = this.editor,
container = this.container,
links = this.commandLinks.concat(this.actionLinks),
length = links.length,
i = 0;
for (; i<length; i++) {
// 'javascript:;' and unselectable=on Needed for IE, but done in all browsers to make sure that all get the same css applied
// (you know, a:link { ... } doesn't match anchors with missing href attribute)
if (links[i].nodeName === "A") {
dom.setAttributes({
href: "javascript:;",
unselectable: "on"
}).on(links[i]);
} else {
dom.setAttributes({ unselectable: "on" }).on(links[i]);
}
}
// Needed for opera and chrome
dom.delegate(container, "[data-wysihtml-command], [data-wysihtml-action]", "mousedown", function(event) { event.preventDefault(); });
dom.delegate(container, "[data-wysihtml-command]", "click", function(event) {
var state,
link = this,
command = link.getAttribute("data-wysihtml-command"),
commandValue = link.getAttribute("data-wysihtml-command-value"),
commandObj = that.commandMapping[command + ":" + commandValue];
if (commandValue || !commandObj.dialog) {
that.execCommand(command, commandValue);
} else {
state = getCommandState(that.composer, commandObj);
commandObj.dialog.show(state);
}
event.preventDefault();
});
dom.delegate(container, "[data-wysihtml-action]", "click", function(event) {
var action = this.getAttribute("data-wysihtml-action");
that.execAction(action);
event.preventDefault();
});
editor.on("interaction:composer", function(event) {
if (!that.preventFocus) {
that._updateLinkStates();
}
});
this._ownerDocumentClick = function(event) {
if (!wysihtml.dom.contains(that.container, event.target) && !wysihtml.dom.contains(that.composer.element, event.target)) {
that._updateLinkStates();
that._preventInstantFocus();
}
};
this.container.ownerDocument.addEventListener("click", this._ownerDocumentClick, false);
this.editor.on("destroy:composer", this.destroy.bind(this));
if (this.editor.config.handleTables) {
editor.on("tableselect:composer", function() {
that.container.querySelectorAll('[data-wysihtml-hiddentools="table"]')[0].style.display = "";
});
editor.on("tableunselect:composer", function() {
that.container.querySelectorAll('[data-wysihtml-hiddentools="table"]')[0].style.display = "none";
});
}
editor.on("change_view", function(currentView) {
// Set timeout needed in order to let the blur event fire first
setTimeout(function() {
that.commandsDisabled = (currentView !== "composer");
that._updateLinkStates();
if (that.commandsDisabled) {
dom.addClass(container, CLASS_NAME_COMMANDS_DISABLED);
} else {
dom.removeClass(container, CLASS_NAME_COMMANDS_DISABLED);
}
}, 0);
});
},
destroy: function() {
this.container.ownerDocument.removeEventListener("click", this._ownerDocumentClick, false);
},
_hideAllDialogs: function() {
var commandMapping = this.commandMapping;
for (var i in commandMapping) {
if (commandMapping[i].dialog) {
commandMapping[i].dialog.hide();
}
}
},
_preventInstantFocus: function() {
this.preventFocus = true;
setTimeout(function() {
this.preventFocus = false;
}.bind(this),0);
},
_updateLinkStates: function() {
var i, state, action, command, displayDialogAttributeValue,
commandMapping = this.commandMapping,
composer = this.composer,
actionMapping = this.actionMapping;
// every millisecond counts... this is executed quite often
for (i in commandMapping) {
command = commandMapping[i];
if (this.commandsDisabled) {
state = false;
dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
if (command.group) {
dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
}
if (command.dialog) {
command.dialog.hide();
}
} else {
state = this.composer.commands.state(command.name, command.value);
dom.removeClass(command.link, CLASS_NAME_COMMAND_DISABLED);
if (command.group) {
dom.removeClass(command.group, CLASS_NAME_COMMAND_DISABLED);
}
}
if (command.state === state && !command.tracksBlankValue) {
continue;
}
command.state = state;
if (state) {
if (command.tracksBlankValue) {
dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
} else {
dom.addClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
if (command.group) {
dom.addClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
}
// commands with fixed value can not have a dialog.
if (command.dialog && (typeof command.value === "undefined" || command.value === null)) {
if (state && typeof state === "object") {
state = getCommandState(composer, command);
command.state = state;
// If dialog has dataset.showdialogonselection set as true,
// Dialog displays on text state becoming active regardless of clobal showToolbarDialogsOnSelection options value
displayDialogAttributeValue = command.dialog.container.dataset ? command.dialog.container.dataset.showdialogonselection : false;
if (composer.config.showToolbarDialogsOnSelection || displayDialogAttributeValue) {
command.dialog.show(state);
} else {
command.dialog.update(state);
}
} else {
command.dialog.hide();
}
}
}
} else {
if (command.tracksBlankValue) {
dom.addClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
} else {
dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
if (command.group) {
dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
}
// commands with fixed value can not have a dialog.
if (command.dialog && !command.value) {
command.dialog.hide();
}
}
}
}
for (i in actionMapping) {
action = actionMapping[i];
if (action.name === "change_view") {
action.state = this.editor.currentView === this.editor.textarea || this.editor.currentView === "source";
if (action.state) {
dom.addClass(action.link, CLASS_NAME_ACTION_ACTIVE);
} else {
dom.removeClass(action.link, CLASS_NAME_ACTION_ACTIVE);
}
}
}
},
show: function() {
this.container.style.display = "";
},
hide: function() {
this.container.style.display = "none";
}
});
function getCommandState (composer, command) {
var state = composer.commands.state(command.name, command.value);
// Grab first and only object/element in state array, otherwise convert state into boolean
// to avoid showing a dialog for multiple selected elements which may have different attributes
// eg. when two links with different href are selected, the state will be an array consisting of both link elements
// but the dialog interface can only update one
if (!command.dialog.multiselect && wysihtml.lang.object(state).isArray()) {
state = state.length === 1 ? state[0] : true;
}
return state;
}
// Extend defaults
// Id of the toolbar element, pass falsey value if you don't want any toolbar logic
wysihtml.Editor.prototype.defaults.toolbar = undefined;
// Whether toolbar is displayed after init by script automatically.
// Can be set to false if toolobar is set to display only on editable area focus
wysihtml.Editor.prototype.defaults.showToolbarAfterInit = true;
// With default toolbar it shows dialogs in toolbar when their related text format state becomes active (click on link in text opens link dialogue)
wysihtml.Editor.prototype.defaults.showToolbarDialogsOnSelection= true;
// Bind toolbar initiation on editor instance creation
wysihtml.extendEditor(function(editor) {
if (editor.config.toolbar) {
editor.toolbar = new wysihtml.toolbar.Toolbar(editor, editor.config.toolbar, editor.config.showToolbarAfterInit);
editor.on('destroy:composer', function() {
if (editor && editor.toolbar) {
editor.toolbar.destroy();
}
});
}
});
})(wysihtml);