diff options
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/CSSCompletions.js')
| -rw-r--r-- | Source/WebInspectorUI/UserInterface/CSSCompletions.js | 272 | 
1 files changed, 272 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/CSSCompletions.js b/Source/WebInspectorUI/UserInterface/CSSCompletions.js new file mode 100644 index 000000000..aef5e50f4 --- /dev/null +++ b/Source/WebInspectorUI/UserInterface/CSSCompletions.js @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2010 Nikita Vasilyev. All rights reserved. + * Copyright (C) 2010 Joseph Pecoraro. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + *     * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + *     * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @constructor + */ +WebInspector.CSSCompletions = function(properties, acceptEmptyPrefix) +{ +    this._values = []; +    this._longhands = {}; +    this._shorthands = {}; + +    for (var i = 0; i < properties.length; ++i) { +        // COMPATIBILITY (iOS 6): This used to be an array of strings, +        // now it contains objects with a 'name' property. Support both here. +        var property = properties[i]; +        if (typeof property === "string") { +            this._values.push(property); +            continue; +        } + +        var propertyName = property.name; +        this._values.push(propertyName); + +        var longhands = property.longhands; +        if (longhands) { +            this._longhands[propertyName] = longhands; + +            for (var j = 0; j < longhands.length; ++j) { +                var longhandName = longhands[j]; + +                var shorthands = this._shorthands[longhandName]; +                if (!shorthands) { +                    shorthands = []; +                    this._shorthands[longhandName] = shorthands; +                } + +                shorthands.push(propertyName); +            } +        } +    } + +    this._values.sort(); + +    this._acceptEmptyPrefix = acceptEmptyPrefix; +} + + +/** + * @type {WebInspector.CSSCompletions} + */ +WebInspector.CSSCompletions.cssNameCompletions = null; + +WebInspector.CSSCompletions.requestCSSNameCompletions = function() +{ +    function propertyNamesCallback(error, names) +    { +        if (error) +            return; + +        WebInspector.CSSCompletions.cssNameCompletions = new WebInspector.CSSCompletions(names, false); + +        var propertyNamesForCodeMirror = {}; +        var valueKeywordsForCodeMirror = {"inherit": true, "initial": true}; +        var colorKeywordsForCodeMirror = {}; + +        function nameForCodeMirror(name) +        { +            // CodeMirror parses the vendor prefix separate from the property or keyword name, +            // so we need to strip vendor prefixes from our names. Also strip function parenthesis. +            return name.replace(/^-[^-]+-/, "").replace(/\(\)$/, ""); +        } + +        function collectPropertyNameForCodeMirror(propertyName) +        { +            // Properties can also be value keywords, like when used in a transition. +            // So we add them to both lists. +            var codeMirrorPropertyName = nameForCodeMirror(propertyName); +            propertyNamesForCodeMirror[codeMirrorPropertyName] = true; +            valueKeywordsForCodeMirror[codeMirrorPropertyName] = true; +        } + +        for (var i = 0; i < names.length; ++i) { +            // COMPATIBILITY (iOS 6): This used to be an array of strings, +            // now it contains objects with a 'name' property. Support both here. +            var property = names[i]; +            if (typeof property === "string") +                collectPropertyNameForCodeMirror(property); +            else +                collectPropertyNameForCodeMirror(property.name); +        } + +        for (var propertyName in WebInspector.CSSKeywordCompletions._propertyKeywordMap) { +            var keywords = WebInspector.CSSKeywordCompletions._propertyKeywordMap[propertyName]; +            for (var i = 0; i < keywords.length; ++i) { +                // Skip numbers, like the ones defined for font-weight. +                if (!isNaN(Number(keywords[i]))) +                    continue; +                valueKeywordsForCodeMirror[nameForCodeMirror(keywords[i])] = true; +            } +        } + +        WebInspector.CSSKeywordCompletions._colors.forEach(function(colorName) { +            colorKeywordsForCodeMirror[nameForCodeMirror(colorName)] = true; +        }); + +        function updateCodeMirrorCSSMode(mimeType) +        { +            var modeSpec = CodeMirror.resolveMode(mimeType); + +            console.assert(modeSpec.propertyKeywords); +            console.assert(modeSpec.valueKeywords); +            console.assert(modeSpec.colorKeywords); + +            modeSpec.propertyKeywords = propertyNamesForCodeMirror; +            modeSpec.valueKeywords = valueKeywordsForCodeMirror; +            modeSpec.colorKeywords = colorKeywordsForCodeMirror; + +            CodeMirror.defineMIME(mimeType, modeSpec); +        } + +        updateCodeMirrorCSSMode("text/css"); +        updateCodeMirrorCSSMode("text/x-scss"); +    } + +    if (window.CSSAgent) +        CSSAgent.getSupportedCSSProperties(propertyNamesCallback); +} + +WebInspector.CSSCompletions.prototype = { +    get values() +    { +        return this._values; +    }, + +    startsWith: function(prefix) +    { +        var firstIndex = this._firstIndexOfPrefix(prefix); +        if (firstIndex === -1) +            return []; + +        var results = []; +        while (firstIndex < this._values.length && this._values[firstIndex].startsWith(prefix)) +            results.push(this._values[firstIndex++]); +        return results; +    }, + +    firstStartsWith: function(prefix) +    { +        var foundIndex = this._firstIndexOfPrefix(prefix); +        return (foundIndex === -1 ? "" : this._values[foundIndex]); +    }, + +    _firstIndexOfPrefix: function(prefix) +    { +        if (!this._values.length) +            return -1; +        if (!prefix) +            return this._acceptEmptyPrefix ? 0 : -1; + +        var maxIndex = this._values.length - 1; +        var minIndex = 0; +        var foundIndex; + +        do { +            var middleIndex = (maxIndex + minIndex) >> 1; +            if (this._values[middleIndex].startsWith(prefix)) { +                foundIndex = middleIndex; +                break; +            } +            if (this._values[middleIndex] < prefix) +                minIndex = middleIndex + 1; +            else +                maxIndex = middleIndex - 1; +        } while (minIndex <= maxIndex); + +        if (foundIndex === undefined) +            return -1; + +        while (foundIndex && this._values[foundIndex - 1].startsWith(prefix)) +            foundIndex--; + +        return foundIndex; +    }, + +    keySet: function() +    { +        if (!this._keySet) +            this._keySet = this._values.keySet(); +        return this._keySet; +    }, + +    next: function(str, prefix) +    { +        return this._closest(str, prefix, 1); +    }, + +    previous: function(str, prefix) +    { +        return this._closest(str, prefix, -1); +    }, + +    _closest: function(str, prefix, shift) +    { +        if (!str) +            return ""; + +        var index = this._values.indexOf(str); +        if (index === -1) +            return ""; + +        if (!prefix) { +            index = (index + this._values.length + shift) % this._values.length; +            return this._values[index]; +        } + +        var propertiesWithPrefix = this.startsWith(prefix); +        var j = propertiesWithPrefix.indexOf(str); +        j = (j + propertiesWithPrefix.length + shift) % propertiesWithPrefix.length; +        return propertiesWithPrefix[j]; +    }, + +    isShorthandPropertyName: function(shorthand) +    { +        return shorthand in this._longhands; +    }, + +    isLonghandPropertyName: function(longhand) +    { +        return longhand in this._shorthands; +    }, + +    longhandsForShorthand: function(shorthand) +    { +        return this._longhands[shorthand] || []; +    }, + +    shorthandsForLonghand: function(longhand) +    { +        return this._shorthands[longhand] || []; +    } +}  | 
