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.error('The jquery object did not contain any valid elements.');
 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 129 				if ( !Aloha.isEditable( this ) ) {
130 					new Aloha.Editable( jQuery( this ) );
131 				}
132 133 			});
134 		});
135 
136 		// Chain
137 		return $this;
138 	};
139 
140 	/**
141 	 * jQuery destroy elements as editable
142 	 *
143 	 * destroy all mached elements editable capabilities
144 	 * @return	jQuery object for the matched elements
145 	 * @api
146 	 */
147 	jQuery.fn.mahalo = function() {
148 		return this.each(function() {
149 			if (Aloha.isEditable(this)) {
150 				Aloha.getEditableById(jQuery(this).attr('id')).destroy();
151 			}
152 		});
153 	};
154 
155 	/**
156 	 * jQuery Extension
157 	 * new Event which is triggered whenever a selection (length >= 0) is made in
158 	 * an Aloha Editable element
159 	 */
160 	jQuery.fn.contentEditableSelectionChange = function(callback) {
161 		var that = this;
162 
163 		// update selection when keys are pressed
164 		this.keyup(function(event){
165 			var rangeObject = Selection.getRangeObject();
166 			callback(event);
167 		});
168 
169 		// 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
170 		this.dblclick(function(event) {
171 			callback(event);
172 		});
173 
174 		// update selection when text is selected
175 		this.mousedown(function(event){
176 			// remember that a selection was started
177 			that.selectionStarted = true;
178 		});
179 		jQuery(document).mouseup(function(event) {
180 			Selection.eventOriginalTarget = that;
181 			if (that.selectionStarted) {
182 				callback(event);
183 			}
184 			Selection.eventOriginalTarget = false;
185 			that.selectionStarted = false;
186 		});
187 
188 		return this;
189 	};
190 
191 	/**
192 	 * Fetch the outerHTML of an Element
193 	 * @version 1.0.0
194 	 * @date February 01, 2011
195 	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
196 	 * @author Benjamin Arthur Lupton {@link http://balupton.com}
197 	 * @copyright 2011 Benjamin Arthur Lupton {@link http://balupton.com}
198 	 * @license MIT License {@link http://creativecommons.org/licenses/MIT/}
199 	 * @return {String} outerHtml
200 	 */
201 	jQuery.fn.outerHtml = jQuery.fn.outerHtml || function(){
202 		var
203 			$el = jQuery(this),
204 			el = $el.get(0);
205 			if (typeof el.outerHTML != 'undefined') {
206 				return el.outerHTML;
207 			} else {
208 				try {
209 					// Gecko-based browsers, Safari, Opera.
210 					return (new XMLSerializer()).serializeToString(el);
211 				 } catch (e) {
212 					try {
213 					  // Internet Explorer.
214 					  return el.xml;
215 					} catch (e) {}
216 				}
217 			}
218 	
219 	};
220 
221 
222 	jQuery.fn.zap = function () {
223 		return this.each(function(){ jQuery(this.childNodes).insertBefore(this); }).remove();
224 	};
225 
226 	jQuery.fn.textNodes = function(excludeBreaks, includeEmptyTextNodes) {
227 			var
228 				ret = [],
229 				doSomething = function(el){
230 					if (
231 						(el.nodeType === 3 && jQuery.trim(el.data) && !includeEmptyTextNodes) ||
232 						(el.nodeType === 3 && includeEmptyTextNodes) ||
233 						(el.nodeName =="BR" && !excludeBreaks)) {
234 						ret.push(el);
235 					} else {
236 						for (var i=0, childLength = el.childNodes.length; i < childLength; ++i) {
237 							doSomething(el.childNodes[i]);
238 						}
239 					}
240 				};
241 			
242 			doSomething(this[0]);
243 
244 			return jQuery(ret);
245 	};
246 
247 	/**
248 	 * extendObjects is like jQuery.extend, but it does not extend arrays
249 	 */
250 	jQuery.extendObjects = jQuery.fn.extendObjects = function() {
251 		var options, name, src, copy, copyIsArray, clone,
252 			target = arguments[0] || {},
253 			i = 1,
254 			length = arguments.length,
255 			deep = false;
256 
257 		// Handle a deep copy situation
258 		if ( typeof target === "boolean" ) {
259 			deep = target;
260 			target = arguments[1] || {};
261 			// skip the boolean and the target
262 			i = 2;
263 		}
264 
265 		// Handle case when target is a string or something (possible in deep copy)
266 		if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
267 			target = {};
268 		}
269 
270 		// extend jQuery itself if only one argument is passed
271 		if ( length === i ) {
272 			target = this;
273 			--i;
274 		}
275 
276 		for ( ; i < length; i++ ) {
277 			// Only deal with non-null/undefined values
278 			if ( (options = arguments[ i ]) != null ) {
279 				// Extend the base object
280 				for ( name in options ) {
281 					src = target[ name ];
282 					copy = options[ name ];
283 
284 					// Prevent never-ending loop
285 					if ( target === copy ) {
286 						continue;
287 					}
288 
289 					// Recurse if we're merging plain objects or arrays
290 					if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
291 						if ( copyIsArray ) {
292 							copyIsArray = false;
293 							clone = src && jQuery.isArray(src) ? src : [];
294 
295 						} else {
296 							clone = src && jQuery.isPlainObject(src) ? src : {};
297 						}
298 
299 						// Never move original objects, clone them
300 						if (jQuery.isArray(copy)) {
301 							// don't extend arrays
302 							target[ name ] = copy;
303 						} else {
304 							target[ name ] = jQuery.extendObjects( deep, clone, copy );
305 						}
306 
307 					// Don't bring in undefined values
308 					} else if ( copy !== undefined ) {
309 						target[ name ] = copy;
310 					}
311 				}
312 			}
313 		}
314 
315 		// Return the modified object
316 		return target;
317 	};
318 
319 	jQuery.isBoolean = function(b) {
320 		return b === true || b === false;
321 	},
322 
323 	jQuery.isNumeric = function(o) {
324 		return ! isNaN (o-0);
325 	}
326 });
327