src/pyams_skin/resources/js/ext/tinymce/dev/plugins/textpattern/plugin.js
changeset 557 bca7a7e058a3
equal deleted inserted replaced
-1:000000000000 557:bca7a7e058a3
       
     1 /**
       
     2  * plugin.js
       
     3  *
       
     4  * Copyright, Moxiecode Systems AB
       
     5  * Released under LGPL License.
       
     6  *
       
     7  * License: http://www.tinymce.com/license
       
     8  * Contributing: http://www.tinymce.com/contributing
       
     9  */
       
    10 
       
    11 /*global tinymce:true */
       
    12 
       
    13 tinymce.PluginManager.add('textpattern', function(editor) {
       
    14 	var isPatternsDirty = true, patterns;
       
    15 
       
    16 	patterns = editor.settings.textpattern_patterns || [
       
    17 		{start: '*', end: '*', format: 'italic'},
       
    18 		{start: '**', end: '**', format: 'bold'},
       
    19 		{start: '#', format: 'h1'},
       
    20 		{start: '##', format: 'h2'},
       
    21 		{start: '###', format: 'h3'},
       
    22 		{start: '####', format: 'h4'},
       
    23 		{start: '#####', format: 'h5'},
       
    24 		{start: '######', format: 'h6'},
       
    25 		{start: '1. ', cmd: 'InsertOrderedList'},
       
    26 		{start: '* ', cmd: 'InsertUnorderedList'},
       
    27 		{start: '- ', cmd: 'InsertUnorderedList'}
       
    28 	];
       
    29 
       
    30 	// Returns a sorted patterns list, ordered descending by start length
       
    31 	function getPatterns() {
       
    32 		if (isPatternsDirty) {
       
    33 			patterns.sort(function(a, b) {
       
    34 				if (a.start.length > b.start.length) {
       
    35 					return -1;
       
    36 				}
       
    37 
       
    38 				if (a.start.length < b.start.length) {
       
    39 					return 1;
       
    40 				}
       
    41 
       
    42 				return 0;
       
    43 			});
       
    44 
       
    45 			isPatternsDirty = false;
       
    46 		}
       
    47 
       
    48 		return patterns;
       
    49 	}
       
    50 
       
    51 	// Finds a matching pattern to the specified text
       
    52 	function findPattern(text) {
       
    53 		var patterns = getPatterns();
       
    54 
       
    55 		for (var i = 0; i < patterns.length; i++) {
       
    56 			if (text.indexOf(patterns[i].start) !== 0) {
       
    57 				continue;
       
    58 			}
       
    59 
       
    60 			if (patterns[i].end && text.lastIndexOf(patterns[i].end) != text.length - patterns[i].end.length) {
       
    61 				continue;
       
    62 			}
       
    63 
       
    64 			return patterns[i];
       
    65 		}
       
    66 	}
       
    67 
       
    68 	// Finds the best matching end pattern
       
    69 	function findEndPattern(text, offset, delta) {
       
    70 		var patterns, pattern, i;
       
    71 
       
    72 		// Find best matching end
       
    73 		patterns = getPatterns();
       
    74 		for (i = 0; i < patterns.length; i++) {
       
    75 			pattern = patterns[i];
       
    76 			if (pattern.end && text.substr(offset - pattern.end.length - delta, pattern.end.length) == pattern.end) {
       
    77 				return pattern;
       
    78 			}
       
    79 		}
       
    80 	}
       
    81 
       
    82 	// Handles inline formats like *abc* and **abc**
       
    83 	function applyInlineFormat(space) {
       
    84 		var selection, dom, rng, container, offset, startOffset, text, patternRng, pattern, delta, format;
       
    85 
       
    86 		function splitContainer() {
       
    87 			// Split text node and remove start/end from text node
       
    88 			container = container.splitText(startOffset);
       
    89 			container.splitText(offset - startOffset - delta);
       
    90 			container.deleteData(0, pattern.start.length);
       
    91 			container.deleteData(container.data.length - pattern.end.length, pattern.end.length);
       
    92 		}
       
    93 
       
    94 		selection = editor.selection;
       
    95 		dom = editor.dom;
       
    96 
       
    97 		if (!selection.isCollapsed()) {
       
    98 			return;
       
    99 		}
       
   100 
       
   101 		rng = selection.getRng(true);
       
   102 		container = rng.startContainer;
       
   103 		offset = rng.startOffset;
       
   104 		text = container.data;
       
   105 		delta = space ? 1 : 0;
       
   106 
       
   107 		if (container.nodeType != 3) {
       
   108 			return;
       
   109 		}
       
   110 
       
   111 		// Find best matching end
       
   112 		pattern = findEndPattern(text, offset, delta);
       
   113 		if (!pattern) {
       
   114 			return;
       
   115 		}
       
   116 
       
   117 		// Find start of matched pattern
       
   118 		// TODO: Might need to improve this if there is nested formats
       
   119 		startOffset = Math.max(0, offset - delta);
       
   120 		startOffset = text.lastIndexOf(pattern.start, startOffset - pattern.end.length - 1);
       
   121 
       
   122 		if (startOffset === -1) {
       
   123 			return;
       
   124 		}
       
   125 
       
   126 		// Setup a range for the matching word
       
   127 		patternRng = dom.createRng();
       
   128 		patternRng.setStart(container, startOffset);
       
   129 		patternRng.setEnd(container, offset - delta);
       
   130 		pattern = findPattern(patternRng.toString());
       
   131 
       
   132 		if (!pattern || !pattern.end) {
       
   133 			return;
       
   134 		}
       
   135 
       
   136 		// If container match doesn't have anything between start/end then do nothing
       
   137 		if (container.data.length <= pattern.start.length + pattern.end.length) {
       
   138 			return;
       
   139 		}
       
   140 
       
   141 		format = editor.formatter.get(pattern.format);
       
   142 		if (format && format[0].inline) {
       
   143 			splitContainer();
       
   144 			editor.formatter.apply(pattern.format, {}, container);
       
   145 			return container;
       
   146 		}
       
   147 	}
       
   148 
       
   149 	// Handles block formats like ##abc or 1. abc
       
   150 	function applyBlockFormat() {
       
   151 		var selection, dom, container, firstTextNode, node, format, textBlockElm, pattern, walker, rng, offset;
       
   152 
       
   153 		selection = editor.selection;
       
   154 		dom = editor.dom;
       
   155 
       
   156 		if (!selection.isCollapsed()) {
       
   157 			return;
       
   158 		}
       
   159 
       
   160 		textBlockElm = dom.getParent(selection.getStart(), 'p');
       
   161 		if (textBlockElm) {
       
   162 			walker = new tinymce.dom.TreeWalker(textBlockElm, textBlockElm);
       
   163 			while ((node = walker.next())) {
       
   164 				if (node.nodeType == 3) {
       
   165 					firstTextNode = node;
       
   166 					break;
       
   167 				}
       
   168 			}
       
   169 
       
   170 			if (firstTextNode) {
       
   171 				pattern = findPattern(firstTextNode.data);
       
   172 				if (!pattern) {
       
   173 					return;
       
   174 				}
       
   175 
       
   176 				rng = selection.getRng(true);
       
   177 				container = rng.startContainer;
       
   178 				offset = rng.startOffset;
       
   179 
       
   180 				if (firstTextNode == container) {
       
   181 					offset = Math.max(0, offset - pattern.start.length);
       
   182 				}
       
   183 
       
   184 				if (tinymce.trim(firstTextNode.data).length == pattern.start.length) {
       
   185 					return;
       
   186 				}
       
   187 
       
   188 				if (pattern.format) {
       
   189 					format = editor.formatter.get(pattern.format);
       
   190 					if (format && format[0].block) {
       
   191 						firstTextNode.deleteData(0, pattern.start.length);
       
   192 						editor.formatter.apply(pattern.format, {}, firstTextNode);
       
   193 
       
   194 						rng.setStart(container, offset);
       
   195 						rng.collapse(true);
       
   196 						selection.setRng(rng);
       
   197 					}
       
   198 				}
       
   199 
       
   200 				if (pattern.cmd) {
       
   201 					editor.undoManager.transact(function() {
       
   202 						firstTextNode.deleteData(0, pattern.start.length);
       
   203 						editor.execCommand(pattern.cmd);
       
   204 					});
       
   205 				}
       
   206 			}
       
   207 		}
       
   208 	}
       
   209 
       
   210 	function handleEnter() {
       
   211 		var rng, wrappedTextNode;
       
   212 
       
   213 		wrappedTextNode = applyInlineFormat();
       
   214 		if (wrappedTextNode) {
       
   215 			rng = editor.dom.createRng();
       
   216 			rng.setStart(wrappedTextNode, wrappedTextNode.data.length);
       
   217 			rng.setEnd(wrappedTextNode, wrappedTextNode.data.length);
       
   218 			editor.selection.setRng(rng);
       
   219 		}
       
   220 
       
   221 		applyBlockFormat();
       
   222 	}
       
   223 
       
   224 	function handleSpace() {
       
   225 		var wrappedTextNode, lastChar, lastCharNode, rng, dom;
       
   226 
       
   227 		wrappedTextNode = applyInlineFormat(true);
       
   228 		if (wrappedTextNode) {
       
   229 			dom = editor.dom;
       
   230 			lastChar = wrappedTextNode.data.slice(-1);
       
   231 
       
   232 			// Move space after the newly formatted node
       
   233 			if (/[\u00a0 ]/.test(lastChar)) {
       
   234 				wrappedTextNode.deleteData(wrappedTextNode.data.length - 1, 1);
       
   235 				lastCharNode = dom.doc.createTextNode(lastChar);
       
   236 
       
   237 				if (wrappedTextNode.nextSibling) {
       
   238 					dom.insertAfter(lastCharNode, wrappedTextNode.nextSibling);
       
   239 				} else {
       
   240 					wrappedTextNode.parentNode.appendChild(lastCharNode);
       
   241 				}
       
   242 
       
   243 				rng = dom.createRng();
       
   244 				rng.setStart(lastCharNode, 1);
       
   245 				rng.setEnd(lastCharNode, 1);
       
   246 				editor.selection.setRng(rng);
       
   247 			}
       
   248 		}
       
   249 	}
       
   250 
       
   251 	editor.on('keydown', function(e) {
       
   252 		if (e.keyCode == 13 && !tinymce.util.VK.modifierPressed(e)) {
       
   253 			handleEnter();
       
   254 		}
       
   255 	}, true);
       
   256 
       
   257 	editor.on('keyup', function(e) {
       
   258 		if (e.keyCode == 32 && !tinymce.util.VK.modifierPressed(e)) {
       
   259 			handleSpace();
       
   260 		}
       
   261 	});
       
   262 
       
   263 	this.getPatterns = getPatterns;
       
   264 	this.setPatterns = function(newPatterns) {
       
   265 		patterns = newPatterns;
       
   266 		isPatternsDirty = true;
       
   267 	};
       
   268 });