/browser/devtools/sourceeditor/orion/orion.js
JavaScript | 12303 lines | 9235 code | 215 blank | 2853 comment | 2250 complexity | e11ef20919979de346cbfa2ab72118a6 MD5 | raw file
Possible License(s): GPL-2.0, JSON, 0BSD, LGPL-3.0, AGPL-1.0, MIT, MPL-2.0-no-copyleft-exception, BSD-3-Clause, LGPL-2.1, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- /*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- * Mihai Sucan (Mozilla Foundation) - fix for Bug#364214
- */
- /*global window */
- /**
- * Evaluates the definition function and mixes in the returned module with
- * the module specified by <code>moduleName</code>.
- * <p>
- * This function is intented to by used when RequireJS is not available.
- * </p>
- *
- * @param {String} name The mixin module name.
- * @param {String[]} deps The array of dependency names.
- * @param {Function} callback The definition function.
- */
- if (!window.define) {
- window.define = function(name, deps, callback) {
- var module = this;
- var split = (name || "").split("/"), i, j;
- for (i = 0; i < split.length - 1; i++) {
- module = module[split[i]] = (module[split[i]] || {});
- }
- var depModules = [], depModule;
- for (j = 0; j < deps.length; j++) {
- depModule = this;
- split = deps[j].split("/");
- for (i = 0; i < split.length - 1; i++) {
- depModule = depModule[split[i]] = (depModule[split[i]] || {});
- }
- depModules.push(depModule);
- }
- var newModule = callback.apply(this, depModules);
- for (var p in newModule) {
- if (newModule.hasOwnProperty(p)) {
- module[p] = newModule[p];
- }
- }
- };
- }
- /**
- * Require/get the defined modules.
- * <p>
- * This function is intented to by used when RequireJS is not available.
- * </p>
- *
- * @param {String[]|String} deps The array of dependency names. This can also be
- * a string, a single dependency name.
- * @param {Function} [callback] Optional, the callback function to execute when
- * multiple dependencies are required. The callback arguments will have
- * references to each module in the same order as the deps array.
- * @returns {Object|undefined} If the deps parameter is a string, then this
- * function returns the required module definition, otherwise undefined is
- * returned.
- */
- if (!window.require) {
- window.require = function(deps, callback) {
- var depsArr = typeof deps === "string" ? [deps] : deps;
- var depModules = [], depModule, split, i, j;
- for (j = 0; j < depsArr.length; j++) {
- depModule = this;
- split = depsArr[j].split("/");
- for (i = 0; i < split.length - 1; i++) {
- depModule = depModule[split[i]] = (depModule[split[i]] || {});
- }
- depModules.push(depModule);
- }
- if (callback) {
- callback.apply(this, depModules);
- }
- return typeof deps === "string" ? depModules[0] : undefined;
- };
- }/*******************************************************************************
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- ******************************************************************************/
-
- /*global define */
- define("orion/textview/eventTarget", [], function() {
- /**
- * Constructs a new EventTarget object.
- *
- * @class
- * @name orion.textview.EventTarget
- */
- function EventTarget() {
- }
- /**
- * Adds in the event target interface into the specified object.
- *
- * @param {Object} object The object to add in the event target interface.
- */
- EventTarget.addMixin = function(object) {
- var proto = EventTarget.prototype;
- for (var p in proto) {
- if (proto.hasOwnProperty(p)) {
- object[p] = proto[p];
- }
- }
- };
- EventTarget.prototype = /** @lends orion.textview.EventTarget.prototype */ {
- /**
- * Adds an event listener to this event target.
- *
- * @param {String} type The event type.
- * @param {Function|EventListener} listener The function or the EventListener that will be executed when the event happens.
- * @param {Boolean} [useCapture=false] <code>true</code> if the listener should be trigged in the capture phase.
- *
- * @see #removeEventListener
- */
- addEventListener: function(type, listener, useCapture) {
- if (!this._eventTypes) { this._eventTypes = {}; }
- var state = this._eventTypes[type];
- if (!state) {
- state = this._eventTypes[type] = {level: 0, listeners: []};
- }
- var listeners = state.listeners;
- listeners.push({listener: listener, useCapture: useCapture});
- },
- /**
- * Dispatches the given event to the listeners added to this event target.
- * @param {Event} evt The event to dispatch.
- */
- dispatchEvent: function(evt) {
- if (!this._eventTypes) { return; }
- var type = evt.type;
- var state = this._eventTypes[type];
- if (state) {
- var listeners = state.listeners;
- try {
- state.level++;
- if (listeners) {
- for (var i=0, len=listeners.length; i < len; i++) {
- if (listeners[i]) {
- var l = listeners[i].listener;
- if (typeof l === "function") {
- l.call(this, evt);
- } else if (l.handleEvent && typeof l.handleEvent === "function") {
- l.handleEvent(evt);
- }
- }
- }
- }
- } finally {
- state.level--;
- if (state.compact && state.level === 0) {
- for (var j=listeners.length - 1; j >= 0; j--) {
- if (!listeners[j]) {
- listeners.splice(j, 1);
- }
- }
- if (listeners.length === 0) {
- delete this._eventTypes[type];
- }
- state.compact = false;
- }
- }
- }
- },
- /**
- * Returns whether there is a listener for the specified event type.
- *
- * @param {String} type The event type
- *
- * @see #addEventListener
- * @see #removeEventListener
- */
- isListening: function(type) {
- if (!this._eventTypes) { return false; }
- return this._eventTypes[type] !== undefined;
- },
- /**
- * Removes an event listener from the event target.
- * <p>
- * All the parameters must be the same ones used to add the listener.
- * </p>
- *
- * @param {String} type The event type
- * @param {Function|EventListener} listener The function or the EventListener that will be executed when the event happens.
- * @param {Boolean} [useCapture=false] <code>true</code> if the listener should be trigged in the capture phase.
- *
- * @see #addEventListener
- */
- removeEventListener: function(type, listener, useCapture){
- if (!this._eventTypes) { return; }
- var state = this._eventTypes[type];
- if (state) {
- var listeners = state.listeners;
- for (var i=0, len=listeners.length; i < len; i++) {
- var l = listeners[i];
- if (l && l.listener === listener && l.useCapture === useCapture) {
- if (state.level !== 0) {
- listeners[i] = null;
- state.compact = true;
- } else {
- listeners.splice(i, 1);
- }
- break;
- }
- }
- if (listeners.length === 0) {
- delete this._eventTypes[type];
- }
- }
- }
- };
- return {EventTarget: EventTarget};
- });
- /*******************************************************************************
- * @license
- * Copyright (c) 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
- /*global define */
- /*jslint browser:true regexp:false*/
- /**
- * @name orion.editor.regex
- * @class Utilities for dealing with regular expressions.
- * @description Utilities for dealing with regular expressions.
- */
- define("orion/editor/regex", [], function() {
- /**
- * @methodOf orion.editor.regex
- * @static
- * @description Escapes regex special characters in the input string.
- * @param {String} str The string to escape.
- * @returns {String} A copy of <code>str</code> with regex special characters escaped.
- */
- function escape(str) {
- return str.replace(/([\\$\^*\/+?\.\(\)|{}\[\]])/g, "\\$&");
- }
- /**
- * @methodOf orion.editor.regex
- * @static
- * @description Parses a pattern and flags out of a regex literal string.
- * @param {String} str The string to parse. Should look something like <code>"/ab+c/"</code> or <code>"/ab+c/i"</code>.
- * @returns {Object} If <code>str</code> looks like a regex literal, returns an object with properties
- * <code><dl>
- * <dt>pattern</dt><dd>{String}</dd>
- * <dt>flags</dt><dd>{String}</dd>
- * </dl></code> otherwise returns <code>null</code>.
- */
- function parse(str) {
- var regexp = /^\s*\/(.+)\/([gim]{0,3})\s*$/.exec(str);
- if (regexp) {
- return {
- pattern : regexp[1],
- flags : regexp[2]
- };
- }
- return null;
- }
- return {
- escape: escape,
- parse: parse
- };
- });
- /*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- ******************************************************************************/
- /*global window define */
- define("orion/textview/keyBinding", [], function() {
- var isMac = window.navigator.platform.indexOf("Mac") !== -1;
- /**
- * Constructs a new key binding with the given key code and modifiers.
- *
- * @param {String|Number} keyCode the key code.
- * @param {Boolean} mod1 the primary modifier (usually Command on Mac and Control on other platforms).
- * @param {Boolean} mod2 the secondary modifier (usually Shift).
- * @param {Boolean} mod3 the third modifier (usually Alt).
- * @param {Boolean} mod4 the fourth modifier (usually Control on the Mac).
- *
- * @class A KeyBinding represents of a key code and a modifier state that can be triggered by the user using the keyboard.
- * @name orion.textview.KeyBinding
- *
- * @property {String|Number} keyCode The key code.
- * @property {Boolean} mod1 The primary modifier (usually Command on Mac and Control on other platforms).
- * @property {Boolean} mod2 The secondary modifier (usually Shift).
- * @property {Boolean} mod3 The third modifier (usually Alt).
- * @property {Boolean} mod4 The fourth modifier (usually Control on the Mac).
- *
- * @see orion.textview.TextView#setKeyBinding
- */
- function KeyBinding (keyCode, mod1, mod2, mod3, mod4) {
- if (typeof(keyCode) === "string") {
- this.keyCode = keyCode.toUpperCase().charCodeAt(0);
- } else {
- this.keyCode = keyCode;
- }
- this.mod1 = mod1 !== undefined && mod1 !== null ? mod1 : false;
- this.mod2 = mod2 !== undefined && mod2 !== null ? mod2 : false;
- this.mod3 = mod3 !== undefined && mod3 !== null ? mod3 : false;
- this.mod4 = mod4 !== undefined && mod4 !== null ? mod4 : false;
- }
- KeyBinding.prototype = /** @lends orion.textview.KeyBinding.prototype */ {
- /**
- * Returns whether this key binding matches the given key event.
- *
- * @param e the key event.
- * @returns {Boolean} <code>true</code> whether the key binding matches the key event.
- */
- match: function (e) {
- if (this.keyCode === e.keyCode) {
- var mod1 = isMac ? e.metaKey : e.ctrlKey;
- if (this.mod1 !== mod1) { return false; }
- if (this.mod2 !== e.shiftKey) { return false; }
- if (this.mod3 !== e.altKey) { return false; }
- if (isMac && this.mod4 !== e.ctrlKey) { return false; }
- return true;
- }
- return false;
- },
- /**
- * Returns whether this key binding is the same as the given parameter.
- *
- * @param {orion.textview.KeyBinding} kb the key binding to compare with.
- * @returns {Boolean} whether or not the parameter and the receiver describe the same key binding.
- */
- equals: function(kb) {
- if (!kb) { return false; }
- if (this.keyCode !== kb.keyCode) { return false; }
- if (this.mod1 !== kb.mod1) { return false; }
- if (this.mod2 !== kb.mod2) { return false; }
- if (this.mod3 !== kb.mod3) { return false; }
- if (this.mod4 !== kb.mod4) { return false; }
- return true;
- }
- };
- return {KeyBinding: KeyBinding};
- });
- /*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors:
- * Felipe Heidrich (IBM Corporation) - initial API and implementation
- * Silenio Quarti (IBM Corporation) - initial API and implementation
- ******************************************************************************/
- /*global define */
- define("orion/textview/annotations", ['orion/textview/eventTarget'], function(mEventTarget) {
- /**
- * @class This object represents a decoration attached to a range of text. Annotations are added to a
- * <code>AnnotationModel</code> which is attached to a <code>TextModel</code>.
- * <p>
- * <b>See:</b><br/>
- * {@link orion.textview.AnnotationModel}<br/>
- * {@link orion.textview.Ruler}<br/>
- * </p>
- * @name orion.textview.Annotation
- *
- * @property {String} type The annotation type (for example, orion.annotation.error).
- * @property {Number} start The start offset of the annotation in the text model.
- * @property {Number} end The end offset of the annotation in the text model.
- * @property {String} html The HTML displayed for the annotation.
- * @property {String} title The text description for the annotation.
- * @property {orion.textview.Style} style The style information for the annotation used in the annotations ruler and tooltips.
- * @property {orion.textview.Style} overviewStyle The style information for the annotation used in the overview ruler.
- * @property {orion.textview.Style} rangeStyle The style information for the annotation used in the text view to decorate a range of text.
- * @property {orion.textview.Style} lineStyle The style information for the annotation used in the text view to decorate a line of text.
- */
- /**
- * Constructs a new folding annotation.
- *
- * @param {orion.textview.ProjectionTextModel} projectionModel The projection text model.
- * @param {String} type The annotation type.
- * @param {Number} start The start offset of the annotation in the text model.
- * @param {Number} end The end offset of the annotation in the text model.
- * @param {String} expandedHTML The HTML displayed for this annotation when it is expanded.
- * @param {orion.textview.Style} expandedStyle The style information for the annotation when it is expanded.
- * @param {String} collapsedHTML The HTML displayed for this annotation when it is collapsed.
- * @param {orion.textview.Style} collapsedStyle The style information for the annotation when it is collapsed.
- *
- * @class This object represents a folding annotation.
- * @name orion.textview.FoldingAnnotation
- */
- function FoldingAnnotation (projectionModel, type, start, end, expandedHTML, expandedStyle, collapsedHTML, collapsedStyle) {
- this.type = type;
- this.start = start;
- this.end = end;
- this._projectionModel = projectionModel;
- this._expandedHTML = this.html = expandedHTML;
- this._expandedStyle = this.style = expandedStyle;
- this._collapsedHTML = collapsedHTML;
- this._collapsedStyle = collapsedStyle;
- this.expanded = true;
- }
-
- FoldingAnnotation.prototype = /** @lends orion.textview.FoldingAnnotation.prototype */ {
- /**
- * Collapses the annotation.
- */
- collapse: function () {
- if (!this.expanded) { return; }
- this.expanded = false;
- this.html = this._collapsedHTML;
- this.style = this._collapsedStyle;
- var projectionModel = this._projectionModel;
- var baseModel = projectionModel.getBaseModel();
- this._projection = {
- start: baseModel.getLineStart(baseModel.getLineAtOffset(this.start) + 1),
- end: baseModel.getLineEnd(baseModel.getLineAtOffset(this.end), true)
- };
- projectionModel.addProjection(this._projection);
- },
- /**
- * Expands the annotation.
- */
- expand: function () {
- if (this.expanded) { return; }
- this.expanded = true;
- this.html = this._expandedHTML;
- this.style = this._expandedStyle;
- this._projectionModel.removeProjection(this._projection);
- }
- };
-
- /**
- * Constructs a new AnnotationTypeList object.
- *
- * @class
- * @name orion.textview.AnnotationTypeList
- */
- function AnnotationTypeList () {
- }
- /**
- * Adds in the annotation type interface into the specified object.
- *
- * @param {Object} object The object to add in the annotation type interface.
- */
- AnnotationTypeList.addMixin = function(object) {
- var proto = AnnotationTypeList.prototype;
- for (var p in proto) {
- if (proto.hasOwnProperty(p)) {
- object[p] = proto[p];
- }
- }
- };
- AnnotationTypeList.prototype = /** @lends orion.textview.AnnotationTypeList.prototype */ {
- /**
- * Adds an annotation type to the receiver.
- * <p>
- * Only annotations of the specified types will be shown by
- * the receiver.
- * </p>
- *
- * @param {Object} type the annotation type to be shown
- *
- * @see #removeAnnotationType
- * @see #isAnnotationTypeVisible
- */
- addAnnotationType: function(type) {
- if (!this._annotationTypes) { this._annotationTypes = []; }
- this._annotationTypes.push(type);
- },
- /**
- * Gets the annotation type priority. The priority is determined by the
- * order the annotation type is added to the receiver. Annotation types
- * added first have higher priority.
- * <p>
- * Returns <code>0</code> if the annotation type is not added.
- * </p>
- *
- * @param {Object} type the annotation type
- *
- * @see #addAnnotationType
- * @see #removeAnnotationType
- * @see #isAnnotationTypeVisible
- */
- getAnnotationTypePriority: function(type) {
- if (this._annotationTypes) {
- for (var i = 0; i < this._annotationTypes.length; i++) {
- if (this._annotationTypes[i] === type) {
- return i + 1;
- }
- }
- }
- return 0;
- },
- /**
- * Returns an array of annotations in the specified annotation model for the given range of text sorted by type.
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model.
- * @param {Number} start the start offset of the range.
- * @param {Number} end the end offset of the range.
- * @return {orion.textview.Annotation[]} an annotation array.
- */
- getAnnotationsByType: function(annotationModel, start, end) {
- var iter = annotationModel.getAnnotations(start, end);
- var annotation, annotations = [];
- while (iter.hasNext()) {
- annotation = iter.next();
- var priority = this.getAnnotationTypePriority(annotation.type);
- if (priority === 0) { continue; }
- annotations.push(annotation);
- }
- var self = this;
- annotations.sort(function(a, b) {
- return self.getAnnotationTypePriority(a.type) - self.getAnnotationTypePriority(b.type);
- });
- return annotations;
- },
- /**
- * Returns whether the receiver shows annotations of the specified type.
- *
- * @param {Object} type the annotation type
- * @returns {Boolean} whether the specified annotation type is shown
- *
- * @see #addAnnotationType
- * @see #removeAnnotationType
- */
- isAnnotationTypeVisible: function(type) {
- return this.getAnnotationTypePriority(type) !== 0;
- },
- /**
- * Removes an annotation type from the receiver.
- *
- * @param {Object} type the annotation type to be removed
- *
- * @see #addAnnotationType
- * @see #isAnnotationTypeVisible
- */
- removeAnnotationType: function(type) {
- if (!this._annotationTypes) { return; }
- for (var i = 0; i < this._annotationTypes.length; i++) {
- if (this._annotationTypes[i] === type) {
- this._annotationTypes.splice(i, 1);
- break;
- }
- }
- }
- };
-
- /**
- * Constructs an annotation model.
- *
- * @param {textModel} textModel The text model.
- *
- * @class This object manages annotations for a <code>TextModel</code>.
- * <p>
- * <b>See:</b><br/>
- * {@link orion.textview.Annotation}<br/>
- * {@link orion.textview.TextModel}<br/>
- * </p>
- * @name orion.textview.AnnotationModel
- * @borrows orion.textview.EventTarget#addEventListener as #addEventListener
- * @borrows orion.textview.EventTarget#removeEventListener as #removeEventListener
- * @borrows orion.textview.EventTarget#dispatchEvent as #dispatchEvent
- */
- function AnnotationModel(textModel) {
- this._annotations = [];
- var self = this;
- this._listener = {
- onChanged: function(modelChangedEvent) {
- self._onChanged(modelChangedEvent);
- }
- };
- this.setTextModel(textModel);
- }
- AnnotationModel.prototype = /** @lends orion.textview.AnnotationModel.prototype */ {
- /**
- * Adds an annotation to the annotation model.
- * <p>The annotation model listeners are notified of this change.</p>
- *
- * @param {orion.textview.Annotation} annotation the annotation to be added.
- *
- * @see #removeAnnotation
- */
- addAnnotation: function(annotation) {
- if (!annotation) { return; }
- var annotations = this._annotations;
- var index = this._binarySearch(annotations, annotation.start);
- annotations.splice(index, 0, annotation);
- var e = {
- type: "Changed",
- added: [annotation],
- removed: [],
- changed: []
- };
- this.onChanged(e);
- },
- /**
- * Returns the text model.
- *
- * @return {orion.textview.TextModel} The text model.
- *
- * @see #setTextModel
- */
- getTextModel: function() {
- return this._model;
- },
- /**
- * @class This object represents an annotation iterator.
- * <p>
- * <b>See:</b><br/>
- * {@link orion.textview.AnnotationModel#getAnnotations}<br/>
- * </p>
- * @name orion.textview.AnnotationIterator
- *
- * @property {Function} hasNext Determines whether there are more annotations in the iterator.
- * @property {Function} next Returns the next annotation in the iterator.
- */
- /**
- * Returns an iterator of annotations for the given range of text.
- *
- * @param {Number} start the start offset of the range.
- * @param {Number} end the end offset of the range.
- * @return {orion.textview.AnnotationIterator} an annotation iterartor.
- */
- getAnnotations: function(start, end) {
- var annotations = this._annotations, current;
- //TODO binary search does not work for range intersection when there are overlaping ranges, need interval search tree for this
- var i = 0;
- var skip = function() {
- while (i < annotations.length) {
- var a = annotations[i++];
- if ((start === a.start) || (start > a.start ? start < a.end : a.start < end)) {
- return a;
- }
- if (a.start >= end) {
- break;
- }
- }
- return null;
- };
- current = skip();
- return {
- next: function() {
- var result = current;
- if (result) { current = skip(); }
- return result;
- },
- hasNext: function() {
- return current !== null;
- }
- };
- },
- /**
- * Notifies the annotation model that the given annotation has been modified.
- * <p>The annotation model listeners are notified of this change.</p>
- *
- * @param {orion.textview.Annotation} annotation the modified annotation.
- *
- * @see #addAnnotation
- */
- modifyAnnotation: function(annotation) {
- if (!annotation) { return; }
- var index = this._getAnnotationIndex(annotation);
- if (index < 0) { return; }
- var e = {
- type: "Changed",
- added: [],
- removed: [],
- changed: [annotation]
- };
- this.onChanged(e);
- },
- /**
- * Notifies all listeners that the annotation model has changed.
- *
- * @param {orion.textview.Annotation[]} added The list of annotation being added to the model.
- * @param {orion.textview.Annotation[]} changed The list of annotation modified in the model.
- * @param {orion.textview.Annotation[]} removed The list of annotation being removed from the model.
- * @param {ModelChangedEvent} textModelChangedEvent the text model changed event that trigger this change, can be null if the change was trigger by a method call (for example, {@link #addAnnotation}).
- */
- onChanged: function(e) {
- return this.dispatchEvent(e);
- },
- /**
- * Removes all annotations of the given <code>type</code>. All annotations
- * are removed if the type is not specified.
- * <p>The annotation model listeners are notified of this change. Only one changed event is generated.</p>
- *
- * @param {Object} type the type of annotations to be removed.
- *
- * @see #removeAnnotation
- */
- removeAnnotations: function(type) {
- var annotations = this._annotations;
- var removed, i;
- if (type) {
- removed = [];
- for (i = annotations.length - 1; i >= 0; i--) {
- var annotation = annotations[i];
- if (annotation.type === type) {
- annotations.splice(i, 1);
- }
- removed.splice(0, 0, annotation);
- }
- } else {
- removed = annotations;
- annotations = [];
- }
- var e = {
- type: "Changed",
- removed: removed,
- added: [],
- changed: []
- };
- this.onChanged(e);
- },
- /**
- * Removes an annotation from the annotation model.
- * <p>The annotation model listeners are notified of this change.</p>
- *
- * @param {orion.textview.Annotation} annotation the annotation to be removed.
- *
- * @see #addAnnotation
- */
- removeAnnotation: function(annotation) {
- if (!annotation) { return; }
- var index = this._getAnnotationIndex(annotation);
- if (index < 0) { return; }
- var e = {
- type: "Changed",
- removed: this._annotations.splice(index, 1),
- added: [],
- changed: []
- };
- this.onChanged(e);
- },
- /**
- * Removes and adds the specifed annotations to the annotation model.
- * <p>The annotation model listeners are notified of this change. Only one changed event is generated.</p>
- *
- * @param {orion.textview.Annotation} remove the annotations to be removed.
- * @param {orion.textview.Annotation} add the annotations to be added.
- *
- * @see #addAnnotation
- * @see #removeAnnotation
- */
- replaceAnnotations: function(remove, add) {
- var annotations = this._annotations, i, index, annotation, removed = [];
- if (remove) {
- for (i = remove.length - 1; i >= 0; i--) {
- annotation = remove[i];
- index = this._getAnnotationIndex(annotation);
- if (index < 0) { continue; }
- annotations.splice(index, 1);
- removed.splice(0, 0, annotation);
- }
- }
- if (!add) { add = []; }
- for (i = 0; i < add.length; i++) {
- annotation = add[i];
- index = this._binarySearch(annotations, annotation.start);
- annotations.splice(index, 0, annotation);
- }
- var e = {
- type: "Changed",
- removed: removed,
- added: add,
- changed: []
- };
- this.onChanged(e);
- },
- /**
- * Sets the text model of the annotation model. The annotation
- * model listens for changes in the text model to update and remove
- * annotations that are affected by the change.
- *
- * @param {orion.textview.TextModel} textModel the text model.
- *
- * @see #getTextModel
- */
- setTextModel: function(textModel) {
- if (this._model) {
- this._model.removeEventListener("Changed", this._listener.onChanged);
- }
- this._model = textModel;
- if (this._model) {
- this._model.addEventListener("Changed", this._listener.onChanged);
- }
- },
- /** @ignore */
- _binarySearch: function (array, offset) {
- var high = array.length, low = -1, index;
- while (high - low > 1) {
- index = Math.floor((high + low) / 2);
- if (offset <= array[index].start) {
- high = index;
- } else {
- low = index;
- }
- }
- return high;
- },
- /** @ignore */
- _getAnnotationIndex: function(annotation) {
- var annotations = this._annotations;
- var index = this._binarySearch(annotations, annotation.start);
- while (index < annotations.length && annotations[index].start === annotation.start) {
- if (annotations[index] === annotation) {
- return index;
- }
- index++;
- }
- return -1;
- },
- /** @ignore */
- _onChanged: function(modelChangedEvent) {
- var start = modelChangedEvent.start;
- var addedCharCount = modelChangedEvent.addedCharCount;
- var removedCharCount = modelChangedEvent.removedCharCount;
- var annotations = this._annotations, end = start + removedCharCount;
- //TODO binary search does not work for range intersection when there are overlaping ranges, need interval search tree for this
- var startIndex = 0;
- if (!(0 <= startIndex && startIndex < annotations.length)) { return; }
- var e = {
- type: "Changed",
- added: [],
- removed: [],
- changed: [],
- textModelChangedEvent: modelChangedEvent
- };
- var changeCount = addedCharCount - removedCharCount, i;
- for (i = startIndex; i < annotations.length; i++) {
- var annotation = annotations[i];
- if (annotation.start >= end) {
- annotation.start += changeCount;
- annotation.end += changeCount;
- e.changed.push(annotation);
- } else if (annotation.end <= start) {
- //nothing
- } else if (annotation.start < start && end < annotation.end) {
- annotation.end += changeCount;
- e.changed.push(annotation);
- } else {
- annotations.splice(i, 1);
- e.removed.push(annotation);
- i--;
- }
- }
- if (e.added.length > 0 || e.removed.length > 0 || e.changed.length > 0) {
- this.onChanged(e);
- }
- }
- };
- mEventTarget.EventTarget.addMixin(AnnotationModel.prototype);
- /**
- * Constructs a new styler for annotations.
- *
- * @param {orion.textview.TextView} view The styler view.
- * @param {orion.textview.AnnotationModel} view The styler annotation model.
- *
- * @class This object represents a styler for annotation attached to a text view.
- * @name orion.textview.AnnotationStyler
- * @borrows orion.textview.AnnotationTypeList#addAnnotationType as #addAnnotationType
- * @borrows orion.textview.AnnotationTypeList#getAnnotationTypePriority as #getAnnotationTypePriority
- * @borrows orion.textview.AnnotationTypeList#getAnnotationsByType as #getAnnotationsByType
- * @borrows orion.textview.AnnotationTypeList#isAnnotationTypeVisible as #isAnnotationTypeVisible
- * @borrows orion.textview.AnnotationTypeList#removeAnnotationType as #removeAnnotationType
- */
- function AnnotationStyler (view, annotationModel) {
- this._view = view;
- this._annotationModel = annotationModel;
- var self = this;
- this._listener = {
- onDestroy: function(e) {
- self._onDestroy(e);
- },
- onLineStyle: function(e) {
- self._onLineStyle(e);
- },
- onChanged: function(e) {
- self._onAnnotationModelChanged(e);
- }
- };
- view.addEventListener("Destroy", this._listener.onDestroy);
- view.addEventListener("LineStyle", this._listener.onLineStyle);
- annotationModel.addEventListener("Changed", this._listener.onChanged);
- }
- AnnotationStyler.prototype = /** @lends orion.textview.AnnotationStyler.prototype */ {
- /**
- * Destroys the styler.
- * <p>
- * Removes all listeners added by this styler.
- * </p>
- */
- destroy: function() {
- var view = this._view;
- if (view) {
- view.removeEventListener("Destroy", this._listener.onDestroy);
- view.removeEventListener("LineStyle", this._listener.onLineStyle);
- this.view = null;
- }
- var annotationModel = this._annotationModel;
- if (annotationModel) {
- annotationModel.removeEventListener("Changed", this._listener.onChanged);
- annotationModel = null;
- }
- },
- _mergeStyle: function(result, style) {
- if (style) {
- if (!result) { result = {}; }
- if (result.styleClass && style.styleClass && result.styleClass !== style.styleClass) {
- result.styleClass += " " + style.styleClass;
- } else {
- result.styleClass = style.styleClass;
- }
- var prop;
- if (style.style) {
- if (!result.style) { result.style = {}; }
- for (prop in style.style) {
- if (!result.style[prop]) {
- result.style[prop] = style.style[prop];
- }
- }
- }
- if (style.attributes) {
- if (!result.attributes) { result.attributes = {}; }
- for (prop in style.attributes) {
- if (!result.attributes[prop]) {
- result.attributes[prop] = style.attributes[prop];
- }
- }
- }
- }
- return result;
- },
- _mergeStyleRanges: function(ranges, styleRange) {
- if (!ranges) { return; }
- for (var i=0; i<ranges.length; i++) {
- var range = ranges[i];
- if (styleRange.end <= range.start) { break; }
- if (styleRange.start >= range.end) { continue; }
- var mergedStyle = this._mergeStyle({}, range.style);
- mergedStyle = this._mergeStyle(mergedStyle, styleRange.style);
- if (styleRange.start <= range.start && styleRange.end >= range.end) {
- ranges[i] = {start: range.start, end: range.end, style: mergedStyle};
- } else if (styleRange.start > range.start && styleRange.end < range.end) {
- ranges.splice(i, 1,
- {start: range.start, end: styleRange.start, style: range.style},
- {start: styleRange.start, end: styleRange.end, style: mergedStyle},
- {start: styleRange.end, end: range.end, style: range.style});
- i += 2;
- } else if (styleRange.start > range.start) {
- ranges.splice(i, 1,
- {start: range.start, end: styleRange.start, style: range.style},
- {start: styleRange.start, end: range.end, style: mergedStyle});
- i += 1;
- } else if (styleRange.end < range.end) {
- ranges.splice(i, 1,
- {start: range.start, end: styleRange.end, style: mergedStyle},
- {start: styleRange.end, end: range.end, style: range.style});
- i += 1;
- }
- }
- },
- _onAnnotationModelChanged: function(e) {
- if (e.textModelChangedEvent) {
- return;
- }
- var view = this._view;
- if (!view) { return; }
- var self = this;
- var model = view.getModel();
- function redraw(changes) {
- for (var i = 0; i < changes.length; i++) {
- if (!self.isAnnotationTypeVisible(changes[i].type)) { continue; }
- var start = changes[i].start;
- var end = changes[i].end;
- if (model.getBaseModel) {
- start = model.mapOffset(start, true);
- end = model.mapOffset(end, true);
- }
- if (start !== -1 && end !== -1) {
- view.redrawRange(start, end);
- }
- }
- }
- redraw(e.added);
- redraw(e.removed);
- redraw(e.changed);
- },
- _onDestroy: function(e) {
- this.destroy();
- },
- _onLineStyle: function (e) {
- var annotationModel = this._annotationModel;
- var viewModel = this._view.getModel();
- var baseModel = annotationModel.getTextModel();
- var start = e.lineStart;
- var end = e.lineStart + e.lineText.length;
- if (baseModel !== viewModel) {
- start = viewModel.mapOffset(start);
- end = viewModel.mapOffset(end);
- }
- var annotations = annotationModel.getAnnotations(start, end);
- while (annotations.hasNext()) {
- var annotation = annotations.next();
- if (!this.isAnnotationTypeVisible(annotation.type)) { continue; }
- if (annotation.rangeStyle) {
- var annotationStart = annotation.start;
- var annotationEnd = annotation.end;
- if (baseModel !== viewModel) {
- annotationStart = viewModel.mapOffset(annotationStart, true);
- annotationEnd = viewModel.mapOffset(annotationEnd, true);
- }
- this._mergeStyleRanges(e.ranges, {start: annotationStart, end: annotationEnd, style: annotation.rangeStyle});
- }
- if (annotation.lineStyle) {
- e.style = this._mergeStyle({}, e.style);
- e.style = this._mergeStyle(e.style, annotation.lineStyle);
- }
- }
- }
- };
- AnnotationTypeList.addMixin(AnnotationStyler.prototype);
-
- return {
- FoldingAnnotation: FoldingAnnotation,
- AnnotationTypeList: AnnotationTypeList,
- AnnotationModel: AnnotationModel,
- AnnotationStyler: AnnotationStyler
- };
- });
- /*******************************************************************************
- * @license
- * Copyright (c) 2010, 2011 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License v1.0
- * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
- * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
- *
- * Contributors: IBM Corporation - initial API and implementation
- ******************************************************************************/
- /*global define setTimeout clearTimeout setInterval clearInterval Node */
- define("orion/textview/rulers", ['orion/textview/annotations', 'orion/textview/tooltip'], function(mAnnotations, mTooltip) {
- /**
- * Constructs a new ruler.
- * <p>
- * The default implementation does not implement all the methods in the interface
- * and is useful only for objects implementing rulers.
- * <p/>
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model for the ruler.
- * @param {String} [rulerLocation="left"] the location for the ruler.
- * @param {String} [rulerOverview="page"] the overview for the ruler.
- * @param {orion.textview.Style} [rulerStyle] the style for the ruler.
- *
- * @class This interface represents a ruler for the text view.
- * <p>
- * A Ruler is a graphical element that is placed either on the left or on the right side of
- * the view. It can be used to provide the view with per line decoration such as line numbering,
- * bookmarks, breakpoints, folding disclosures, etc.
- * </p><p>
- * There are two types of rulers: page and document. A page ruler only shows the content for the lines that are
- * visible, while a document ruler always shows the whole content.
- * </p>
- * <b>See:</b><br/>
- * {@link orion.textview.LineNumberRuler}<br/>
- * {@link orion.textview.AnnotationRuler}<br/>
- * {@link orion.textview.OverviewRuler}<br/>
- * {@link orion.textview.TextView}<br/>
- * {@link orion.textview.TextView#addRuler}
- * </p>
- * @name orion.textview.Ruler
- * @borrows orion.textview.AnnotationTypeList#addAnnotationType as #addAnnotationType
- * @borrows orion.textview.AnnotationTypeList#getAnnotationTypePriority as #getAnnotationTypePriority
- * @borrows orion.textview.AnnotationTypeList#getAnnotationsByType as #getAnnotationsByType
- * @borrows orion.textview.AnnotationTypeList#isAnnotationTypeVisible as #isAnnotationTypeVisible
- * @borrows orion.textview.AnnotationTypeList#removeAnnotationType as #removeAnnotationType
- */
- function Ruler (annotationModel, rulerLocation, rulerOverview, rulerStyle) {
- this._location = rulerLocation || "left";
- this._overview = rulerOverview || "page";
- this._rulerStyle = rulerStyle;
- this._view = null;
- var self = this;
- this._listener = {
- onTextModelChanged: function(e) {
- self._onTextModelChanged(e);
- },
- onAnnotationModelChanged: function(e) {
- self._onAnnotationModelChanged(e);
- }
- };
- this.setAnnotationModel(annotationModel);
- }
- Ruler.prototype = /** @lends orion.textview.Ruler.prototype */ {
- /**
- * Returns the annotations for a given line range merging multiple
- * annotations when necessary.
- * <p>
- * This method is called by the text view when the ruler is redrawn.
- * </p>
- *
- * @param {Number} startLine the start line index
- * @param {Number} endLine the end line index
- * @return {orion.textview.Annotation[]} the annotations for the line range. The array might be sparse.
- */
- getAnnotations: function(startLine, endLine) {
- var annotationModel = this._annotationModel;
- if (!annotationModel) { return []; }
- var model = this._view.getModel();
- var start = model.getLineStart(startLine);
- var end = model.getLineEnd(endLine - 1);
- var baseModel = model;
- if (model.getBaseModel) {
- baseModel = model.getBaseModel();
- start = model.mapOffset(start);
- end = model.mapOffset(end);
- }
- var result = [];
- var annotations = this.getAnnotationsByType(annotationModel, start, end);
- for (var i = 0; i < annotations.length; i++) {
- var annotation = annotations[i];
- var annotationLineStart = baseModel.getLineAtOffset(annotation.start);
- var annotationLineEnd = baseModel.getLineAtOffset(Math.max(annotation.start, annotation.end - 1));
- for (var lineIndex = annotationLineStart; lineIndex<=annotationLineEnd; lineIndex++) {
- var visualLineIndex = lineIndex;
- if (model !== baseModel) {
- var ls = baseModel.getLineStart(lineIndex);
- ls = model.mapOffset(ls, true);
- if (ls === -1) { continue; }
- visualLineIndex = model.getLineAtOffset(ls);
- }
- if (!(startLine <= visualLineIndex && visualLineIndex < endLine)) { continue; }
- var rulerAnnotation = this._mergeAnnotation(result[visualLineIndex], annotation, lineIndex - annotationLineStart, annotationLineEnd - annotationLineStart + 1);
- if (rulerAnnotation) {
- result[visualLineIndex] = rulerAnnotation;
- }
- }
- }
- if (!this._multiAnnotation && this._multiAnnotationOverlay) {
- for (var k in result) {
- if (result[k]._multiple) {
- result[k].html = result[k].html + this._multiAnnotationOverlay.html;
- }
- }
- }
- return result;
- },
- /**
- * Returns the annotation model.
- *
- * @returns {orion.textview.AnnotationModel} the ruler annotation model.
- *
- * @see #setAnnotationModel
- */
- getAnnotationModel: function() {
- return this._annotationModel;
- },
- /**
- * Returns the ruler location.
- *
- * @returns {String} the ruler location, which is either "left" or "right".
- *
- * @see #getOverview
- */
- getLocation: function() {
- return this._location;
- },
- /**
- * Returns the ruler overview type.
- *
- * @returns {String} the overview type, which is either "page" or "document".
- *
- * @see #getLocation
- */
- getOverview: function() {
- return this._overview;
- },
- /**
- * Returns the style information for the ruler.
- *
- * @returns {orion.textview.Style} the style information.
- */
- getRulerStyle: function() {
- return this._rulerStyle;
- },
- /**
- * Returns the widest annotation which determines the width of the ruler.
- * <p>
- * If the ruler does not have a fixed width it should provide the widest
- * annotation to avoid the ruler from changing size as the view scrolls.
- * </p>
- * <p>
- * This method is called by the text view when the ruler is redrawn.
- * </p>
- *
- * @returns {orion.textview.Annotation} the widest annotation.
- *
- * @see #getAnnotations
- */
- getWidestAnnotation: function() {
- return null;
- },
- /**
- * Sets the annotation model for the ruler.
- *
- * @param {orion.textview.AnnotationModel} annotationModel the annotation model.
- *
- * @see #getAnnotationModel
- */
- setAnnotationModel: function (annotationModel) {
- if (this._annotationModel) {
- this._annotationModel.removEventListener("Changed", this._listener.onAnnotationModelChanged);
- }
- this._annotationModel = annotationModel;
- if (this._annotationModel) {
- this._annotationModel.addEventListener("Changed", this._listener.onAnnotationModelChanged);
- }
- },
- /**
- * Sets the annotation that is displayed when a given line contains multiple
- * annotations. This annotation is used when there are different types of
- * annotations in a given line.
- *
- * @param {orion.textview.Annotation} annotation the annotation for lines with multiple annotations.
- *
- * @see #setMultiAnnotationOverlay
- */
- setMultiAnnotation: function(annotation) {
- this._multiAnnotation = annotation;
- },
- /**
- * Sets the annotation that overlays a line with multiple annotations. This annotation is displayed on
- * top of the computed annotation for a given line when there are multiple annotations of the same type
- * in the line. It is also used when the multiple annotation is not set.
- *
- * @param {orion.textview.Annotation} annotation the annotation overlay for lines with multiple annotations.
- *
- * @see #setMultiAnnotation
- */
- setMultiAnnotationOverlay: function(annotation) {
- this._multiAnnotationOverlay = annotation;
- },
- /**
- * Sets the view for the ruler.
- * <p>
- * This method is called by the text view when the ruler
- * is added to the view.
- * </p>
- *
- * @param {orion.textview.TextView} view the text view.
- */
- setView: function (view) {
- if (this._onTextModelChanged && this._view) {
- this._view.removeEventListener("ModelChanged", this._listener.onTextModelChanged);
- }
- this._view = view;
- if (this._onTextModelChanged && this._view) {
- this._view.addEventListener("ModelChanged", this._listener.onTextModelChanged);
- }
- },
- /**
- * This event is sent when the user clicks a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the click event.
- */
- onClick: function(lineIndex, e) {
- },
- /**
- * This event is sent when the user double clicks a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the double click event.
- */
- onDblClick: function(lineIndex, e) {
- },
- /**
- * This event is sent when the user moves the mouse over a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the mouse move event.
- */
- onMouseMove: function(lineIndex, e) {
- var tooltip = mTooltip.Tooltip.getTooltip(this._view);
- if (!tooltip) { return; }
- if (tooltip.isVisible() && this._tooltipLineIndex === lineIndex) { return; }
- this._tooltipLineIndex = lineIndex;
- var self = this;
- tooltip.setTarget({
- y: e.clientY,
- getTooltipInfo: function() {
- return self._getTooltipInfo(self._tooltipLineIndex, this.y);
- }
- });
- },
- /**
- * This event is sent when the mouse pointer enters a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the mouse over event.
- */
- onMouseOver: function(lineIndex, e) {
- this.onMouseMove(lineIndex, e);
- },
- /**
- * This event is sent when the mouse pointer exits a line annotation.
- *
- * @event
- * @param {Number} lineIndex the line index of the annotation under the pointer.
- * @param {DOMEvent} e the mouse out event.
- */
- onMouseOut: function(lineIndex, e) {
- var tooltip = mTooltip.Tooltip.getTooltip(this._view);
- if (!tooltip) { return; }
- tooltip.setTarget(null);
- },
- /** @ignore */
- _getTooltipInfo: function(lineIndex, y) {
- if (lineIndex === undefined) { return; }
- var view = this._view;
- var model = view.getModel();
- var annotationModel = this._annotationModel;
- var annotations = [];
- if (annotationModel) {
- var start = model.getLineStart(lineIndex);
- var end = model.getLineEnd(lineIndex);
- if (model.getBaseModel) {
- start = model.mapOffset(start);
- end = model.mapOffset(end);
- }
- annotations = this.getAnnotationsByType(annotationModel, start, end);
- }
- var contents = this._getTooltipContents(lineIndex, annotations);
- if (!contents) { return null; }
- var info = {
- contents: contents,
- anchor: this.getLocation()
- };
- var rect = view.getClientArea();
- if (this.getOverview() === "document") {
- rect.y = view.convert({y: y}, "view", "document").y;
- } else {
- rect.y = view.getLocationAtOffset(model.getLineStart(lineIndex)).y;
- }
- view.convert(rect, "document", "page");
- info.x = rect.x;
- info.y = rect.y;
- if (info.anchor === "right") {
- info.x += rect.width;
- }
- info.maxWidth = rect.width;
- info.maxHeight = rect.height - (rect.y - view._parent.getBoundingClientRect().top);
- return info;
- },
- /** @ignore */
- _getTooltipContents: function(lineIndex, annotations) {
- return annotations;
- },
- /** @ignore */
- _onAnnotationModelChanged: function(e) {
- var view = this._view;
- if (!view) { return; }
- var model = view.getModel(), self = this;
- var lineCount = model.getLineCount();
- if (e.textModelChangedEvent) {
- var start = e.textModelChangedEvent.start;
- if (model.getBaseModel) { start = model.mapOffset(start, true); }
- var startLine = model.getLineAtOffset(start);
- view.redrawLines(startLine, lineCount, self);
- return;
- }
- function redraw(changes) {
- for (var i = 0; i < changes.length; i++) {
- if (!self.isAnnotationTypeVisible(changes[i].type)) { continue; }
- var start = changes[i].start;
- var end = changes[i].end;
- if (model.getBaseModel) {
- start = model.mapOffset(start, true);
- end = model.mapOffset(end, true);
- }
- if (start !== -1 && end !== -1) {
- view.redrawLines(model.getLineAtOffset(start), model.getLineAtOffset(Math.max(start, end - 1)) + 1, self);
- }
- }
- }
- redraw(e.added);
- redraw(e.removed);
- redraw(e.changed);
- },
- /** @ignore */
- _mergeAnnotation: function(result, annotation, annotationLineIndex, annotationLineCount) {
- if (!result) { result = {}; }
- if (annotationLineIndex === 0) {
- if (result.html && annotation.html) {
- if (annotation.html !== result.html) {
- if (!result._multiple && this._multiAnnotation) {
- result.html = this._multiAnnotation.html;
- }
- }
- result._multiple = true;
- } else {
- result.html = annotation.html;
- }
- }
- result.style = this.…
Large files files are truncated, but you can click here to view the full file