/*!
 * jQuery UI Menu @VERSION
 *
 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Menu#theming
 */
.ui-menu { list-style:none; padding: 2px; margin: 0; display:block; outline: none; }
.ui-menu .ui-menu { margin-top: -3px; position: absolute; }
.ui-menu .ui-menu-item { margin: 0; padding: 0; zoom: 1; width: 100%; }
.ui-menu .ui-menu-divider { margin: 5px -2px 5px -2px; height: 0; font-size: 0; line-height: 0; border-width: 1px 0 0 0; }
.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.5; zoom: 1; font-weight: normal; }
.ui-menu .ui-menu-item a.ui-state-focus,
.ui-menu .ui-menu-item a.ui-state-active { font-weight: normal; margin: -1px; }

.ui-menu .ui-state-disabled { font-weight: normal; margin: .4em 0 .2em; line-height: 1.5; }
.ui-menu .ui-state-disabled a { cursor: default; }

/* icon support */
.ui-menu-icons { position: relative; }
.ui-menu-icons .ui-menu-item a { position: relative; padding-left: 2em; }

/* left-aligned */
.ui-menu .ui-icon { position: absolute; top: .2em; left: .2em; }

/* right-aligned */
.ui-menu .ui-menu-icon { position: static; float: right; }


* You are free to store other data on this object to assist in higher-level caching.

* (This object's caches are all stored in "_" prefixed properties.)

// We keep track of the last query to know when we need to invalidate.

* Map from search-result string to the findSpecialCharacters() result for that string - easy to cache

keep keep keep keep keep replace replace replace replace replace keep keep keep keep

var common = require('../common');
var assert = common.assert;
var http = require('http');
var path = require('path');
var mime = require('mime');
var request = require('request');
var fs = require('fs');
var FormData = require(common.dir.lib + '/form_data');
var IncomingForm = require('formidable').IncomingForm;

var remoteFile = 'http://nodejs.org/images/logo.png';

// wrap non simple values into function
// just to deal with ReadStream "autostart"
// Can't wait for 0.10
var FIELDS = [
  {name: 'my_field', value: 'my_value'},
  {name: 'my_buffer', value: function(){ return new Buffer([1, 2, 3])} },
  {name: 'my_file', value: function(){ return fs.createReadStream(common.dir.fixture + '/unicycle.jpg')} },
  {name: 'remote_file', value: function(){ return request(remoteFile)} }
];

var server = http.createServer(function(req, res) {

  // formidable is fixed on github
  // but still 7 month old in npm
  //
  // var form = new IncomingForm();
  // form.uploadDir = common.dir.tmp;
  //  form.parse(req);
  // form
  //   .on('field', function(name, value) {
  //     var field = FIELDS.shift();
  //     assert.strictEqual(name, field.name);
  //     assert.strictEqual(value, field.value+'');
  //   })
  //   .on('file', function(name, file) {
  //     var field = FIELDS.shift();
  //     assert.strictEqual(name, field.name);
  //     assert.strictEqual(file.name, path.basename(field.value.path));
  //     // mime.lookup file.NAME == 'my_file' ?
  //     assert.strictEqual(file.type, mime.lookup(file.name));
  //   })
  //   .on('end', function() {
  //     res.writeHead(200);
  //     res.end('done');
  //   });

  // temp workaround
  var data = '';
  req.setEncoding('utf8');
  req.on('data', function(d) {
    data += d;
  });
  req.on('end', function() {
    // check for the fields' traces

    // 1st field : my_field
    var field = FIELDS.shift();
    assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
    assert.ok( data.indexOf(field.value) != -1 );

    // 2nd field : my_buffer
    var field = FIELDS.shift();
    assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
    assert.ok( data.indexOf(field.value) != -1 );

    // 3rd field : my_file
    var field = FIELDS.shift();
    assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
    assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
    // check for unicycle.jpg traces
    assert.ok( data.indexOf('2005:06:21 01:44:12') != -1 );
    assert.ok( data.indexOf('Content-Type: '+mime.lookup(field.value.path) ) != -1 );

    // 4th field : remote_file
    var field = FIELDS.shift();
    assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
    assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
    // check for http://nodejs.org/images/logo.png traces
    assert.ok( data.indexOf('ImageReady') != -1 );
    assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 );

    res.writeHead(200);
    res.end('done');

  });

});

server.listen(common.port, function() {

  var form = new FormData();

  FIELDS.forEach(function(field) {
    // important to append ReadStreams within the same tick
    if ((typeof field.value == 'function')) {
      field.value = field.value();
    }
    form.append(field.name, field.value);
  });

  // custom params object passed to submit
  form.submit({
    port: common.port,
    path: '/'
  }, function(err, res) {

    if (err) {
      throw err;
    }

    assert.strictEqual(res.statusCode, 200);
    server.close();
  });

});

process.on('exit', function() {
  assert.strictEqual(FIELDS.length, 0);
});


* Clears the caches. Use this in the event that the caches may be invalid.

StringMatcher.prototype.reset = function () {

/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, window, setTimeout */
/*unittests: StringMatch */

define(function (require, exports, module) {
    "use strict";

    var CollectionUtils = require("utils/CollectionUtils");

    /*
     * Performs matching that is useful for QuickOpen and similar searches.
     */

    /** Object representing a search result with associated metadata (added as extra ad hoc fields) */
    function SearchResult(label) {
        this.label = label;
    }

    /*
     * Identifies the "special" characters in the given string.
     * Special characters for matching purposes are:
     *
     * * the first character
     * * "/" and the character following the "/"
     * * "_", "." and "-" and the character following it
     * * an uppercase character that follows a lowercase one (think camelCase)
     *
     * The returned object contains an array called "specials". This array is
     * a list of indexes into the original string where all of the special
     * characters are. It also has a property "lastSegmentSpecialsIndex" which
     * is an index into the specials array that denotes which location is the
     * beginning of the last path segment. (This is used to allow scanning of
     * the last segment's specials separately.)
     *
     * @param {string} input string to break apart (e.g. filename that is being searched)
     * @return {{specials:Array.<number>, lastSegmentSpecialsIndex:number}}
     */
    function findSpecialCharacters(str) {
        var i, c;

        // the beginning of the string is always special
        var specials = [0];

        // lastSegmentSpecialsIndex starts off with the assumption that
        // there are no segments
        var lastSegmentSpecialsIndex = 0;

        // used to track down the camelCase changeovers
        var lastWasLowerCase = false;

        for (i = 0; i < str.length; i++) {
            c = str[i];
            if (c === "/") {
                // new segment means this character and the next are special
                specials.push(i++);
                specials.push(i);
                lastSegmentSpecialsIndex = specials.length - 1;
                lastWasLowerCase = false;
            } else if (c === "." || c === "-" || c === "_") {
                // _, . and - are separators so they are
                // special and so is the next character
                specials.push(i++);
                specials.push(i);
                lastWasLowerCase = false;
            } else if (c.toUpperCase() === c) {
                // this is the check for camelCase changeovers
                if (lastWasLowerCase) {
                    specials.push(i);
                }
                lastWasLowerCase = false;
            } else {
                lastWasLowerCase = true;
            }
        }
        return {
            specials: specials,
            lastSegmentSpecialsIndex: lastSegmentSpecialsIndex
        };
    }

    // states used during the scanning of the string
    var SPECIALS_MATCH = 0;
    var ANY_MATCH = 1;

    // Scores can be hard to make sense of. The DEBUG_SCORES flag
    // provides a way to peek into the parts that made up a score.
    // This flag is used for manual debugging and in the unit tests only.
    var DEBUG_SCORES = false;
    function _setDebugScores(ds) {
        DEBUG_SCORES = ds;
    }

    // Constants for scoring
    var SPECIAL_POINTS = 35;
    var MATCH_POINTS = 10;
    var LAST_SEGMENT_BOOST = 1;
    var BEGINNING_OF_NAME_POINTS = 25;
    var DEDUCTION_FOR_LENGTH = 0.2;
    var CONSECUTIVE_MATCHES_POINTS = 10;
    var NOT_STARTING_ON_SPECIAL_PENALTY = 25;

    // Used in match lists to designate matches of "special" characters (see
    // findSpecialCharacters above
    function SpecialMatch(index) {
        this.index = index;
    }

    // Used in match lists to designate any matched characters that are not special
    function NormalMatch(index) {
        this.index = index;
    }

    /*
     * Finds the best matches between the query and the string. The query is
     * compared with str (usually a lower case string with a lower case
     * query).
     *
     * Generally speaking, this function tries to find "special" characters
     * (see findSpecialCharacters above) first. Failing that, it starts scanning
     * the "normal" characters. Sometimes, it will find a special character that matches
     * but then not be able to match the rest of the query. In cases like that, the
     * search will backtrack and try to find matches for the whole query earlier in the
     * string.
     *
     * A contrived example will help illustrate how the searching and backtracking works. It's a bit long,
     * but it illustrates different pieces of the algorithm which can be tricky. Let's say that we're
     * searching the string "AzzBzzCzdzezzDgxgEF" for "abcdex".
     *
     * To start with, it will match "abcde" from the query to "A B C D E" in the string (the spaces
     * represent gaps in the matched part of the string), because those are all "special characters".
     * However, the "x" in the query doesn't match the "F" which is the only character left in the
     * string.
     *
     * Backtracking kicks in. The "E" is pulled off of the match list.
     * deadBranches[4] is set to the "g" before the "E". This means that for the 5th
     * query character (the "e") we know that we don't have a match beyond that point in the string.
     *
     * To resume searching, the backtrack function looks at the previous match (the "D") and starts
     * searching in character-by-character (ANY_MATCH) mode right after that. It fails to find an
     * "e" before it gets to deadBranches[4], so it has to backtrack again.
     *
     * This time, the "D" is pulled off the match list.
     * deadBranches[3] is set to the "z" before the "D", because we know that for the "dex" part of the
     * query, we can't make it work past the "D". We'll resume searching with the "z" after the "C".
     *
     * Doing an ANY_MATCH search, we find the "d". We then start searching specials for "e", but we
     * stop before we get to "E" because deadBranches[4] tells us that's a dead end. So, we switch
     * to ANY_MATCH and find the "e".
     *
     * Finally, we search for the "x". We don't find a special that matches, so we start an ANY_MATCH
     * search. Then we find the "x", and we have a successful match.
     *
     * Here are some notes on how the algorithm works:
     *
     * * We only backtrack() when we're exhausted both special AND normal forward searches past that point,
     *   for the query remainder we currently have.  For a different query remainder, we may well get further
     *   along - hence deadBranches[] being dependent on queryCounter; but in order to get a different query
     *   remainder, we must give up one or more current matches by backtracking.
     *
     * * Normal "any char" forward search is a superset of special matching mode -- anything that would have
     *   been matched in special mode *could* also be matched by normal mode. In practice, however,
     *   any special characters that could have matched would be picked up first by the specials matching
     *   code.
     *
     * * backtrack() always goes at least as far back as str[deadBranches[queryCounter]-1] before allowing
     *   forward searching to resume
     *
     * * When `deadBranches[queryCounter] = strCounter` it means if we're still trying to match
     *   `queryStr[queryCounter]` and we get to `str[strCounter]`, there's no way we can match the
     *   remainer of `queryStr` with the remainder of `str` -- either using specials-only or
     *   full any-char matching.
     *
     * * We know this because deadBranches[] is set in backtrack(), and we don't get to backtrack() unless
     *   either:
     *   1. We've already exhausted both special AND normal forward searches past that point
     *      (i.e. backtrack() due to `strCounter >= str.length`, yet `queryCounter < query.length`)
     *   2. We stopped searching further forward due to a previously set deadBranches[] value
     *      (i.e. backtrack() due to `strCounter > deadBranches[queryCounter]`, yet
     *      `queryCounter < query.length`)
     *
     * @param {string} query the search string (generally lower cased)
     * @param {string} str the string to compare with (generally lower cased)
     * @param {Array} specials list of special indexes in str (from findSpecialCharacters)
     * @param {int} startingSpecial index into specials array to start scanning with
     * @return {Array.<SpecialMatch|NormalMatch>} matched indexes or null if no matches possible
     */
    function _generateMatchList(query, str, specials, startingSpecial) {
        var result = [];

        // used to keep track of which special character we're testing now
        var specialsCounter = startingSpecial;

        // strCounter and queryCounter are the indexes used for pulling characters
        // off of the str/compareStr and query.
        var strCounter = specials[startingSpecial];
        var queryCounter;

        // the search branches out between special characters and normal characters
        // that are found via consecutive character scanning. In the process of
        // performing these scans, we discover that parts of the query will not match
        // beyond a given point in the string. We keep track of that information
        // in deadBranches, which has a slot for each character in the query.
        // The value stored in the slot is the index into the string after which we
        // are certain there is no match.
        var deadBranches = [];

        for (queryCounter = 0; queryCounter < query.length; queryCounter++) {
            deadBranches[queryCounter] = Infinity;
        }

        queryCounter = 0;

        var state = SPECIALS_MATCH;

        // Compares the current character from the query string against the
        // special characters in str. Returns true if a match was found,
        // false otherwise.
        function findMatchingSpecial() {
            // used to loop through the specials
            var i;

            for (i = specialsCounter; i < specials.length; i++) {
                // short circuit this search when we know there are no matches following
                if (specials[i] >= deadBranches[queryCounter]) {
                    break;
                }

                // First, ensure that we're not comparing specials that
                // come earlier in the string than our current search position.
                // This can happen when the string position changes elsewhere.
                if (specials[i] < strCounter) {
                    specialsCounter = i;
                } else if (query[queryCounter] === str[specials[i]]) {
                    // we have a match! do the required tracking
                    specialsCounter = i;
                    queryCounter++;
                    strCounter = specials[i];
                    result.push(new SpecialMatch(strCounter++));
                    return true;
                }
            }

            return false;
        }

        // This function implements the backtracking that is done when we fail to find
        // a match with the query using the "search for specials first" approach.
        //
        // returns false when it is not able to backtrack successfully
        function backtrack() {

            // The idea is to pull matches off of our match list, rolling back
            // characters from the query. We pay special attention to the special
            // characters since they are searched first.
            while (result.length > 0) {
                var item = result.pop();

                // nothing in the list? there's no possible match then.
                if (!item) {
                    return false;
                }

                // we pulled off a match, which means that we need to put a character
                // back into our query. strCounter is going to be set once we've pulled
                // off the right special character and know where we're going to restart
                // searching from.
                queryCounter--;

                if (item instanceof SpecialMatch) {
                    // pulled off a special, which means we need to make that special available
                    // for matching again
                    specialsCounter--;

                    // check to see if we've gone back as far as we need to
                    if (item.index < deadBranches[queryCounter]) {
                        // we now know that this part of the query does not match beyond this
                        // point
                        deadBranches[queryCounter] = item.index - 1;

                        // since we failed with the specials along this track, we're
                        // going to reset to looking for matches consecutively.
                        state = ANY_MATCH;

                        // we figure out where to start looking based on the new
                        // last item in the list. If there isn't anything else
                        // in the match list, we'll start over at the starting special
                        // (which is generally the beginning of the string, or the
                        // beginning of the last segment of the string)
                        item = result[result.length - 1];
                        if (!item) {
                            strCounter = specials[startingSpecial] + 1;
                            return true;
                        }
                        strCounter = item.index + 1;
                        return true;
                    }
                }
            }
            return false;
        }

        while (true) {

            // keep looping until we've either exhausted the query or the string
            while (queryCounter < query.length && strCounter < str.length && strCounter <= deadBranches[queryCounter]) {
                if (state === SPECIALS_MATCH) {
                    if (!findMatchingSpecial()) {
                        state = ANY_MATCH;
                    }
                }

                if (state === ANY_MATCH) {
                    // we look character by character for matches
                    if (query[queryCounter] === str[strCounter]) {
                        // got a match! record it, and switch back to searching specials
                        queryCounter++;
                        result.push(new NormalMatch(strCounter++));
                        state = SPECIALS_MATCH;
                    } else {
                        // no match, keep looking
                        strCounter++;
                    }
                }
            }

            // if we've finished the query, or we haven't finished the query but we have no
            // more backtracking we can do, then we're all done searching.
            if (queryCounter >= query.length || (queryCounter < query.length && !backtrack())) {
                break;
            }
        }

        // return null when we don't find anything
        if (queryCounter < query.length || result.length === 0) {
            return null;
        }
        return result;
    }

    /*
     * Seek out the best match in the last segment (generally the filename).
     * Matches in the filename are preferred, but the query entered could match
     * any part of the path. So, we find the best match we can get in the filename
     * and then allow for searching the rest of the string with any characters that
     * are left from the beginning of the query.
     *
     * The parameters and return value are the same as for getMatchRanges,
     * except this function is always working on the last segment and the
     * result can optionally include a remainder, which is the characters
     * at the beginning of the query which did not match in the last segment.
     *
     * @param {string} query the search string (generally lower cased)
     * @param {string} str the string to compare with (generally lower cased)
     * @param {Array} specials list of special indexes in str (from findSpecialCharacters)
     * @param {int} startingSpecial index into specials array to start scanning with
     * @param {boolean} lastSegmentStart which character does the last segment start at
     * @return {{remainder:int, matchList:Array.<SpecialMatch|NormalMatch>}} matched indexes or null if no matches possible
     */
    function _lastSegmentSearch(query, str, specials, startingSpecial, lastSegmentStart) {
        var queryCounter, matchList;

        // It's possible that the query is longer than the last segment.
        // If so, we can chop off the bit that we know couldn't possibly be there.
        var remainder = "";
        var extraCharacters = specials[startingSpecial] + query.length - str.length;

        if (extraCharacters > 0) {
            remainder = query.substring(0, extraCharacters);
            query = query.substring(extraCharacters);
        }

        for (queryCounter = 0; queryCounter < query.length; queryCounter++) {
            matchList = _generateMatchList(query.substring(queryCounter),
                                     str, specials, startingSpecial);

            // if we've got a match *or* there are no segments in this string, we're done
            if (matchList || startingSpecial === 0) {
                break;
            }
        }

        if (queryCounter === query.length || !matchList) {
            return null;
        } else {
            return {
                remainder: remainder + query.substring(0, queryCounter),
                matchList: matchList
            };
        }
    }

    /*
     * Implements the top-level search algorithm. Search the last segment first,
     * then search the rest of the string with the remainder.
     *
     * The parameters and return value are the same as for getMatchRanges.
     *
     * @param {string} query the search string (will be searched lower case)
     * @param {string} str the original string to search
     * @param {Array} specials list of special indexes in str (from findSpecialCharacters)
     * @param {int} lastSegmentSpecialsIndex index into specials array to start scanning with
     * @return {Array.<SpecialMatch|NormalMatch>} matched indexes or null if no matches possible
     */
    function _wholeStringSearch(query, str, specials, lastSegmentSpecialsIndex) {
        // set up query as all lower case and make a lower case string to use for comparisons
        query = query.toLowerCase();
        var compareStr = str.toLowerCase();

        var lastSegmentStart = specials[lastSegmentSpecialsIndex];
        var result;
        var matchList;

        result = _lastSegmentSearch(query, compareStr, specials, lastSegmentSpecialsIndex, lastSegmentStart);

        if (result) {
            matchList = result.matchList;

            // Do we have more query characters that did not fit?
            if (result.remainder) {
                // Scan with the remainder only through the beginning of the last segment
                var remainderMatchList = _generateMatchList(result.remainder,
                                              compareStr.substring(0, lastSegmentStart),
                                              specials.slice(0, lastSegmentSpecialsIndex), 0);

                if (remainderMatchList) {
                    // add the new matched ranges to the beginning of the set of ranges we had
                    matchList.unshift.apply(matchList, remainderMatchList);
                } else {
                    // no match
                    return null;
                }
            }
        } else {
            // No match in the last segment, so we start over searching the whole
            // string
            matchList = _generateMatchList(query, compareStr, specials, 0, lastSegmentStart);
        }

        return matchList;
    }

    /**
     * Converts a list of matches into a form suitable for returning from stringMatch.
     *
     * @param {Array.<SpecialMatch|NormalMatch>} matchList to convert
     * @param {string} original string
     * @param {int} character index where last segment begins
     * @return {{ranges:Array.<{text:string, matched:boolean, includesLastSegment:boolean}>, matchGoodness:int, scoreDebug: Object}} matched ranges and score
     */
    function _computeRangesAndScore(matchList, str, lastSegmentStart) {
        var matchCounter;
        var ranges = [];
        var lastMatchIndex = -1;
        var lastSegmentScore = 0;
        var currentRangeStartedOnSpecial = false;

        var score = 0;
        var scoreDebug;
        if (DEBUG_SCORES) {
            scoreDebug = {
                special: 0,
                match: 0,
                lastSegment: 0,
                beginning: 0,
                lengthDeduction: 0,
                consecutive: 0,
                notStartingOnSpecial: 0
            };
        }

        var currentRange = null;

        // Records the current range and adds any additional ranges required to
        // get to character index c. This function is called before starting a new range
        // or returning from the function.
        function closeRangeGap(c) {
            // Close the current range
            if (currentRange) {
                currentRange.includesLastSegment = lastMatchIndex >= lastSegmentStart;
                if (currentRange.matched && currentRange.includesLastSegment) {
                    if (DEBUG_SCORES) {
                        scoreDebug.lastSegment += lastSegmentScore * LAST_SEGMENT_BOOST;
                    }
                    score += lastSegmentScore * LAST_SEGMENT_BOOST;
                }

                if (currentRange.matched && !currentRangeStartedOnSpecial) {
                    if (DEBUG_SCORES) {
                        scoreDebug.notStartingOnSpecial -= NOT_STARTING_ON_SPECIAL_PENALTY;
                    }
                    score -= NOT_STARTING_ON_SPECIAL_PENALTY;
                }
                ranges.push(currentRange);
            }

            // If there was space between the new range and the last,
            // add a new unmatched range before the new range can be added.
            if (lastMatchIndex + 1 < c) {
                ranges.push({
                    text: str.substring(lastMatchIndex + 1, c),
                    matched: false,
                    includesLastSegment: c > lastSegmentStart
                });
            }
            currentRange = null;
            lastSegmentScore = 0;
        }

        // In some cases (see the use of this variable below), we accelerate the
        // bonus the more consecutive matches there are.
        var numConsecutive = 0;

        // Adds a matched character to the appropriate range
        function addMatch(match) {
            // Pull off the character index
            var c = match.index;
            var newPoints = 0;

            // A match means that we need to do some scoring bookkeeping.
            // Start with points added for any match
            if (DEBUG_SCORES) {
                scoreDebug.match += MATCH_POINTS;
            }
            newPoints += MATCH_POINTS;

            // A bonus is given for characters that match at the beginning
            // of the filename
            if (c === lastSegmentStart) {
                if (DEBUG_SCORES) {
                    scoreDebug.beginning += BEGINNING_OF_NAME_POINTS;
                }
                newPoints += BEGINNING_OF_NAME_POINTS;
            }

            // If the new character immediately follows the last matched character,
            // we award the consecutive matches bonus. The check for score > 0
            // handles the initial value of lastMatchIndex which is used for
            // constructing ranges but we don't yet have a true match.
            if (score > 0 && lastMatchIndex + 1 === c) {
                // Consecutive matches that started on a special are a
                // good indicator of intent, so we award an added bonus there.
                if (currentRangeStartedOnSpecial) {
                    numConsecutive++;
                }

                if (DEBUG_SCORES) {
                    scoreDebug.consecutive += CONSECUTIVE_MATCHES_POINTS * numConsecutive;
                }
                newPoints += CONSECUTIVE_MATCHES_POINTS * numConsecutive;
            } else {
                numConsecutive = 1;
            }

            // add points for "special" character matches
            if (match instanceof SpecialMatch) {
                if (DEBUG_SCORES) {
                    scoreDebug.special += SPECIAL_POINTS;
                }
                newPoints += SPECIAL_POINTS;
            }

            score += newPoints;

            // points accumulated in the last segment get an extra bonus
            if (c >= lastSegmentStart) {
                lastSegmentScore += newPoints;
            }

            // if the last range wasn't a match or there's a gap, we need to close off
            // the range to start a new one.
            if ((currentRange && !currentRange.matched) || c > lastMatchIndex + 1) {
                closeRangeGap(c);
            }
            lastMatchIndex = c;

            // set up a new match range or add to the current one
            if (!currentRange) {
                currentRange = {
                    text: str[c],
                    matched: true
                };

                // Check to see if this new matched range is starting on a special
                // character. We penalize those ranges that don't, because most
                // people will search on the logical boundaries of the name
                currentRangeStartedOnSpecial = match instanceof SpecialMatch;
            } else {
                currentRange.text += str[c];
            }
        }

        // scan through the matches, adding each one in turn
        for (matchCounter = 0; matchCounter < matchList.length; matchCounter++) {
            var match = matchList[matchCounter];
            addMatch(match);
        }

        // add a range for the last part of the string
        closeRangeGap(str.length);

        // shorter strings that match are often better than longer ones
        var lengthPenalty = -1 * Math.round(str.length * DEDUCTION_FOR_LENGTH);
        if (DEBUG_SCORES) {
            scoreDebug.lengthDeduction = lengthPenalty;
        }
        score = score + lengthPenalty;

        var result = {
            ranges: ranges,
            matchGoodness: score
        };

        if (DEBUG_SCORES) {
            result.scoreDebug = scoreDebug;
        }
        return result;
    }

    /*
     * Match str against the query using the QuickOpen algorithm provided by
     * the functions above. The general idea is to prefer matches in the last
     * segment (the filename) and matches of "special" characters. stringMatch
     * will try to provide the best match and produces a "matchGoodness" score
     * to allow for relative ranking.
     *
     * The result object returned includes "stringRanges" which can be used to highlight
     * the matched portions of the string, in addition to the "matchGoodness"
     * mentioned above. If DEBUG_SCORES is true, scoreDebug is set on the result
     * to provide insight into the score.
     *
     * The matching is done in a case-insensitive manner.
     *
     * @param {string} str  The string to search
     * @param {string} query  The query string to find in string
     * @param {?Object} (optional) the specials data from findSpecialCharacters, if already known
     * @return {{ranges:Array.<{text:string, matched:boolean, includesLastSegment:boolean}>, matchGoodness:int, scoreDebug: Object}} matched ranges and score
     */
    function stringMatch(str, query, special) {
        var result;

        // No query? Short circuit the normal work done and just
        // return a single range that covers the whole string.
        if (!query) {
            result = new SearchResult(str);
            result.matchGoodness = 0;
            if (DEBUG_SCORES) {
                result.scoreDebug = {};
            }
            result.stringRanges = [{
                text: str,
                matched: false,
                includesLastSegment: true
            }];
            return result;
        }

        // Locate the special characters and then use orderedCompare to do the real
        // work.
        if (!special) {
            special = findSpecialCharacters(str);
        }
        var lastSegmentStart = special.specials[special.lastSegmentSpecialsIndex];
        var matchList = _wholeStringSearch(query, str, special.specials,
                              special.lastSegmentSpecialsIndex);

        // If we get a match, turn this into a SearchResult as expected by the consumers
        // of this API.
        if (matchList) {
            var compareData = _computeRangesAndScore(matchList, str, lastSegmentStart);
            result = new SearchResult(str);
            result.stringRanges = compareData.ranges;
            result.matchGoodness = -1 * compareData.matchGoodness;
            if (DEBUG_SCORES) {
                result.scoreDebug = compareData.scoreDebug;
            }
        }
        return result;
    }

    /**
     * Sorts an array of SearchResult objects on a primary field, followed by secondary fields
     * in case of ties. 'fields' maps field name to priority, where 0 is the primary field. E.g.:
     *      multiFieldSort(bugList, { milestone: 0, severity: 1 });
     * Would sort a bug list by milestone, and within each milestone sort bugs by severity.
     *
     * Any fields that have a string value are compared case-insensitively. Fields used should be
     * present on all SearchResult objects (no optional/undefined fields).
     *
     * @param {!Array.<SearchResult>} searchResults
     * @param {!Object.<string, number>} fields
     */
    function multiFieldSort(searchResults, fields) {
        // Move field names into an array, with primary field first
        var fieldNames = [];
        CollectionUtils.forEach(fields, function (priority, key) {
            fieldNames[priority] = key;
        });

        searchResults.sort(function (a, b) {
            var priority;
            for (priority = 0; priority < fieldNames.length; priority++) {
                var fieldName = fieldNames[priority];
                var valueA = a[fieldName];
                var valueB = b[fieldName];
                if (typeof valueA === "string") {
                    valueA = valueA.toLowerCase();
                    valueB = valueB.toLowerCase();
                }

                if (valueA < valueB) {
                    return -1;
                } else if (valueA > valueB) {
                    return 1;
                }
                // otherwise, move on to next sort priority
            }
            return 0; // all sort fields are equal
        });
    }

    /**
     * Sorts search results generated by stringMatch(): results are sorted into several
     * tiers based on how well they matched the search query, then sorted alphabetically
     * within each tier.
     */
    function basicMatchSort(searchResults) {
        multiFieldSort(searchResults, { matchGoodness: 0, label: 1 });
    }

    /**
     * A StringMatcher provides an interface to the stringMatch function with built-in
     * caching. You should use a StringMatcher for the lifetime of queries over a
     * single data set.
     *
     * You are free to store other data on this object to assist in higher-level caching.
     * (This object's caches are all stored in "_" prefixed properties.)
     */
    function StringMatcher() {
        // We keep track of the last query to know when we need to invalidate.
        this._lastQuery = null;

        this._specialsCache = {};
        this._noMatchCache = {};
    }

    /**
     * Map from search-result string to the findSpecialCharacters() result for that string - easy to cache
     * since this info doesn't change as the query changes.
     * @type {Object.<string, {specials:Array.<number>, lastSegmentSpecialsIndex:number}>}
     */
    StringMatcher.prototype._specialsCache = null;

    /**
     * Set of search-result strings that we know don't match the query _lastQuery - or any other query with
     * that prefix.
     * @type {Object.<string, boolean>}
     */
    StringMatcher.prototype._noMatchCache = null;

    /**
     * Performs a single match using the stringMatch function. See stringMatch for full documentation.
     *
     * @param {string} str  The string to search
     * @param {string} query  The query string to find in string
     * @return {{ranges:Array.<{text:string, matched:boolean, includesLastSegment:boolean}>, matchGoodness:int, scoreDebug: Object}} matched ranges and score
     */
    StringMatcher.prototype.match = function (str, query) {

        // If the query is not just added characters from the previous query, we invalidate
        // the no match cache and will re-match everything.
        if (this._lastQuery !== null && (this._lastQuery !== query.substring(0, this._lastQuery.length))) {
            this._noMatchCache = {};
        }

        this._lastQuery = query;

        // Check for a known non-matching string.
        if (this._noMatchCache.hasOwnProperty(str)) {
            return undefined;
        }

        // Load up the cached specials information (or build it if this is our first time through).
        var special = this._specialsCache.hasOwnProperty(str) ? this._specialsCache[str] : undefined;
        if (special === undefined) {
            special = findSpecialCharacters(str);
            this._specialsCache[str] = special;
        }

        var result = stringMatch(str, query, special);

        // If this query was not a match, we cache that fact for next time.
        if (!result) {
            this._noMatchCache[str] = true;
        }
        return result;
    };

    exports._findSpecialCharacters  = findSpecialCharacters;
    exports._wholeStringSearch      = _wholeStringSearch;
    exports._lastSegmentSearch      = _lastSegmentSearch;
    exports._setDebugScores         = _setDebugScores;
    exports._generateMatchList      = _generateMatchList;
    exports._SpecialMatch           = SpecialMatch;
    exports._NormalMatch            = NormalMatch;
    exports._computeRangesAndScore  = _computeRangesAndScore;

    // public exports
    exports.SearchResult            = SearchResult;
    exports.stringMatch             = stringMatch;
    exports.basicMatchSort          = basicMatchSort;
    exports.multiFieldSort          = multiFieldSort;
    exports.StringMatcher           = StringMatcher;
});


<!DOCTYPE html>

	<meta charset="utf-8">
	<title>jQuery UI Datepicker - Default functionality</title>
	<link rel="stylesheet" href="../../themes/base/jquery.ui.all.css">

	<link rel="stylesheet" href="../demos.css">

Date: <input type="text" id="datepicker">

The datepicker is tied to a standard form input field.  Focus on the input (click, or use the tab key) to open an interactive calendar in a small overlay.  Choose a date, click elsewhere on the page (blur the input), or hit the Esc key to close. If a date is chosen, feedback is shown as the input's value.


<!doctype html>

	<meta charset="utf-8">
	<title>jQuery UI Position - Default functionality</title>
	<link rel="stylesheet" href="../../themes/base/jquery.ui.all.css">

	<link rel="stylesheet" href="../demos.css">

	This is the position parent element.

	to position

	to position 2

	position...

		my:
		<select id="my_horizontal">
			<option value="left">left</option>
			<option value="center">center</option>
			<option value="right">right</option>
		</select>
		<select id="my_vertical">
			<option value="top">top</option>
			<option value="middle">center</option>
			<option value="bottom">bottom</option>
		</select>

		at:
		<select id="at_horizontal">
			<option value="left">left</option>
			<option value="center">center</option>
			<option value="right">right</option>
		</select>
		<select id="at_vertical">
			<option value="top">top</option>
			<option value="middle">center</option>
			<option value="bottom">bottom</option>
		</select>

		offset:
		<input id="offset">

		collision:
		<select id="collision_horizontal">
			<option value="flip">flip</option>
			<option value="fit">fit</option>
			<option value="flipfit">flipfit</option>
			<option value="none">none</option>
		</select>
		<select id="collision_vertical">
			<option value="flip">flip</option>
			<option value="fit">fit</option>
			<option value="flipfit">flipfit</option>
			<option value="none">none</option>
		</select>

Use the form controls to configure the positioning, or drag the positioned element to modify its offset.

Drag around the parent element to see collision detection in action.


var server = require('./server')
  , assert = require('assert')
  , request = require('../index')

var s = server.createServer()
var ss = server.createSSLServer()
var sUrl = 'http://localhost:' + s.port
var ssUrl = 'https://localhost:' + ss.port

s.listen(s.port, bouncy(s, ssUrl))
ss.listen(ss.port, bouncy(ss, sUrl))

var hits = {}
var expect = {}
var pending = 0
function bouncy (s, server) { return function () {

  var redirs = { a: 'b'
               , b: 'c'
               , c: 'd'
               , d: 'e'
               , e: 'f'
               , f: 'g'
               , g: 'h'
               , h: 'end' }

  var perm = true
  Object.keys(redirs).forEach(function (p) {
    var t = redirs[p]

    // switch type each time
    var type = perm ? 301 : 302
    perm = !perm
    s.on('/' + p, function (req, res) {
      res.writeHead(type, { location: server + '/' + t })
      res.end()
    })
  })

  s.on('/end', function (req, res) {
    var h = req.headers['x-test-key']
    hits[h] = true
    pending --
    if (pending === 0) done()
  })
}}

for (var i = 0; i < 5; i ++) {
  pending ++
  var val = 'test_' + i
  expect[val] = true
  request({ url: (i % 2 ? sUrl : ssUrl) + '/a'
          , headers: { 'x-test-key': val }
          , rejectUnauthorized: false })
}

function done () {
  assert.deepEqual(hits, expect)
  process.exit(0)
}


keep keep keep keep keep replace replace replace keep keep

function _0xColorToHex(color, convertToStr) {

var hexColor = tinycolor(color.replace("0x", "#"));

/*
 * Copyright (c) 2012 - present Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint regexp: true */

define(function (require, exports, module) {
    "use strict";

    var KeyEvent           = brackets.getModule("utils/KeyEvent"),
        PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
        StringUtils        = brackets.getModule("utils/StringUtils"),
        Strings            = brackets.getModule("strings"),
        Mustache           = brackets.getModule("thirdparty/mustache/mustache"),
        tinycolor          = require("thirdparty/tinycolor-min");

    /** Mustache template that forms the bare DOM structure of the UI */
    var ColorEditorTemplate = require("text!ColorEditorTemplate.html");

    /**
     * @const @type {number}
     */
    var STEP_MULTIPLIER = 5;

    /**
     * Convert 0x notation into hex6 format for tinycolor
     * compatibility: ("0xFFAACC" => "#FFFFFF")
     */
    function color0xToHex(color,convertToStr){
        var format0xToHexColor = color.replace("0x","#");
        var hexColor = tinycolor(format0xToHexColor);
        hexColor._format = "0x";

        if(convertToStr){
            return hexColor.toString();
        }
        return hexColor;
    }

    function as0xString(color){
        return color.toHexString().replace("#","0x");
    }

    function checkSetFormat(color,convertToStr){
        if ((/^0x/).test(color)) {
            return color0xToHex(color,convertToStr);
        }
        return tinycolor(color);
    }

    /**
     * Color picker control; may be used standalone or within an InlineColorEditor inline widget.
     * @param {!jQuery} $parent  DOM node into which to append the root of the color picker UI
     * @param {!string} color  Initially selected color
     * @param {!function(string)} callback  Called whenever selected color changes
     * @param {!Array.<{value:string, count:number}>} swatches  Quick-access color swatches to include in UI
     */
    function ColorEditor($parent, color, callback, swatches) {
        // Create the DOM structure, filling in localized strings via Mustache
        this.$element = $(Mustache.render(ColorEditorTemplate, Strings));
        $parent.append(this.$element);

        this._callback = callback;

        this._handleKeydown = this._handleKeydown.bind(this);
        this._handleOpacityKeydown = this._handleOpacityKeydown.bind(this);
        this._handleHslKeydown = this._handleHslKeydown.bind(this);
        this._handleHueKeydown = this._handleHueKeydown.bind(this);
        this._handleSelectionKeydown = this._handleSelectionKeydown.bind(this);
        this._handleOpacityDrag = this._handleOpacityDrag.bind(this);
        this._handleHueDrag = this._handleHueDrag.bind(this);
        this._handleSelectionFieldDrag = this._handleSelectionFieldDrag.bind(this);

        this._originalColor = color;
        this._color = checkSetFormat(color);

        this._redoColor = null;
        this._isUpperCase = PreferencesManager.get("uppercaseColors");
        PreferencesManager.on("change", "uppercaseColors", function () {
            this._isUpperCase = PreferencesManager.get("uppercaseColors");
        }.bind(this));

        this.$colorValue = this.$element.find(".color-value");
        this.$buttonList = this.$element.find("ul.button-bar");
        this.$rgbaButton = this.$element.find(".rgba");
        this.$hexButton = this.$element.find(".hex");
        this.$hslButton = this.$element.find(".hsla");
        this.$0xButton = this.$element.find(".0x");
        this.$currentColor = this.$element.find(".current-color");
        this.$originalColor = this.$element.find(".original-color");
        this.$selection = this.$element.find(".color-selection-field");
        this.$selectionBase = this.$element.find(".color-selection-field .selector-base");
        this.$hueBase = this.$element.find(".hue-slider .selector-base");
        this.$opacityGradient = this.$element.find(".opacity-gradient");
        this.$hueSlider = this.$element.find(".hue-slider");
        this.$hueSelector = this.$element.find(".hue-slider .selector-base");
        this.$opacitySlider = this.$element.find(".opacity-slider");
        this.$opacitySelector = this.$element.find(".opacity-slider .selector-base");
        this.$swatches = this.$element.find(".swatches");

        // Create quick-access color swatches
        this._addSwatches(swatches);

        // Attach event listeners to main UI elements
        this._addListeners();

        this._commitColor(color);
        this.$originalColor.css("background-color", checkSetFormat(this._originalColor));
    }

    /**
     * A string or tinycolor object representing the currently selected color
     * TODO (#2201): type is unpredictable
     * @type {tinycolor|string}
     */
    ColorEditor.prototype._color = null;

    /**
     * An HSV representation of the currently selected color.
     * TODO (#2201): type of _hsv.s/.v is unpredictable
     * @type {!{h:number, s:number|string, v:number|string, a:number}}
     */
    ColorEditor.prototype._hsv = tinycolor("rgba(0,0,0,1)").toHsv();

    /**
     * Color that was selected before undo(), if undo was the last change made. Else null.
     * @type {?string}
     */
    ColorEditor.prototype._redoColor = null;

    /**
     * Initial value the color picker was opened with
     * @type {!string}
     */
    ColorEditor.prototype._originalColor = null;

    /** Returns the root DOM node of the ColorPicker UI */
    ColorEditor.prototype.getRootElement = function () {
        return this.$element;
    };

    /** Attach event listeners for main UI elements */
    ColorEditor.prototype._addListeners = function () {
        this._bindColorFormatToRadioButton("rgba");
        this._bindColorFormatToRadioButton("hex");
        this._bindColorFormatToRadioButton("hsla");
        this._bindColorFormatToRadioButton("0x");

        this._bindInputHandlers();

        this._bindOriginalColorButton();

        this._registerDragHandler(this.$selection, this._handleSelectionFieldDrag);
        this._registerDragHandler(this.$hueSlider, this._handleHueDrag);
        this._registerDragHandler(this.$opacitySlider, this._handleOpacityDrag);
        this._bindKeyHandler(this.$selectionBase, this._handleSelectionKeydown);
        this._bindKeyHandler(this.$hueBase, this._handleHueKeydown);
        this._bindKeyHandler(this.$opacitySelector, this._handleOpacityKeydown);
        this._bindKeyHandler(this.$hslButton, this._handleHslKeydown);

        // General key handler gets bubbling events from any focusable part of widget
        this._bindKeyHandler(this.$element, this._handleKeydown);
    };

    /**
     * Update all UI elements to reflect the selected color (_color and _hsv). It is usually
     * incorrect to call this directly; use _commitColor() or setColorAsHsv() instead.
     */

    ColorEditor.prototype._synchronize = function () {
        var colorValue  = this.getColor().getOriginalInput();
        var colorObject = checkSetFormat(colorValue);
        var hueColor    = "hsl(" + this._hsv.h + ", 100%, 50%)";
        this._updateColorTypeRadioButtons(colorObject.getFormat());
        this.$colorValue.val(colorValue);
        this.$currentColor.css("background-color", checkSetFormat(colorValue,true));
        this.$selection.css("background-color", hueColor);
        this.$hueBase.css("background-color", hueColor);

        // Update gradients in color square & opacity slider
        this.$selectionBase.css("background-color", colorObject.toHexString());
        this.$opacityGradient.css("background-image", "linear-gradient(" + hueColor + ", transparent)");

        // Update slider thumb positions
        this.$hueSelector.css("bottom", (this._hsv.h / 360 * 100) + "%");
        this.$opacitySelector.css("bottom", (this._hsv.a * 100) + "%");
        if (!isNaN(this._hsv.s)) {      // TODO (#2201): type of _hsv.s/.v is unpredictable
            this._hsv.s = (this._hsv.s * 100) + "%";
        }
        if (!isNaN(this._hsv.v)) {
            this._hsv.v = (this._hsv.v * 100) + "%";
        }
        this.$selectionBase.css({
            left: this._hsv.s,
            bottom: this._hsv.v
        });
    };

    /**
     * Focus the main color square's thumb.
     * @return {boolean} True if we focused the square, false otherwise.
     */
    ColorEditor.prototype.focus = function () {
        if (!this.$selectionBase.is(":focus")) {
            this.$selectionBase.focus();
            return true;
        }
        return false;
    };

    /**
     * Remove any preference listeners before destroying the editor.
     */
    ColorEditor.prototype.destroy = function () {
        PreferencesManager.off("change", "uppercaseColors");
    };

    /**
     * @return {tinycolor|string} The currently selected color (TODO (#2201): type is unpredictable).
     */
    ColorEditor.prototype.getColor = function () {
        return this._color;
    };

    /** Update the format button bar's selection */
    ColorEditor.prototype._updateColorTypeRadioButtons = function (format) {
        this.$buttonList.find("li").removeClass("selected");
        switch (format) {
        case "rgb":
            this.$buttonList.find(".rgba").parent().addClass("selected");
            break;
        case "hex":
        case "name":
            this.$buttonList.find(".hex").parent().addClass("selected");
            break;
        case "hsl":
            this.$buttonList.find(".hsla").parent().addClass("selected");
            break;
        case "0x":
            this.$buttonList.find(".0x").parent().addClass("selected");
            break;
        }
    };

    /** Add event listeners to the format button bar */
    ColorEditor.prototype._bindColorFormatToRadioButton = function (buttonClass, propertyName, value) {
        var handler,
            self = this;
        handler = function (event) {
            var newFormat   = $(event.currentTarget).html().toLowerCase().replace("%", "p"),
                newColor    = self.getColor().toString();

            var colorObject = checkSetFormat(newColor);

            switch (newFormat) {
            case "hsla":
                newColor = colorObject.toHslString();
                break;
            case "rgba":
                newColor = colorObject.toRgbString();
                break;
            case "prgba":
                newColor = colorObject.toPercentageRgbString();
                break;
            case "hex":
                newColor = colorObject.toHexString();
                self._hsv.a = 1;
                break;
            case "0x":
                newColor = as0xString(colorObject);
                self._hsv.a = 1;
                self._format = "0x";
                break;
            }

            // We need to run this again whenever RGB/HSL/Hex conversions
            // are performed to preserve the case
            newColor = self._isUpperCase ? newColor.toUpperCase() : newColor;
            self._commitColor(newColor, false);
        };
        this.$element.find("." + buttonClass).click(handler);
    };

    /** Add event listener to the "original color value" swatch */
    ColorEditor.prototype._bindOriginalColorButton = function () {
        var self = this;
        this.$originalColor.click(function (event) {
            self._commitColor(self._originalColor, true);
        });
    };

    /**
     * Convert percentage values in an RGB color into normal RGB values in the range of 0 - 255.
     * If the original color is already in non-percentage format, does nothing.
     * @param {string} color The color to be converted to non-percentage RGB color string.
     * @return {string} an RGB color string in the normal format using non-percentage values
     */
    ColorEditor.prototype._convertToNormalRGB = function (color) {
        var matches = color.match(/^rgb.*?([0-9]+)\%.*?([0-9]+)\%.*?([0-9]+)\%/i);
        if (matches) {
            var i, percentStr, value;
            for (i = 0; i < 3; i++) {
                percentStr = matches[i + 1];
                value = Math.round(255 * Number(percentStr) / 100);
                if (!isNaN(value)) {
                    color = color.replace(percentStr + "%", value);
                }
            }
        }
        return color;
    };

    /**
     * Normalize the given color string into the format used by tinycolor, by adding a space
     * after commas.
     * @param {string} color The color to be corrected if it looks like an RGB or HSL color.
     * @return {string} a normalized color string.
     */
    ColorEditor.prototype._normalizeColorString = function (color) {
        var normalizedColor = color;

        // Convert from 0x notation into hex format string
        if (color.match(/^0x[0-9a-fA-F]{6}/)) {
            return color.replace("0x","#") || color;
        }
        // Convert 6-digit hex to 3-digit hex as TinyColor (#ffaacc -> #fac)
        if (color.match(/^#[0-9a-fA-F]{6}/)) {
            return tinycolor(color).toString();
        }
        if (color.match(/^(rgb|hsl)/i)) {
            normalizedColor = normalizedColor.replace(/,\s*/g, ", ");
            normalizedColor = normalizedColor.replace(/\(\s+/, "(");
            normalizedColor = normalizedColor.replace(/\s+\)/, ")");
        }
        return normalizedColor;
    };

    /** Handle changes in text field */
    ColorEditor.prototype._handleTextFieldInput = function (losingFocus) {
        var newColor    = $.trim(this.$colorValue.val()),
            newColorObj = checkSetFormat(newColor),
            newColorOk  = newColorObj.isValid();

        // TinyColor will auto correct an incomplete rgb or hsl value into a valid color value.
        // eg. rgb(0,0,0 -> rgb(0, 0, 0)
        // We want to avoid having TinyColor do this, because we don't want to sync the color
        // to the UI if it's incomplete. To accomplish this, we first normalize the original
        // color string into the format TinyColor would generate, and then compare it to what
        // TinyColor actually generates to see if it's different. If so, then we assume the color
        // was incomplete to begin with.
        if (newColorOk) {
            newColorOk = (newColorObj.toString() === this._normalizeColorString(newColor));
        }

        // Restore to the previous valid color if the new color is invalid or incomplete.
        if (losingFocus && !newColorOk) {
            newColor = this.getColor().toString();
        }

        // Sync only if we have a valid color or we're restoring the previous valid color.
        if (losingFocus || newColorOk) {
            this._commitColor(newColor, true);
        }
    };

    ColorEditor.prototype._bindInputHandlers = function () {
        var self = this;

        this.$colorValue.bind("input", function (event) {
            self._handleTextFieldInput(false);
        });

        this.$colorValue.bind("change", function (event) {
            self._handleTextFieldInput(true);
        });
    };

    /**
     * Populate the UI with the given color swatches and add listeners so they're selectable.
     * @param {!Array.<{value:string, count:number}>} swatches
     */
    ColorEditor.prototype._addSwatches = function (swatches) {
        var self = this;

        // Create swatches
        swatches.forEach(function (swatch) {
            var swatchValue = swatch.value.replace("0x","#") || swatch.value;
            var stringFormat = (swatch.count > 1) ? Strings.COLOR_EDITOR_USED_COLOR_TIP_PLURAL : Strings.COLOR_EDITOR_USED_COLOR_TIP_SINGULAR,
                usedColorTip = StringUtils.format(stringFormat, swatch.value, swatch.count);

            self.$swatches.append("

 " + swatch.value + "
");
        });

        // Add key & click listeners to each
        this.$swatches.find("li").keydown(function (event) {
            if (event.keyCode === KeyEvent.DOM_VK_RETURN ||
                    event.keyCode === KeyEvent.DOM_VK_ENTER ||
                    event.keyCode === KeyEvent.DOM_VK_SPACE) {
                // Enter/Space is same as clicking on swatch

                self._commitColor($(event.currentTarget).find(".value").html());
            } else if (event.keyCode === KeyEvent.DOM_VK_TAB) {
                // Tab on last swatch loops back to color square
                if (!event.shiftKey && $(this).next("li").length === 0) {
                    self.$selectionBase.focus();
                    return false;
                }
            }
        });

        this.$swatches.find("li").click(function (event) {
            self._commitColor($(event.currentTarget).find(".value").html());
        });
    };

    /**
     * Checks whether colorVal is a valid color
     * @param {!string} colorVal
     * @return {boolean} Whether colorVal is valid
     */
    ColorEditor.prototype.isValidColor = function (colorVal) {
        return tinycolor(colorVal).isValid();
    };

    /**
     * Sets _hsv and _color based on an HSV input, and updates the UI. Attempts to preserve
     * the previous color format.
     * @param {!{h:number=, s:number=, v:number=}} hsv  Any missing values use the previous color's values.
     */
    ColorEditor.prototype.setColorAsHsv = function (hsv) {
        var colorVal, newColor,
            oldFormat = tinycolor(this.getColor()).getFormat();

        // Set our state to the new color
        $.extend(this._hsv, hsv);
        newColor = tinycolor(this._hsv);

        switch (oldFormat) {
        case "hsl":
            colorVal = newColor.toHslString();
            break;
        case "rgb":
            colorVal = newColor.toRgbString();
            break;
        case "prgb":
            colorVal = newColor.toPercentageRgbString();
            break;
        case "hex":
        case "name":
            colorVal = this._hsv.a < 1 ? newColor.toRgbString() : newColor.toHexString();
            break;
        case "0x":
            colorVal = as0xString(newColor);
            break;
        }
        colorVal = this._isUpperCase ? colorVal.toUpperCase() : colorVal;
        this._commitColor(colorVal, false);
    };

    /**
     * Sets _color (and optionally _hsv) based on a string input, and updates the UI. The string's
     * format determines the new selected color's format.
     * @param {!string} colorVal
     * @param {boolean=} resetHsv  Pass false ONLY if hsv set already been modified to match colorVal. Default: true.
     */
    ColorEditor.prototype._commitColor = function (colorVal, resetHsv) {

        if (resetHsv === undefined) {
            resetHsv = true;
        }
        this._callback(colorVal);

        var colorObj = checkSetFormat(colorVal);
        colorObj._originalInput = colorVal;
        this._color = colorObj;

        if (resetHsv) {
            this._hsv = this._color.toHsv();
        }

        this._redoColor = null;  // if we had undone, this new value blows away the redo history
        this._synchronize();
    };

    /**
     * Sets _color and _hsv based on a string input, and updates the UI. The string's
     * format determines the new selected color's format.
     * @param {!string} colorVal
     */
    ColorEditor.prototype.setColorFromString = function (colorVal) {
        this._commitColor(colorVal, true);  // TODO (#2204): make this less entangled with setColorAsHsv()
    };

    /** Converts a mouse coordinate to be relative to zeroPos, and clips to [0, maxOffset] */
    function _getNewOffset(pos, zeroPos, maxOffset) {
        var offset = pos - zeroPos;
        offset = Math.min(maxOffset, Math.max(0, offset));
        return offset;
    }

    /** Dragging color square's thumb */
    ColorEditor.prototype._handleSelectionFieldDrag = function (event) {
        var height  = this.$selection.height(),
            width   = this.$selection.width(),
            xOffset = _getNewOffset(event.clientX, this.$selection.offset().left, width),
            yOffset = _getNewOffset(event.clientY, this.$selection.offset().top, height),
            hsv     = {};
        hsv.s = xOffset / width;
        hsv.v = 1 - yOffset / height;
        this.setColorAsHsv(hsv, false);
        if (!this.$selection.find(".selector-base").is(":focus")) {
            this.$selection.find(".selector-base").focus();
        }
    };

    /** Dragging hue slider thumb */
    ColorEditor.prototype._handleHueDrag = function (event) {
        var height = this.$hueSlider.height(),
            offset = _getNewOffset(event.clientY, this.$hueSlider.offset().top, height),
            hsv    = {};
        hsv.h = (1 - offset / height) * 360;
        this.setColorAsHsv(hsv, false);
        if (!this.$hueSlider.find(".selector-base").is(":focus")) {
            this.$hueSlider.find(".selector-base").focus();
        }
    };

    /** Dragging opacity slider thumb */
    ColorEditor.prototype._handleOpacityDrag = function (event) {
        var height = this.$opacitySlider.height(),
            offset = _getNewOffset(event.clientY, this.$opacitySlider.offset().top, height),
            hsv    = {};
        hsv.a = 1 - (offset / height);
        this.setColorAsHsv(hsv, false);
        if (!this.$opacitySlider.find(".selector-base").is(":focus")) {
            this.$opacitySlider.find(".selector-base").focus();
        }
    };

    /**
     * Helper for attaching drag-related mouse listeners to an element. It's up to
     * 'handler' to actually move the element as mouse is dragged.
     * @param {!function(jQuery.event)} handler  Called whenever drag position changes
     */
    ColorEditor.prototype._registerDragHandler = function ($element, handler) {
        var mouseupHandler = function (event) {
            $(window).unbind("mousemove", handler);
            $(window).unbind("mouseup", mouseupHandler);
        };
        $element.mousedown(function (event) {
            $(window).bind("mousemove", handler);
            $(window).bind("mouseup", mouseupHandler);
        });
        $element.mousedown(handler);  // run drag-update handler on initial mousedown too
    };

    /**
     * Handles undo gestures while color picker has focus. We don't want to let CodeMirror's
     * usual undo logic run since it will destroy our marker.
     */
    ColorEditor.prototype.undo = function () {
        if (this._originalColor.toString() !== this._color.toString()) {
            var curColor = this._color.toString();
            this._commitColor(this._originalColor, true);
            this._redoColor = curColor;
        }
    };

    /** Similarly, handle redo gestures while color picker has focus. */
    ColorEditor.prototype.redo = function () {
        if (this._redoColor) {
            this._commitColor(this._redoColor, true);
            this._redoColor = null;
        }
    };

    /**
     * Global handler for keys in the color editor. Catches undo/redo keys and traps
     * arrow keys that would be handled by the scroller.
     */
    ColorEditor.prototype._handleKeydown = function (event) {
        var hasCtrl = (brackets.platform === "win") ? (event.ctrlKey) : (event.metaKey);
        if (hasCtrl) {
            switch (event.keyCode) {
            case KeyEvent.DOM_VK_Z:
                if (event.shiftKey) {
                    this.redo();
                } else {
                    this.undo();
                }
                return false;
            case KeyEvent.DOM_VK_Y:
                this.redo();
                return false;
            }
        } else {
            if (event.keyCode === KeyEvent.DOM_VK_LEFT ||
                    event.keyCode === KeyEvent.DOM_VK_RIGHT ||
                    event.keyCode === KeyEvent.DOM_VK_UP ||
                    event.keyCode === KeyEvent.DOM_VK_DOWN) {
                // Prevent arrow keys that weren't handled by a child control
                // from being handled by a parent, either through bubbling or
                // through default native behavior. There isn't a good general
                // way to tell if the target would handle this event by default,
                // so we look to see if the target is a text input control.
                var preventDefault = false,
                    $target = $(event.target);

                // If the input has no "type" attribute, it defaults to text. So we
                // have to check for both possibilities.
                if ($target.is("input:not([type])") || $target.is("input[type=text]")) {
                    // Text input control. In WebKit, if the cursor gets to the start
                    // or end of a text field and can't move any further, the default
                    // action doesn't take place in the text field, so the event is handled
                    // by the outer scroller. We have to prevent in that case too.
                    if ($target[0].selectionStart === $target[0].selectionEnd &&
                            ((event.keyCode === KeyEvent.DOM_VK_LEFT && $target[0].selectionStart === 0) ||
                             (event.keyCode === KeyEvent.DOM_VK_RIGHT && $target[0].selectionEnd === $target.val().length))) {
                        preventDefault = true;
                    }
                } else {
                    // Not a text input control, so we want to prevent default.
                    preventDefault = true;
                }

                if (preventDefault) {
                    event.stopPropagation();
                    return false; // equivalent to event.preventDefault()
                }
            }
        }
    };

    ColorEditor.prototype._handleHslKeydown = function (event) {
        if (event.keyCode === KeyEvent.DOM_VK_TAB) {
            // If we're the last focusable element (no color swatches), Tab wraps around to color square
            if (!event.shiftKey) {
                if (this.$swatches.children().length === 0) {
                    this.$selectionBase.focus();
                    return false;
                }
            }
        }
    };

    /** Key events on the color square's thumb */
    ColorEditor.prototype._handleSelectionKeydown = function (event) {
        var hsv = {},
            step = 1.5,
            xOffset,
            yOffset,
            adjustedOffset;

        switch (event.keyCode) {
        case KeyEvent.DOM_VK_LEFT:
        case KeyEvent.DOM_VK_RIGHT:
            step = event.shiftKey ? step * STEP_MULTIPLIER : step;
            xOffset = Number($.trim(this.$selectionBase[0].style.left.replace("%", "")));
            adjustedOffset = (event.keyCode === KeyEvent.DOM_VK_LEFT) ? (xOffset - step) : (xOffset + step);
            xOffset = Math.min(100, Math.max(0, adjustedOffset));
            hsv.s = xOffset / 100;
            this.setColorAsHsv(hsv, false);
            return false;
        case KeyEvent.DOM_VK_DOWN:
        case KeyEvent.DOM_VK_UP:
            step = event.shiftKey ? step * STEP_MULTIPLIER : step;
            yOffset = Number($.trim(this.$selectionBase[0].style.bottom.replace("%", "")));
            adjustedOffset = (event.keyCode === KeyEvent.DOM_VK_DOWN) ? (yOffset - step) : (yOffset + step);
            yOffset = Math.min(100, Math.max(0, adjustedOffset));
            hsv.v = yOffset / 100;
            this.setColorAsHsv(hsv, false);
            return false;
        case KeyEvent.DOM_VK_TAB:
            // Shift+Tab loops back to last focusable element: last swatch if any; format button bar if not
            if (event.shiftKey) {
                if (this.$swatches.children().length === 0) {
                    this.$hslButton.focus();
                } else {
                    this.$swatches.find("li:last").focus();
                }
                return false;
            }
            break;
        }
    };

    /** Key events on the hue slider thumb */
    ColorEditor.prototype._handleHueKeydown = function (event) {
        var hsv = {},
            hue = Number(this._hsv.h),
            step = 3.6;

        switch (event.keyCode) {
        case KeyEvent.DOM_VK_DOWN:
            step = event.shiftKey ? step * STEP_MULTIPLIER : step;
            hsv.h = (hue - step) <= 0 ? 360 - step : hue - step;
            this.setColorAsHsv(hsv, false);
            return false;
        case KeyEvent.DOM_VK_UP:
            step = event.shiftKey ? step * STEP_MULTIPLIER : step;
            hsv.h = (hue + step) >= 360 ? step : hue + step;
            this.setColorAsHsv(hsv, false);
            return false;
        }
    };

    /** Key events on the opacity slider thumb */
    ColorEditor.prototype._handleOpacityKeydown = function (event) {
        var alpha = this._hsv.a,
            hsv = {},
            step = 0.01;

        switch (event.keyCode) {
        case KeyEvent.DOM_VK_DOWN:
            step = event.shiftKey ? step * STEP_MULTIPLIER : step;
            if (alpha > 0) {
                hsv.a = (alpha - step) <= 0 ? 0 : alpha - step;
                this.setColorAsHsv(hsv);
            }
            return false;
        case KeyEvent.DOM_VK_UP:
            step = event.shiftKey ? step * STEP_MULTIPLIER : step;
            if (alpha < 100) {
                hsv.a = (alpha + step) >= 1 ? 1 : alpha + step;
                this.setColorAsHsv(hsv);
            }
            return false;
        }
    };

    ColorEditor.prototype._bindKeyHandler = function ($element, handler) {
        $element.bind("keydown", handler);
    };

    // Prevent clicks on some UI elements (color selection field, slider and large swatch) from taking focus
    $(window.document).on("mousedown", ".color-selection-field, .slider, .large-swatch", function (e) {
        e.preventDefault();
    });

    exports.ColorEditor = ColorEditor;
});


/* ==========================================================
 * bootstrap-twipsy.js v1.4.0
 * http://twitter.github.com/bootstrap/javascript.html#twipsy
 * Adapted from the original jQuery.tipsy by Jason Frame
 * Adjusted for Brackets
 * ==========================================================
 * Copyright 2011 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

!function( $ ) {

  "use strict"

  /***** [changed for Brackets] *****/
  // Undefined until the focus state changed once
  var _windowHasFocus;

  $(window)
    .focus(function _onWindowGainedFocus() {
      _windowHasFocus = true;
    })
    .blur(function _onWindowLostFocus() {
      _windowHasFocus = false;
    });
  /***** [/changed for Brackets] *****/

 /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
  * ======================================================= */

  var transitionEnd

  $(document).ready(function () {

    $.support.transition = (function () {

      var transitionEnd = (function () {

        var el = document.createElement('bootstrap')
          , transEndEventNames = {
               'WebkitTransition' : 'webkitTransitionEnd'
            ,  'MozTransition'    : 'transitionend'
            ,  'OTransition'      : 'oTransitionEnd'
            ,  'msTransition'     : 'MSTransitionEnd'
            ,  'transition'       : 'transitionend'
            }
          , name

        for (name in transEndEventNames){
          if (el.style[name] !== undefined) {
            return transEndEventNames[name]
          }
        }

      }())

      return transitionEnd && {
        end: transitionEnd
      }

    })()

    // set CSS transition event type
    if ( $.support.transition ) {
      transitionEnd = $.support.transition.end
    }

  })

 /* TWIPSY PUBLIC CLASS DEFINITION
  * ============================== */

  var Twipsy = function ( element, options ) {
    this.$element = $(element)
    this.options = options
    this.enabled = true
    /***** [changed for Brackets] *****/
    this.autoHideTimeout = null;
    /***** [/changed for Brackets] *****/
    this.fixTitle()
  }

  Twipsy.prototype = {

    show: function() {
      /***** [changed for Brackets: moved some variables to updatePosition()] *****/
      var $tip
        , that = this;
      /***** [/changed for Brackets] *****/

      if (this.hasContent() && this.enabled) {
        $tip = this.tip()
        this.setContent()

        if (this.options.animate) {
          $tip.addClass('fade')
        }

        $tip
          .remove()
          .css({ top: 0, left: 0, display: 'block' })
          .prependTo(document.body)

/***** [changed for Brackets] *****/
        this.updatePosition();

        $(window).off("resize", this.resizeHandler);
        this.resizeHandler = function(e) {
          that.updatePosition();
        };
        $(window).on("resize", this.resizeHandler);

        if (this.options.autoHideDelay) {
          var startAutoHide = function () {
            window.clearTimeout(that.autoHideTimeout);
            that.autoHideTimeout = window.setTimeout(function () {
              that.hide();
            }, that.options.autoHideDelay);
          }
          if (_windowHasFocus) {
            startAutoHide();
          } else {
            $(window).one("focus", startAutoHide);
          }
        }

        $tip.addClass('in');
      }
    }

  , updatePosition: function () {
      var pos
        , actualWidth
        , actualHeight
        , paddingLeft
        , paddingRight
        , surplusRight
        , shiftArrow
        , placement
        , $tip
        , $arrow
        , tp
        , that = this

      $tip = this.tip()

      pos = $.extend({}, this.$element.offset(), {
        width: this.$element[0].offsetWidth
      , height: this.$element[0].offsetHeight
      })

      paddingLeft  = parseInt(this.$element.css("padding-left"),  10);
      paddingRight = parseInt(this.$element.css("padding-right"), 10);

      pos.left += paddingLeft;
      pos.width -= (paddingLeft + paddingRight);

      actualWidth = $tip[0].offsetWidth
      actualHeight = $tip[0].offsetHeight

      placement = maybeCall(this.options.placement, this, [ $tip[0], this.$element[0] ])
      // Add the placement class so the arrow's margin can be determined
      $tip.addClass(placement)

      switch (placement) {
        case 'below':
          tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
          break
        case 'above':
          tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
          break
        case 'left':
          /***** [changed for Brackets] *****/
          tp = {top: pos.top + pos.height + 20 - actualHeight, left: pos.left - actualWidth - this.options.offset}
          break
        case 'right':
          /***** [changed for Brackets] *****/
          tp = {top: pos.top + pos.height + 20 - actualHeight, left: pos.left + pos.width + this.options.offset}
          break
      }

      shiftArrow = 0;

      surplusRight = (tp.left + actualWidth - $(document.body).width());
      if (surplusRight > 0) {
        shiftArrow = surplusRight;
        tp.left -= surplusRight;
      } else if (tp.left < 0) {
        shiftArrow = tp.left;
        tp.left = 0;
      }

      if (surplusRight > 0) {
        $arrow = $tip.find(".tooltip-arrow");
        if (! this.defaultMargin) {
          this.defaultMargin = parseInt($arrow.css("margin-left"), 10);
        }
        $arrow.css("margin-left", this.defaultMargin + shiftArrow);
      }

      $tip.css(tp);
    }
/***** [/changed for Brackets] *****/

  , setContent: function () {
      var $tip = this.tip()
      $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](this.getTitle())
      $tip[0].className = 'tooltip'
    }

  , hide: function() {
      var that = this
        , $tip = this.tip()

      $tip.removeClass('in')

      function removeElement () {
        $tip.remove()
      }

      $.support.transition && this.$tip.hasClass('fade') ?
        $tip.bind(transitionEnd, removeElement) :
        removeElement()

      /***** [changed for Brackets] *****/
      window.clearTimeout(this.autoHideTimeout);
      $(window).off("resize", this.resizeHandler)
      /***** [/changed for Brackets] *****/
    }

  , fixTitle: function() {
      var $e = this.$element
      if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
        $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
      }
    }

  , hasContent: function () {
      return this.getTitle()
    }

  , getTitle: function() {
      var title
        , $e = this.$element
        , o = this.options

        this.fixTitle()

        if (typeof o.title == 'string') {
          title = $e.attr(o.title == 'title' ? 'data-original-title' : o.title)
        } else if (typeof o.title == 'function') {
          title = o.title.call($e[0])
        }

        title = ('' + title).replace(/(^\s*|\s*$)/, "")

        return title || o.fallback
    }

  , tip: function() {
      return this.$tip = this.$tip || $('
').html(this.options.template)
    }

  , validate: function() {
      if (!this.$element[0].parentNode) {
        this.hide()
        this.$element = null
        this.options = null
      }
    }

  , enable: function() {
      this.enabled = true
    }

  , disable: function() {
      this.enabled = false
    }

  , toggleEnabled: function() {
      this.enabled = !this.enabled
    }

  , toggle: function () {
      this[this.tip().hasClass('in') ? 'hide' : 'show']()
    }

  }

 /* TWIPSY PRIVATE METHODS
  * ====================== */

   function maybeCall ( thing, ctx, args ) {
     return typeof thing == 'function' ? thing.apply(ctx, args) : thing
   }

 /* TWIPSY PLUGIN DEFINITION
  * ======================== */

  $.fn.twipsy = function (options) {
    $.fn.twipsy.initWith.call(this, options, Twipsy, 'twipsy')
    return this
  }

  $.fn.twipsy.initWith = function (options, Constructor, name) {
    var twipsy
      , binder
      , eventIn
      , eventOut

    if (options === true) {
      return this.data(name)
    } else if (typeof options == 'string') {
      twipsy = this.data(name)
      if (twipsy) {
        twipsy[options]()
      }
      return this
    }

    options = $.extend({}, $.fn[name].defaults, options)

    function get(ele) {
      var twipsy = $.data(ele, name)

      if (!twipsy) {
        twipsy = new Constructor(ele, $.fn.twipsy.elementOptions(ele, options))
        $.data(ele, name, twipsy)
      }

      return twipsy
    }

    function enter() {
      var twipsy = get(this)
      twipsy.hoverState = 'in'

      if (options.delayIn == 0) {
        twipsy.show()
      } else {
        twipsy.fixTitle()
        setTimeout(function() {
          if (twipsy.hoverState == 'in') {
            twipsy.show()
          }
        }, options.delayIn)
      }
    }

    function leave() {
      var twipsy = get(this)
      twipsy.hoverState = 'out'
      if (options.delayOut == 0) {
        twipsy.hide()
      } else {
        setTimeout(function() {
          if (twipsy.hoverState == 'out') {
            twipsy.hide()
          }
        }, options.delayOut)
      }
    }

    if (!options.live) {
      this.each(function() {
        get(this)
      })
    }

    if (options.trigger != 'manual') {
      binder   = options.live ? 'live' : 'bind'
      eventIn  = options.trigger == 'hover' ? 'mouseenter' : 'focus'
      eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'
      this[binder](eventIn, enter)[binder](eventOut, leave)
    }

    return this
  }

  $.fn.twipsy.Twipsy = Twipsy

  $.fn.twipsy.defaults = {
    animate: true
  , delayIn: 0
  , delayOut: 0
  , fallback: ''
  , placement: 'above'
  , html: false
  , live: false
  , offset: 0
  , title: 'title'
  , trigger: 'hover'
  , template: '

'
  }

  $.fn.twipsy.rejectAttrOptions = [ 'title' ]

  $.fn.twipsy.elementOptions = function(ele, options) {
    var data = $(ele).data()
      , rejects = $.fn.twipsy.rejectAttrOptions
      , i = rejects.length

    while (i--) {
      delete data[rejects[i]]
    }

    return $.extend({}, options, data)
  }

}( window.jQuery || window.ender );


var mkdirp = require('mkdirp');

module.exports.mkdirs = mkdirp;
module.exports.mkdirsSync = mkdirp.sync;


// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var common = require('../common');
var assert = require('assert');

var Readable = require('../../lib/_stream_readable');
var EE = require('events').EventEmitter;

var oldStream = new EE();
oldStream.pause = function(){};
oldStream.resume = function(){};

var newStream = new Readable().wrap(oldStream);

var ended = false;
newStream
  .on('readable', function(){})
  .on('end', function(){ ended = true; });

oldStream.emit('end');

process.on('exit', function(){
  assert.ok(ended);
});