1 /*!
  2 * This file is part of Aloha Editor Project http://aloha-editor.org
  3 * Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
  4 * Contributors http://aloha-editor.org/contribution.php 
  5 * Licensed unter the terms of http://www.aloha-editor.org/license.html
  6 *//*
  7 * Aloha Editor is free software: you can redistribute it and/or modify
  8 * it under the terms of the GNU Affero General Public License as published by
  9 * the Free Software Foundation, either version 3 of the License, or
 10 * (at your option) any later version.*
 11 *
 12 * Aloha Editor is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 15 * GNU Affero General Public License for more details.
 16 *
 17 * You should have received a copy of the GNU Affero General Public License
 18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 19 */
 20 
 21 define(
 22 [ 'aloha/core', 'aloha/selection', 'aloha/jquery', 'aloha/console' ],
 23 function( Aloha, Selection, jQuery, console ) {
 24 	"use strict";
 25 	
 26 	var
 27 //		$ = jQuery,
 28 //		Aloha = window.Aloha,
 29 //		console = window.console,
 30 		XMLSerializer = window.XMLSerializer;
 31 
 32 	/**
 33 	 * jQuery between Extension
 34 	 *
 35 	 * insert either html code, a dom object OR a jQuery object inside of an existing text node.
 36 	 * if the chained jQuery object is not a text node, nothing will happen.
 37 	 *
 38 	 * @param content HTML Code, DOM object or jQuery object to be inserted
 39 	 * @param offset character offset from the start where the content should be inserted
 40 	 */
 41 	jQuery.fn.between = function(content, offset) {
 42 		var
 43 			offSize,
 44 			fullText;
 45 
 46 		if (this[0].nodeType !== 3) {
 47 			// we are not in a text node, just insert the element at the corresponding position
 48 			offSize = this.children().size();
 49 			if (offset > offSize) {
 50 				offset = offSize;
 51 			}
 52 			if (offset <= 0) {
 53 				this.prepend(content);
 54 			} else {
 55 				this.children().eq(offset -1).after(content);
 56 			}
 57 		} else {
 58 			// we are in a text node so we have to split it at the correct position
 59 			if (offset <= 0) {
 60 				this.before(content);
 61 			} else if (offset >= this[0].length) {
 62 				this.after(content);
 63 			} else {
 64 				fullText = this[0].data;
 65 				this[0].data = fullText.substring(0, offset);
 66 				this.after(fullText.substring(offset, fullText.length));
 67 				this.after(content);
 68 			}
 69 		}
 70 	};
 71 
 72 	/**
 73 	 * Make the object contenteditable. Care about browser version (name of contenteditable attribute depends on it)
 74 	 */
 75 	jQuery.fn.contentEditable = function( b ) {
 76 		// ie does not understand contenteditable but contentEditable
 77 		// contentEditable is not xhtml compatible.
 78 		var	$el = jQuery(this);
 79 		var	ce = 'contenteditable';
 80 
 81 		// Check
 82 		if (jQuery.browser.msie && parseInt(jQuery.browser.version,10) == 7 ) {
 83 			ce = 'contentEditable';
 84 		}
 85 		
 86 		if (typeof b === 'undefined' ) {
 87 			
 88 			// For chrome use this specific attribute. The old ce will only
 89 			// return 'inherit' for nested elements of a contenteditable.
 90 			// The isContentEditable is a w3c standard compliant property which works in IE7,8,FF36+, Chrome 12+
 91 			if (typeof $el[0] === 'undefined' ) {
 92 				console.warn('The jquery object did not contain any valid elements.'); // die silent
 93 				return undefined;
 94 			}
 95 			if (typeof $el[0].isContentEditable === 'undefined') {
 96 				console.warn('Could not determine whether the is editable or not. I assume it is.');
 97 				return true;
 98 			} else { 
 99 				return $el[0].isContentEditable;
100 			}
101 		} else if (b === '') {
102 			$el.removeAttr(ce);
103 		} else {
104 			if (b && b !== 'false') {
105 				b = 'true';
106 			} else {
107 				b = 'false';
108 			}
109 			$el.attr(ce, b);
110 		}
111 
112 		return $el;
113 	};
114 
115 	/**
116 	 * jQuery Aloha Plugin
117 	 *
118 	 * turn all dom elements to continous text
119 	 * @return	jQuery object for the matched elements
120 	 * @api
121 	 */
122 	jQuery.fn.aloha = function() {
123 		var $this = jQuery( this );
124 
125 		Aloha.bind( 'aloha-ready', function() {
126 			$this.each( function() {
127 				// create a new aloha editable object for each passed object
128 				if ( !Aloha.isEditable( this ) ) {
129 					new Aloha.Editable( jQuery( this ) );
130 				}
131 			});
132 		});
133 
134 		// Chain
135 		return $this;
136 	};
137 
138 	/**
139 	 * jQuery destroy elements as editable
140 	 *
141 	 * destroy all mached elements editable capabilities
142 	 * @return	jQuery object for the matched elements
143 	 * @api
144 	 */
145 	jQuery.fn.mahalo = function() {
146 		return this.each(function() {
147 			if (Aloha.isEditable(this)) {
148 				Aloha.getEditableById(jQuery(this).attr('id')).destroy();
149 			}
150 		});
151 	};
152 
153 	/**
154 	 * jQuery Extension
155 	 * new Event which is triggered whenever a selection (length >= 0) is made in
156 	 * an Aloha Editable element
157 	 */
158 	jQuery.fn.contentEditableSelectionChange = function(callback) {
159 		var that = this;
160 
161 		// update selection when keys are pressed
162 		this.keyup(function(event){
163 			var rangeObject = Selection.getRangeObject();
164 			callback(event);
165 		});
166 
167 		// update selection on doubleclick (especially important for the first automatic selection, when the Editable is not active yet, but is at the same time activated as the selection occurs
168 		this.dblclick(function(event) {
169 			callback(event);
170 		});
171 
172 		// update selection when text is selected
173 		this.mousedown(function(event){
174 			// remember that a selection was started
175 			that.selectionStarted = true;
176 		});
177 		jQuery(document).mouseup(function(event) {
178 			Selection.eventOriginalTarget = that;
179 			if (that.selectionStarted) {
180 				callback(event);
181 			}
182 			Selection.eventOriginalTarget = false;
183 			that.selectionStarted = false;
184 		});
185 
186 		return this;
187 	};
188 
189 	/**
190 	 * Fetch the outerHTML of an Element
191 	 * @version 1.0.0
192 	 * @date February 01, 2011
193 	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
194 	 * @author Benjamin Arthur Lupton {@link http://balupton.com}
195 	 * @copyright 2011 Benjamin Arthur Lupton {@link http://balupton.com}
196 	 * @license MIT License {@link http://creativecommons.org/licenses/MIT/}
197 	 * @return {String} outerHtml
198 	 */
199 	jQuery.fn.outerHtml = jQuery.fn.outerHtml || function(){
200 		var
201 			$el = jQuery(this),
202 			el = $el.get(0);
203 			if (typeof el.outerHTML != 'undefined') {
204 				return el.outerHTML;
205 			} else {
206 				try {
207 					// Gecko-based browsers, Safari, Opera.
208 					return (new XMLSerializer()).serializeToString(el);
209 				 } catch (e) {
210 					try {
211 					  // Internet Explorer.
212 					  return el.xml;
213 					} catch (e) {}
214 				}
215 			}
216 	
217 	};
218 
219 
220 	jQuery.fn.zap = function () {
221 		return this.each(function(){ jQuery(this.childNodes).insertBefore(this); }).remove();
222 	};
223 
224 	jQuery.fn.textNodes = function(excludeBreaks, includeEmptyTextNodes) {
225 			var
226 				ret = [],
227 				doSomething = function(el){
228 					if (
229 						(el.nodeType === 3 && jQuery.trim(el.data) && !includeEmptyTextNodes) ||
230 						(el.nodeType === 3 && includeEmptyTextNodes) ||
231 						(el.nodeName =="BR" && !excludeBreaks)) {
232 						ret.push(el);
233 					} else {
234 						for (var i=0, childLength = el.childNodes.length; i < childLength; ++i) {
235 							doSomething(el.childNodes[i]);
236 						}
237 					}
238 				};
239 			
240 			doSomething(this[0]);
241 
242 			return jQuery(ret);
243 	};
244 
245 	/**
246 	 * extendObjects is like jQuery.extend, but it does not extend arrays
247 	 */
248 	jQuery.extendObjects = jQuery.fn.extendObjects = function() {
249 		var options, name, src, copy, copyIsArray, clone,
250 			target = arguments[0] || {},
251 			i = 1,
252 			length = arguments.length,
253 			deep = false;
254 
255 		// Handle a deep copy situation
256 		if ( typeof target === "boolean" ) {
257 			deep = target;
258 			target = arguments[1] || {};
259 			// skip the boolean and the target
260 			i = 2;
261 		}
262 
263 		// Handle case when target is a string or something (possible in deep copy)
264 		if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
265 			target = {};
266 		}
267 
268 		// extend jQuery itself if only one argument is passed
269 		if ( length === i ) {
270 			target = this;
271 			--i;
272 		}
273 
274 		for ( ; i < length; i++ ) {
275 			// Only deal with non-null/undefined values
276 			if ( (options = arguments[ i ]) != null ) {
277 				// Extend the base object
278 				for ( name in options ) {
279 					src = target[ name ];
280 					copy = options[ name ];
281 
282 					// Prevent never-ending loop
283 					if ( target === copy ) {
284 						continue;
285 					}
286 
287 					// Recurse if we're merging plain objects or arrays
288 					if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
289 						if ( copyIsArray ) {
290 							copyIsArray = false;
291 							clone = src && jQuery.isArray(src) ? src : [];
292 
293 						} else {
294 							clone = src && jQuery.isPlainObject(src) ? src : {};
295 						}
296 
297 						// Never move original objects, clone them
298 						if (jQuery.isArray(copy)) {
299 							// don't extend arrays
300 							target[ name ] = copy;
301 						} else {
302 							target[ name ] = jQuery.extendObjects( deep, clone, copy );
303 						}
304 
305 					// Don't bring in undefined values
306 					} else if ( copy !== undefined ) {
307 						target[ name ] = copy;
308 					}
309 				}
310 			}
311 		}
312 
313 		// Return the modified object
314 		return target;
315 	};
316 
317 	jQuery.isBoolean = function(b) {
318 		return b === true || b === false;
319 	},
320 
321 	jQuery.isNumeric = function(o) {
322 		return ! isNaN (o-0);
323 	}
324 });
325