home *** CD-ROM | disk | FTP | other *** search
/ Computer Active Guide 2009 July / CAG7.ISO / Internetas / SafariSetup.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_TextEditorModel.js < prev    next >
Encoding:
Text File  |  2010-06-03  |  9.8 KB  |  313 lines

  1. /*
  2.  * Copyright (C) 2009 Google Inc. All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions are
  6.  * met:
  7.  *
  8.  *     * Redistributions of source code must retain the above copyright
  9.  * notice, this list of conditions and the following disclaimer.
  10.  *     * Redistributions in binary form must reproduce the above
  11.  * copyright notice, this list of conditions and the following disclaimer
  12.  * in the documentation and/or other materials provided with the
  13.  * distribution.
  14.  *     * Neither the name of Google Inc. nor the names of its
  15.  * contributors may be used to endorse or promote products derived from
  16.  * this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  */
  30.  
  31. WebInspector.TextRange = function(startLine, startColumn, endLine, endColumn)
  32. {
  33.     this.startLine = startLine;
  34.     this.startColumn = startColumn;
  35.     this.endLine = endLine;
  36.     this.endColumn = endColumn;
  37. }
  38.  
  39. WebInspector.TextRange.prototype = {
  40.     isEmpty: function()
  41.     {
  42.         return this.startLine === this.endLine && this.startColumn === this.endColumn;
  43.     },
  44.  
  45.     get linesCount()
  46.     {
  47.         return this.endLine - this.startLine;
  48.     },
  49.  
  50.     clone: function()
  51.     {
  52.         return new WebInspector.TextRange(this.startLine, this.startColumn, this.endLine, this.endColumn); 
  53.     }
  54. }
  55.  
  56. WebInspector.TextEditorModel = function()
  57. {
  58.     this._lines = [""];
  59.     this._attributes = [];
  60.     this._undoStack = [];
  61.     this._noPunctuationRegex = /[^ !%&()*+,-.:;<=>?\[\]\^{|}~]+/;
  62. }
  63.  
  64. WebInspector.TextEditorModel.prototype = {
  65.     set changeListener(changeListener)
  66.     {
  67.         this._changeListener = changeListener;
  68.     },
  69.  
  70.     get linesCount()
  71.     {
  72.         return this._lines.length;
  73.     },
  74.  
  75.     line: function(lineNumber)
  76.     {
  77.         if (lineNumber >= this._lines.length)
  78.             throw "Out of bounds:" + lineNumber;
  79.         return this._lines[lineNumber];
  80.     },
  81.  
  82.     lineLength: function(lineNumber)
  83.     {
  84.         return this._lines[lineNumber].length;
  85.     },
  86.  
  87.     setText: function(range, text)
  88.     {
  89.         if (!range)
  90.             range = new WebInspector.TextRange(0, 0, this._lines.length - 1, this._lines[this._lines.length - 1].length);
  91.         var command = this._pushUndoableCommand(range, text);
  92.         var newRange = this._innerSetText(range, text);
  93.         command.range = newRange.clone();
  94.  
  95.         if (this._changeListener)
  96.             this._changeListener(range, newRange, command.text, text);
  97.         return newRange;
  98.     },
  99.  
  100.     set replaceTabsWithSpaces(replaceTabsWithSpaces)
  101.     {
  102.         this._replaceTabsWithSpaces = replaceTabsWithSpaces;
  103.     },
  104.  
  105.     _innerSetText: function(range, text)
  106.     {
  107.         this._eraseRange(range);
  108.         if (text === "")
  109.             return new WebInspector.TextRange(range.startLine, range.startColumn, range.startLine, range.startColumn);
  110.  
  111.         var newLines = text.split("\n");
  112.         this._replaceTabsIfNeeded(newLines);
  113.  
  114.         var prefix = this._lines[range.startLine].substring(0, range.startColumn);
  115.         var prefixArguments = this._arguments
  116.         var suffix = this._lines[range.startLine].substring(range.startColumn);
  117.  
  118.         var postCaret = prefix.length;
  119.         // Insert text.
  120.         if (newLines.length === 1) {
  121.             this._setLine(range.startLine, prefix + newLines[0] + suffix);
  122.             postCaret += newLines[0].length;
  123.         } else {
  124.             this._setLine(range.startLine, prefix + newLines[0]);
  125.             for (var i = 1; i < newLines.length; ++i)
  126.                 this._insertLine(range.startLine + i, newLines[i]);
  127.             this._setLine(range.startLine + newLines.length - 1, newLines[newLines.length - 1] + suffix);
  128.             postCaret = newLines[newLines.length - 1].length;
  129.         }
  130.         return new WebInspector.TextRange(range.startLine, range.startColumn,
  131.                                           range.startLine + newLines.length - 1, postCaret);
  132.     },
  133.  
  134.     _replaceTabsIfNeeded: function(lines)
  135.     {
  136.         if (!this._replaceTabsWithSpaces)
  137.             return;
  138.         var spaces = [ "    ", "   ", "  ", " "];
  139.         for (var i = 0; i < lines.length; ++i) {
  140.             var line = lines[i];
  141.             var index = line.indexOf("\t");
  142.             while (index !== -1) {
  143.                 line = line.substring(0, index) + spaces[index % 4] + line.substring(index + 1);
  144.                 index = line.indexOf("\t", index + 1);
  145.             }
  146.             lines[i] = line;
  147.         }
  148.     },
  149.  
  150.     _eraseRange: function(range)
  151.     {
  152.         if (range.isEmpty())
  153.             return;
  154.  
  155.         var prefix = this._lines[range.startLine].substring(0, range.startColumn);
  156.         var suffix = this._lines[range.endLine].substring(range.endColumn);
  157.  
  158.         if (range.endLine > range.startLine)
  159.             this._removeLines(range.startLine + 1, range.endLine - range.startLine);
  160.         this._setLine(range.startLine, prefix + suffix);
  161.     },
  162.  
  163.     _setLine: function(lineNumber, text)
  164.     {
  165.         this._lines[lineNumber] = text;
  166.     },
  167.  
  168.     _removeLines: function(fromLine, count)
  169.     {
  170.         this._lines.splice(fromLine, count);
  171.         this._attributes.splice(fromLine, count);
  172.     },
  173.  
  174.     _insertLine: function(lineNumber, text)
  175.     {
  176.         this._lines.splice(lineNumber, 0, text);
  177.         this._attributes.splice(lineNumber, 0, {});
  178.     },
  179.  
  180.     wordRange: function(lineNumber, column)
  181.     {
  182.         return new WebInspector.TextRange(lineNumber, this.wordStart(lineNumber, column, true), lineNumber, this.wordEnd(lineNumber, column, true));
  183.     },
  184.  
  185.     wordStart: function(lineNumber, column, gapless)
  186.     {
  187.         var line = this._lines[lineNumber];
  188.         var prefix = line.substring(0, column).split("").reverse().join("");
  189.         var prefixMatch = this._noPunctuationRegex.exec(prefix);
  190.         return prefixMatch && (!gapless || prefixMatch.index === 0) ? column - prefixMatch.index - prefixMatch[0].length : column;
  191.     },
  192.  
  193.     wordEnd: function(lineNumber, column, gapless)
  194.     {
  195.         var line = this._lines[lineNumber];
  196.         var suffix = line.substring(column);
  197.         var suffixMatch = this._noPunctuationRegex.exec(suffix);
  198.         return suffixMatch && (!gapless || suffixMatch.index === 0) ? column + suffixMatch.index + suffixMatch[0].length : column;
  199.     },
  200.  
  201.     copyRange: function(range)
  202.     {
  203.         if (!range)
  204.             range = new WebInspector.TextRange(0, 0, this._lines.length - 1, this._lines[this._lines.length - 1].length);
  205.  
  206.         var clip = [];
  207.         if (range.startLine === range.endLine) {
  208.             clip.push(this._lines[range.startLine].substring(range.startColumn, range.endColumn));
  209.             return clip.join("\n");
  210.         }
  211.         clip.push(this._lines[range.startLine].substring(range.startColumn));
  212.         for (var i = range.startLine + 1; i < range.endLine; ++i)
  213.             clip.push(this._lines[i]);
  214.         clip.push(this._lines[range.endLine].substring(0, range.endColumn));
  215.         return clip.join("\n");
  216.     },
  217.  
  218.     setAttribute: function(line, name, value)
  219.     {
  220.         var attrs = this._attributes[line];
  221.         if (!attrs) {
  222.             attrs = {};
  223.             this._attributes[line] = attrs;
  224.         }
  225.         attrs[name] = value;
  226.     },
  227.  
  228.     getAttribute: function(line, name)
  229.     {
  230.         var attrs = this._attributes[line];
  231.         return attrs ? attrs[name] : null;
  232.     },
  233.  
  234.     removeAttribute: function(line, name)
  235.     {
  236.         var attrs = this._attributes[line];
  237.         if (attrs)
  238.             delete attrs[name];
  239.     },
  240.  
  241.     _pushUndoableCommand: function(range, text)
  242.     {
  243.         var command = {
  244.             text: this.copyRange(range),
  245.             startLine: range.startLine,
  246.             startColumn: range.startColumn,
  247.             endLine: range.startLine,
  248.             endColumn: range.startColumn
  249.         };
  250.         if (this._inUndo)
  251.             this._redoStack.push(command);
  252.         else {
  253.             if (!this._inRedo)
  254.                 this._redoStack = [];
  255.             this._undoStack.push(command);
  256.         }
  257.         return command;
  258.     },
  259.  
  260.     undo: function()
  261.     {
  262.         this._markRedoableState();
  263.  
  264.         this._inUndo = true;
  265.         var range = this._doUndo(this._undoStack);
  266.         delete this._inUndo;
  267.  
  268.         return range;
  269.     },
  270.  
  271.     redo: function()
  272.     {
  273.         this.markUndoableState();
  274.  
  275.         this._inRedo = true;
  276.         var range = this._doUndo(this._redoStack);
  277.         delete this._inRedo;
  278.  
  279.         return range;
  280.     },
  281.  
  282.     _doUndo: function(stack)
  283.     {
  284.         var range = null;
  285.         for (var i = stack.length - 1; i >= 0; --i) {
  286.             var command = stack[i];
  287.             stack.length = i;
  288.  
  289.             range = this.setText(command.range, command.text);
  290.             if (i > 0 && stack[i - 1].explicit)
  291.                 return range;
  292.         }
  293.         return range;
  294.     },
  295.  
  296.     markUndoableState: function()
  297.     {
  298.         if (this._undoStack.length)
  299.             this._undoStack[this._undoStack.length - 1].explicit = true;
  300.     },
  301.  
  302.     _markRedoableState: function()
  303.     {
  304.         if (this._redoStack.length)
  305.             this._redoStack[this._redoStack.length - 1].explicit = true;
  306.     },
  307.  
  308.     resetUndoStack: function()
  309.     {
  310.         this._undoStack = [];
  311.     }
  312. }
  313.