1 /* 2 * Copyright (C) 2010 Nikita Vasilyev. All rights reserved. 3 * Copyright (C) 2010 Joseph Pecoraro. All rights reserved. 4 * Copyright (C) 2010 Google Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following disclaimer 14 * in the documentation and/or other materials provided with the 15 * distribution. 16 * * Neither the name of Google Inc. nor the names of its 17 * contributors may be used to endorse or promote products derived from 18 * this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 WebInspector.CSSCompletions = function(values, acceptEmptyPrefix) 34 { 35 this._values = values.slice(); 36 this._values.sort(); 37 this._acceptEmptyPrefix = acceptEmptyPrefix; 38 } 39 40 WebInspector.CSSCompletions.prototype = { 41 startsWith: function(prefix) 42 { 43 var firstIndex = this._firstIndexOfPrefix(prefix); 44 if (firstIndex === -1) 45 return []; 46 47 var results = []; 48 while (firstIndex < this._values.length && this._values[firstIndex].indexOf(prefix) === 0) 49 results.push(this._values[firstIndex++]); 50 return results; 51 }, 52 53 firstStartsWith: function(prefix) 54 { 55 var foundIndex = this._firstIndexOfPrefix(prefix); 56 return (foundIndex === -1 ? "" : this._values[foundIndex]); 57 }, 58 59 _firstIndexOfPrefix: function(prefix) 60 { 61 if (!this._values.length) 62 return -1; 63 if (!prefix) 64 return this._acceptEmptyPrefix ? 0 : -1; 65 66 var maxIndex = this._values.length - 1; 67 var minIndex = 0; 68 var foundIndex; 69 70 do { 71 var middleIndex = (maxIndex + minIndex) >> 1; 72 if (this._values[middleIndex].indexOf(prefix) === 0) { 73 foundIndex = middleIndex; 74 break; 75 } 76 if (this._values[middleIndex] < prefix) 77 minIndex = middleIndex + 1; 78 else 79 maxIndex = middleIndex - 1; 80 } while (minIndex <= maxIndex); 81 82 if (foundIndex === undefined) 83 return -1; 84 85 while (foundIndex && this._values[foundIndex - 1].indexOf(prefix) === 0) 86 foundIndex--; 87 88 return foundIndex; 89 }, 90 91 keySet: function() 92 { 93 return this._values.keySet(); 94 }, 95 96 next: function(str, prefix) 97 { 98 return this._closest(str, prefix, 1); 99 }, 100 101 previous: function(str, prefix) 102 { 103 return this._closest(str, prefix, -1); 104 }, 105 106 _closest: function(str, prefix, shift) 107 { 108 if (!str) 109 return ""; 110 111 var index = this._values.indexOf(str); 112 if (index === -1) 113 return ""; 114 115 if (!prefix) { 116 index = (index + this._values.length + shift) % this._values.length; 117 return this._values[index]; 118 } 119 120 var propertiesWithPrefix = this.startsWith(prefix); 121 var j = propertiesWithPrefix.indexOf(str); 122 j = (j + propertiesWithPrefix.length + shift) % propertiesWithPrefix.length; 123 return propertiesWithPrefix[j]; 124 } 125 } 126