// Check for gradient. -webkit-gradient() can have parens in parameters

// nested 2 levels. Other gradients can only nest 1 level.

gradientMatch = line.match(gradientRegEx1) || line.match(gradientRegEx2),

keep keep keep replace keep keep keep keep

/*
 * 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, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, describe, it, expect, beforeEach, afterEach, waitsFor, runs, $, brackets, waitsForDone */

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

    var SpecRunnerUtils = brackets.getModule("spec/SpecRunnerUtils"),
        FileUtils       = brackets.getModule("file/FileUtils");

    describe("Hover Preview", function () {
        var testFolder = FileUtils.getNativeModuleDirectoryPath(module) + "/unittest-files/";
        var testWindow, brackets, CommandManager, Commands, EditorManager, HoverPreview, editor;

        beforeEach(function () {
            // Create a new window that will be shared by ALL tests in this spec.
            if (!testWindow) {
                runs(function () {
                    SpecRunnerUtils.createTestWindowAndRun(this, function (w) {
                        testWindow = w;
                        // Load module instances from brackets.test
                        brackets = testWindow.brackets;
                        CommandManager = testWindow.brackets.test.CommandManager;
                        Commands = testWindow.brackets.test.Commands;
                        EditorManager = brackets.test.EditorManager;
                        HoverPreview = brackets.test.extensions.HoverPreview;
                    });
                });

                runs(function () {
                    SpecRunnerUtils.loadProjectInTestWindow(testFolder);
                });

                runs(function () {
                    waitsForDone(SpecRunnerUtils.openProjectFiles(["test.css"]), "open test file");
                });

                runs(function () {
                    editor = EditorManager.getCurrentFullEditor();
                });
            }
        });

        function getPopoverAtPos(lineNum, columnNum) {
            var cm = editor._codeMirror,
                pos = { line: lineNum, ch: columnNum },
                token;

            editor.setCursorPos(pos);
            token = cm.getTokenAt(pos);

            return HoverPreview._queryPreviewProviders(editor, pos, token);
        }

        function expectNoPreviewAtPos(line, ch) {
            var popoverInfo = getPopoverAtPos(line, ch);
            expect(popoverInfo).toBeFalsy();
        }

        function checkColorAtPos(expectedColor, line, ch) {
            var popoverInfo = getPopoverAtPos(line, ch);
            expect(popoverInfo._previewCSS).toBe(expectedColor);
        }

        function checkGradientAtPos(expectedGradient, line, ch) {
            // Just call checkColorAtPos since both have the same function calls.
            checkColorAtPos(expectedGradient, line, ch);
        }

        function checkImagePathAtPos(expectedPathEnding, line, ch) {
            var popoverInfo = getPopoverAtPos(line, ch),
                imagePath = popoverInfo._imgPath;

            // Just check end of path - local drive location prefix unimportant
            expect(imagePath.substr(imagePath.length - expectedPathEnding.length)).toBe(expectedPathEnding);
        }

        describe("Hover preview colors", function () {
            it("should show preview of hex colors either in 3 digit hex or or 6-digit hex", function () {
                runs(function () {
                    checkColorAtPos("#369", 3, 12);
                    checkColorAtPos("#2491F5", 4, 13);
                });
            });

            it("should NOT show preview of color on words start with #", function () {
                runs(function () {
                    expectNoPreviewAtPos(7, 7);     // cursor on #invalid_hex
                    expectNoPreviewAtPos(8, 15);    // cursor on #web
                });
            });

            it("should show preview of valid rgb/rgba colors", function () {
                runs(function () {
                    checkColorAtPos("rgb(255,0,0)",       12, 12);
                    checkColorAtPos("rgb(100%, 0%, 0%)",  13, 17);
                    checkColorAtPos("rgb(50%, 75%, 25%)", 14, 24);

                    // rgba with values of 0-255
                    checkColorAtPos("rgba(255, 0, 0, 0.5)", 15, 23);
                    checkColorAtPos("rgba(255, 0, 0, 1)",   16, 22);
                    checkColorAtPos("rgba(255, 0, 0, .5)",  17, 19);

                    // rgba with percentage values
                    checkColorAtPos("rgba(100%, 0%, 0%, 0.5)",  18, 32);
                    checkColorAtPos("rgba(80%, 50%, 50%, 1)",   20, 33);
                    //checkColorAtPos("rgba(50%, 75%, 25%, 1.0)", 21, 23);  // TODO (#3454): not working yet
                });
            });

            it("should NOT show preview of unsupported rgb/rgba colors", function () {
                runs(function () {
                    expectNoPreviewAtPos(25, 14);    // cursor on rgb(300, 0, 0)
                    expectNoPreviewAtPos(26, 15);    // cursor on rgb(0, 95.5, 0)
                    expectNoPreviewAtPos(27, 15);    // cursor on rgba(-0, 0, 0, 0.5)
                });
            });

            it("should show preview of valid hsl/hsla colors", function () {
                runs(function () {
                    checkColorAtPos("hsl(0, 100%, 50%)",       31, 22);
                    checkColorAtPos("hsla(0, 100%, 50%, 0.5)", 32, 23);
                    checkColorAtPos("hsla(0, 100%, 50%, .5)",  33, 23);
                });
            });

            it("should NOT show preview of unsupported hsl/hsla colors", function () {
                runs(function () {
                    expectNoPreviewAtPos(37, 24);    // cursor on hsl(390, 100%, 50%)
                    expectNoPreviewAtPos(38, 25);    // cursor on hsla(90, 100%, 50%, 2)
                    expectNoPreviewAtPos(39, 24);    // cursor on hsla(0, 200%, 50%, 0.5)
                    expectNoPreviewAtPos(40, 25);    // cursor on hsla(0.0, 100%, 50%, .5)
                });
            });

            it("should show preview of colors with valid names", function () {
                runs(function () {
                    checkColorAtPos("blueviolet",    47, 15);
                    checkColorAtPos("darkgoldenrod", 49, 16);
                    checkColorAtPos("darkgray",      50, 16);
                    checkColorAtPos("firebrick",     51, 15);
                    checkColorAtPos("honeydew",      53, 16);
                    checkColorAtPos("lavenderblush", 56, 16);
                    checkColorAtPos("salmon",        61, 16);
                    checkColorAtPos("tomato",        66, 16);
                });
            });

            it("should NOT show preview of colors with invalid names", function () {
                runs(function () {
                    expectNoPreviewAtPos(72, 15);    // cursor on redsox
                    expectNoPreviewAtPos(73, 16);    // cursor on pinky
                    // issue #3445
//                    expectNoPreviewAtPos(74, 16);    // cursor on blue in hyphenated word blue-chesse
//                    expectNoPreviewAtPos(75, 18);    // cursor on white in hyphenated word @bc-white
                });
            });
        });

        describe("Hover preview gradients", function () {
            it("Should show linear gradient preview for those with vendor prefix", function () {
                runs(function () {
                    var expectedGradient1 = "-webkit-linear-gradient(top,  #d2dfed 0%, #c8d7eb 26%, #bed0ea 51%, #a6c0e3 51%, #afc7e8 62%, #bad0ef 75%, #99b5db 88%, #799bc8 100%)",
                        expectedGradient2 = "-webkit-gradient(linear, left top, left bottom, color-stop(0%,#d2dfed), color-stop(26%,#c8d7eb), color-stop(51%,#bed0ea), color-stop(51%,#a6c0e3), color-stop(62%,#afc7e8), color-stop(75%,#bad0ef), color-stop(88%,#99b5db), color-stop(100%,#799bc8));",
                        expectedGradient3 = "-webkit-linear-gradient(top,  #d2dfed 0%,#c8d7eb 26%,#bed0ea 51%,#a6c0e3 51%,#afc7e8 62%,#bad0ef 75%,#99b5db 88%,#799bc8 100%)";
                    checkGradientAtPos(expectedGradient1, 80, 36);   // -moz- prefix gets stripped
                    checkGradientAtPos(expectedGradient2, 81, 36);   // Old webkit syntax
                    checkGradientAtPos(expectedGradient3, 82, 36);   // -webkit- prefix gets stripped
                    checkGradientAtPos(expectedGradient3, 83, 36);   // -o- prefix gets stripped
                    checkGradientAtPos(expectedGradient3, 84, 36);   // -ms- prefix gets stripped
                });
            });

            it("Should show linear gradient preview for those with w3c standard syntax (no prefix)", function () {
                runs(function () {
                    checkGradientAtPos("-webkit-linear-gradient(#333, #CCC)",                  99, 50);
                    checkGradientAtPos("-webkit-linear-gradient(135deg, #333, #CCC)",          101, 50);

                    // TODO (#3458): Keyword "to" not supported until Brackets upgrades to Chrome 26
                    //checkGradientAtPos("-webkit-linear-gradient(to right, #333, #CCC)",        98, 50);
                    //checkGradientAtPos("-webkit-linear-gradient(to bottom right, #333, #CCC)", 100, 50);
                    expectNoPreviewAtPos(98, 50);
                    expectNoPreviewAtPos(100, 50);

                    // multiple colors
                    checkGradientAtPos("-webkit-linear-gradient(#333, #CCC, #333)",             104, 50);
                    checkGradientAtPos("-webkit-linear-gradient(#333 0%, #CCC 33%, #333 100%)", 105, 50);
                    checkGradientAtPos("-webkit-linear-gradient(yellow, blue 20%, #0f0)",       106, 50);
                });
            });

            it("Should show radial gradient preview for those with vendor prefix syntax", function () {
                runs(function () {
                    var expectedGradient1 = "-webkit-gradient(radial, center center, 0, center center, 141, from(black), to(white), color-stop(25%, blue), color-stop(40%, green), color-stop(60%, red), color-stop(80%, purple));",
                        expectedGradient2 = "-webkit-radial-gradient(center center, circle contain, black 0%, blue 25%, green 40%, red 60%, purple 80%, white 100%)";
                    checkGradientAtPos(expectedGradient1, 110, 93);   // old webkit syntax
                    checkGradientAtPos(expectedGradient2, 111, 36);   // -webkit- prefix preserved
                    checkGradientAtPos(expectedGradient2, 112, 36);   // -moz- prefix gets stripped
                    checkGradientAtPos(expectedGradient2, 113, 36);   // -ms- prefix gets stripped
                    checkGradientAtPos(expectedGradient2, 114, 36);   // -0- prefix gets stripped
                });
            });

            it("Should show radial gradient preview for those with w3c standard syntax (no prefix)", function () {
                runs(function () {
                    // TODO (#3458): support new W3C syntax
//                    checkGradientAtPos("-webkit-radial-gradient(yellow, green)", 118, 35);
//                    checkGradientAtPos("-webkit-radial-gradient(yellow, green)", 118, 40);

                    // For now the color stops are just previewed in isolation
                    expectNoPreviewAtPos(118, 35);
                    checkColorAtPos("yellow", 118, 40);
                });
            });

            it("Should show repeating linear gradient preview", function () {
                runs(function () {
                    // TODO (#3458): support repeat
//                    checkGradientAtPos("repeating-linear-gradient(red, blue 20px, red 40px)", 122, 50);
//                    checkGradientAtPos("repeating-linear-gradient(red 0px, white 0px, blue 0px)", 123, 50);
//                    checkGradientAtPos("repeating-linear-gradient(red 0px, white .1px, blue .2px)", 124, 50);

                    // For now the color stops are just previewed in isolation
                    expectNoPreviewAtPos(122, 35);
                    expectNoPreviewAtPos(123, 35);
                    expectNoPreviewAtPos(124, 35);
                    checkColorAtPos("red", 122, 50);
                });
            });

            it("Should show repeating radial gradient preview", function () {
                runs(function () {
                    // TODO (#3458): support repeat
//                    checkGradientAtPos("repeating-radial-gradient(circle closest-side at 20px 30px, red, yellow, green 100%, yellow 150%, red 200%)", 128, 40);
//                    checkGradientAtPos("repeating-radial-gradient(red, blue 20px, red 40px)", 129, 40);

                    expectNoPreviewAtPos(128, 40);
                    expectNoPreviewAtPos(129, 40);
                });
            });

        });

        describe("Hover preview display", function () {

            function showPopoverAtPos(line, ch) {
                var popoverInfo = getPopoverAtPos(line, ch);
                HoverPreview._forceShow(popoverInfo);
            }

            function getBounds(object) {
                return {
                    left:   object.offset().left,
                    top:    object.offset().top,
                    right:  object.offset().left + object.width(),
                    bottom: object.offset().top + object.height()
                };
            }

            function boundsInsideWindow(object) {
                var bounds = getBounds(object);
                return bounds.left   >= 0                      &&
                       bounds.right  <= $(testWindow).width()  &&
                       bounds.top    >= 0                      &&
                       bounds.bottom <= $(testWindow).height();
            }

            function toggleOption(commandID, text) {
                runs(function () {
                    var promise = CommandManager.execute(commandID);
                    waitsForDone(promise, text);
                });
            }

            it("popover is positioned within window bounds", function () {
                var $popover  = testWindow.$("#hover-preview-container");
                expect($popover.length).toEqual(1);

                runs(function () {
                    // Popover should be below item
                    showPopoverAtPos(3, 12);
                    expect(boundsInsideWindow($popover)).toBeTruthy();

                    // Popover should above item
                    showPopoverAtPos(20, 33);
                    expect(boundsInsideWindow($popover)).toBeTruthy();
                });

                runs(function () {
                    // Turn off word wrap for next tests
                    toggleOption(Commands.TOGGLE_WORD_WRAP, "Toggle word-wrap");
                });

                runs(function () {

// Issue #3447 - fixes both of the following tests
/*
                    // Popover should be inside right edge
                    showPopoverAtPos(81, 36);
                    expect(boundsInsideWindow($popover)).toBeTruthy();
*/

/*
                    // Popover should be inside left edge
                    var scrollX = editor._codeMirror.defaultCharWidth()  * 120,
                        scrollY = editor._codeMirror.defaultTextHeight() * 190;

                    editor.setScrollPos(scrollX, scrollY);      // Scroll right
                    showPopoverAtPos(82, 136);
                    expect(boundsInsideWindow($popover)).toBeTruthy();
*/

                    // restore word wrap
                    toggleOption(Commands.TOGGLE_WORD_WRAP, "Toggle word-wrap");
                });
            });

            it("highlight matched text when popover shown", function () {
                showPopoverAtPos(4, 14);
                var markers = editor._codeMirror.findMarksAt({line: 4, ch: 14});
                expect(markers.length).toBe(1);
                var range = markers[0].find();
                expect(range.from.ch).toBe(11);
                expect(range.to.ch).toBe(18);
            });

        });

        describe("Hover preview images", function () {
            it("Should show image preview for file path inside url()", function () {
                runs(function () {
                    checkImagePathAtPos("img/grabber_color-well.png", 133, 26);
                    checkImagePathAtPos("img/Color.png",              134, 26);
                    checkImagePathAtPos("img/DancingPeaks.gif",       135, 26);
                    checkImagePathAtPos("img/Example.svg",            136, 26);
                });
            });

            it("Should show image preview for urls with http/https", function () {
                runs(function () {
                    checkImagePathAtPos("https://raw.github.com/gruehle/HoverPreview/master/screenshots/Image.png", 138, 26);
                });
            });

            it("Should show image preview for file path inside single or double quotes", function () {
                runs(function () {
                    checkImagePathAtPos("img/med_hero.jpg",       140, 26);
                    checkImagePathAtPos("img/Gradient.png",       141, 26);
                    checkImagePathAtPos("Lake_mapourika_NZ.jpeg", 142, 26);
                });

                // This must be in the last spec in the suite.
                runs(function () {
                    this.after(function () {
                        SpecRunnerUtils.closeTestWindow();
                    });
                });
            });
        });
    });
});


/*!
 * jQuery UI Effects @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/Effects/
 */
;(jQuery.effects || (function($, undefined) {

var backCompat = $.uiBackCompat !== false,
	// prefix used for storing data on .data()
	dataSpace = "ui-effects-";

$.effects = {
	effect: {}
};

/******************************************************************************/
/****************************** COLOR ANIMATIONS ******************************/
/******************************************************************************/
(function() {

// override the animation for color styles
$.each(["backgroundColor", "borderBottomColor", "borderLeftColor",
	"borderRightColor", "borderTopColor", "borderColor", "color", "outlineColor"],
function(i, attr) {
	$.fx.step[attr] = function(fx) {
		if (!fx.colorInit) {
			fx.start = getColor(fx.elem, attr);
			fx.end = getRGB(fx.end);
			fx.colorInit = true;
		}

		fx.elem.style[attr] = "rgb(" +
			Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + "," +
			Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + "," +
			Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ")";
	};
});

// Color Conversion functions from highlightFade
// By Blair Mitchelmore
// http://jquery.offput.ca/highlightFade/

// Parse strings looking for color tuples [255,255,255]
function getRGB(color) {
		var result;

		// Check if we're already dealing with an array of colors
		if ( color && color.constructor === Array && color.length === 3 ) {
			return color;
		}

		// Look for rgb(num,num,num)
		if ( (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) ) {
			return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
		}

		// Look for rgb(num%,num%,num%)
		if ( (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) ) {
			return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
		}

		// Look for #a0b1c2
		if ( (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) ) {
			return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
		}

		// Look for #fff
		if ( (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) ) {
			return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
		}

		// Look for rgba(0, 0, 0, 0) == transparent in Safari 3
		if ( (result = /rgba\(0, 0, 0, 0\)/.exec(color)) ) {
			return colors.transparent;
		}

		// Otherwise, we're most likely dealing with a named color
		return colors[$.trim(color).toLowerCase()];
}

function getColor(elem, attr) {
		var color;

		do {
				color = $.css(elem, attr);

				// Keep going until we find an element that has color, or we hit the body
				if ( color && color !== "transparent" || $.nodeName(elem, "body") ) {
					break;
				}

				attr = "backgroundColor";
		} while ( (elem = elem.parentNode) );

		return getRGB(color);
}

// Some named colors to work with
// From Interface by Stefan Petre
// http://interface.eyecon.ro/

var colors = {
	aqua:[0,255,255],
	azure:[240,255,255],
	beige:[245,245,220],
	black:[0,0,0],
	blue:[0,0,255],
	brown:[165,42,42],
	cyan:[0,255,255],
	darkblue:[0,0,139],
	darkcyan:[0,139,139],
	darkgrey:[169,169,169],
	darkgreen:[0,100,0],
	darkkhaki:[189,183,107],
	darkmagenta:[139,0,139],
	darkolivegreen:[85,107,47],
	darkorange:[255,140,0],
	darkorchid:[153,50,204],
	darkred:[139,0,0],
	darksalmon:[233,150,122],
	darkviolet:[148,0,211],
	fuchsia:[255,0,255],
	gold:[255,215,0],
	green:[0,128,0],
	indigo:[75,0,130],
	khaki:[240,230,140],
	lightblue:[173,216,230],
	lightcyan:[224,255,255],
	lightgreen:[144,238,144],
	lightgrey:[211,211,211],
	lightpink:[255,182,193],
	lightyellow:[255,255,224],
	lime:[0,255,0],
	magenta:[255,0,255],
	maroon:[128,0,0],
	navy:[0,0,128],
	olive:[128,128,0],
	orange:[255,165,0],
	pink:[255,192,203],
	purple:[128,0,128],
	violet:[128,0,128],
	red:[255,0,0],
	silver:[192,192,192],
	white:[255,255,255],
	yellow:[255,255,0],
	transparent: [255,255,255]
};

})();

/******************************************************************************/
/****************************** CLASS ANIMATIONS ******************************/
/******************************************************************************/
(function() {

var classAnimationActions = [ "add", "remove", "toggle" ],
	shorthandStyles = {
		border: 1,
		borderBottom: 1,
		borderColor: 1,
		borderLeft: 1,
		borderRight: 1,
		borderTop: 1,
		borderWidth: 1,
		margin: 1,
		padding: 1
	};

$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
	$.fx.step[ prop ] = function( fx ) {
		if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
			jQuery.style( fx.elem, prop, fx.end );
			fx.setAttr = true;
		}
	};
});

function getElementStyles() {
	var style = this.ownerDocument.defaultView ?
			this.ownerDocument.defaultView.getComputedStyle( this, null ) :
			this.currentStyle,
		newStyle = {},
		key,
		camelCase,
		len;

	// webkit enumerates style porperties
	if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
		len = style.length;
		while ( len-- ) {
			key = style[ len ];
			if ( typeof style[ key ] === "string" ) {
				newStyle[ $.camelCase( key ) ] = style[ key ];
			}
		}
	} else {
		for ( key in style ) {
			if ( typeof style[ key ] === "string" ) {
				newStyle[ key ] = style[ key ];
			}
		}
	}

	return newStyle;
}

function styleDifference( oldStyle, newStyle ) {
	var diff = {},
		name, value;

	for ( name in newStyle ) {
		value = newStyle[ name ];
		if ( oldStyle[ name ] !== value ) {
			if ( !shorthandStyles[ name ] ) {
				if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
					diff[ name ] = value;
				}
			}
		}
	}

	return diff;
}

$.effects.animateClass = function( value, duration, easing, callback ) {
	var o = $.speed( duration, easing, callback );

	return this.queue( function() {
		var animated = $( this ),
			baseClass = animated.attr( "class" ) || "",
			applyClassChange,
			allAnimations = o.children ? animated.find( "*" ).andSelf() : animated;

		// map the animated objects to store the original styles.
		allAnimations = allAnimations.map(function() {
			var el = $( this );
			return {
				el: el,
				start: getElementStyles.call( this )
			};
		});

		// apply class change
		applyClassChange = function() {
			$.each( classAnimationActions, function(i, action) {
				if ( value[ action ] ) {
					animated[ action + "Class" ]( value[ action ] );
				}
			});
		};
		applyClassChange();

		// map all animated objects again - calculate new styles and diff
		allAnimations = allAnimations.map(function() {
			this.end = getElementStyles.call( this.el[ 0 ] );
			this.diff = styleDifference( this.start, this.end );
			return this;
		});

		// apply original class
		animated.attr( "class", baseClass );

		// map all animated objects again - this time collecting a promise
		allAnimations = allAnimations.map(function() {
			var styleInfo = this,
				dfd = $.Deferred(),
				opts = jQuery.extend({}, o, {
					queue: false,
					complete: function() {
						dfd.resolve( styleInfo );
					}
				});

			this.el.animate( this.diff, opts );
			return dfd.promise();
		});

		// once all animations have completed:
		$.when.apply( $, allAnimations.get() ).done(function() {

			// set the final class
			applyClassChange();

			// for each animated element,
			// clear all css properties that were animated
			$.each( arguments, function() {
				var el = this.el;
				$.each( this.diff, function(key) {
					el.css( key, '' );
				});
			});

			// this is guarnteed to be there if you use jQuery.speed()
			// it also handles dequeuing the next anim...
			o.complete.call( animated[ 0 ] );
		});
	});
};

$.fn.extend({
	_addClass: $.fn.addClass,
	addClass: function( classNames, speed, easing, callback ) {
		return speed ?
			$.effects.animateClass.apply( this, [{ add: classNames }, speed, easing, callback ]) :
			this._addClass(classNames);
	},

	_removeClass: $.fn.removeClass,
	removeClass: function( classNames, speed, easing, callback ) {
		return speed ?
			$.effects.animateClass.apply( this, [{ remove: classNames }, speed, easing, callback ]) :
			this._removeClass(classNames);
	},

	_toggleClass: $.fn.toggleClass,
	toggleClass: function( classNames, force, speed, easing, callback ) {
		if ( typeof force === "boolean" || force === undefined ) {
			if ( !speed ) {
				// without speed parameter;
				return this._toggleClass( classNames, force );
			} else {
				return $.effects.animateClass.apply( this, [( force ? { add:classNames } : { remove:classNames }), speed, easing, callback ]);
			}
		} else {
			// without force parameter;
			return $.effects.animateClass.apply( this, [{ toggle: classNames }, force, speed, easing ]);
		}
	},

	switchClass: function( remove, add, speed, easing, callback) {
		return $.effects.animateClass.apply( this, [{
				add: add,
				remove: remove
			}, speed, easing, callback ]);
	}
});

})();

/******************************************************************************/
/*********************************** EFFECTS **********************************/
/******************************************************************************/

(function() {

$.extend( $.effects, {
	version: "@VERSION",

	// Saves a set of properties in a data storage
	save: function( element, set ) {
		for( var i=0; i < set.length; i++ ) {
			if ( set[ i ] !== null ) {
				element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
			}
		}
	},

	// Restores a set of previously saved properties from a data storage
	restore: function( element, set ) {
		for( var i=0; i < set.length; i++ ) {
			if ( set[ i ] !== null ) {
				element.css( set[ i ], element.data( dataSpace + set[ i ] ) );
			}
		}
	},

	setMode: function( el, mode ) {
		if (mode === "toggle") {
			mode = el.is( ":hidden" ) ? "show" : "hide";
		}
		return mode;
	},

	// Translates a [top,left] array into a baseline value
	// this should be a little more flexible in the future to handle a string & hash
	getBaseline: function( origin, original ) {
		var y, x;
		switch ( origin[ 0 ] ) {
			case "top": y = 0; break;
			case "middle": y = 0.5; break;
			case "bottom": y = 1; break;
			default: y = origin[ 0 ] / original.height;
		}
		switch ( origin[ 1 ] ) {
			case "left": x = 0; break;
			case "center": x = 0.5; break;
			case "right": x = 1; break;
			default: x = origin[ 1 ] / original.width;
		}
		return {
			x: x,
			y: y
		};
	},

	// Wraps the element around a wrapper that copies position properties
	createWrapper: function( element ) {

		// if the element is already wrapped, return it
		if ( element.parent().is( ".ui-effects-wrapper" )) {
			return element.parent();
		}

		// wrap the element
		var props = {
				width: element.outerWidth(true),
				height: element.outerHeight(true),
				"float": element.css( "float" )
			},
			wrapper = $( "

" )
				.addClass( "ui-effects-wrapper" )
				.css({
					fontSize: "100%",
					background: "transparent",
					border: "none",
					margin: 0,
					padding: 0
				}),
			// Store the size in case width/height are defined in % - Fixes #5245
			size = {
				width: element.width(),
				height: element.height()
			},
			active = document.activeElement;

		// support: Firefox
		// Firefox incorrectly exposes anonymous content
		// https://bugzilla.mozilla.org/show_bug.cgi?id=561664
		try {
			active.id;
		} catch( e ) {
			active = document.body;
		}

		element.wrap( wrapper );

		// Fixes #7595 - Elements lose focus when wrapped.
		if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
			$( active ).focus();
		}

		wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element

		// transfer positioning properties to the wrapper
		if ( element.css( "position" ) === "static" ) {
			wrapper.css({ position: "relative" });
			element.css({ position: "relative" });
		} else {
			$.extend( props, {
				position: element.css( "position" ),
				zIndex: element.css( "z-index" )
			});
			$.each([ "top", "left", "bottom", "right" ], function(i, pos) {
				props[ pos ] = element.css( pos );
				if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
					props[ pos ] = "auto";
				}
			});
			element.css({
				position: "relative",
				top: 0,
				left: 0,
				right: "auto",
				bottom: "auto"
			});
		}
		element.css(size);

		return wrapper.css( props ).show();
	},

	removeWrapper: function( element ) {
		var active = document.activeElement;

		if ( element.parent().is( ".ui-effects-wrapper" ) ) {
			element.parent().replaceWith( element );

			// Fixes #7595 - Elements lose focus when wrapped.
			if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
				$( active ).focus();
			}
		}

		return element;
	},

	setTransition: function( element, list, factor, value ) {
		value = value || {};
		$.each( list, function( i, x ) {
			var unit = element.cssUnit( x );
			if ( unit[ 0 ] > 0 ) {
				value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
			}
		});
		return value;
	}
});

// return an effect options object for the given parameters:
function _normalizeArguments( effect, options, speed, callback ) {

	// allow passing all optinos as the first parameter
	if ( $.isPlainObject( effect ) ) {
		options = effect;
		effect = effect.effect;
	}

	// convert to an object
	effect = { effect: effect };

	// catch (effect)
	if ( options === undefined ) {
		options = {};
	}

	// catch (effect, callback)
	if ( $.isFunction( options ) ) {
		callback = options;
		speed = null;
		options = {};
	}

	// catch (effect, speed, ?)
	if ( typeof options === "number" || $.fx.speeds[ options ] ) {
		callback = speed;
		speed = options;
		options = {};
	}

	// catch (effect, options, callback)
	if ( $.isFunction( speed ) ) {
		callback = speed;
		speed = null;
	}

	// add options to effect
	if ( options ) {
		$.extend( effect, options );
	}

	speed = speed || options.duration;
	effect.duration = $.fx.off ? 0 :
		typeof speed === "number" ? speed :
		speed in $.fx.speeds ? $.fx.speeds[ speed ] :
		$.fx.speeds._default;

	effect.complete = callback || options.complete;

	return effect;
}

function standardSpeed( speed ) {
	// valid standard speeds
	if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
		return true;
	}

	// invalid strings - treat as "normal" speed
	if ( typeof speed === "string" && !$.effects.effect[ speed ] ) {
		// TODO: remove in 2.0 (#7115)
		if ( backCompat && $.effects[ speed ] ) {
			return false;
		}
		return true;
	}

	return false;
}

$.fn.extend({
	effect: function( effect, options, speed, callback ) {
		var args = _normalizeArguments.apply( this, arguments ),
			mode = args.mode,
			queue = args.queue,
			effectMethod = $.effects.effect[ args.effect ],

			// DEPRECATED: remove in 2.0 (#7115)
			oldEffectMethod = !effectMethod && backCompat && $.effects[ args.effect ];

		if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) {
			// delegate to the original method (e.g., .show()) if possible
			if ( mode ) {
				return this[ mode ]( args.duration, args.complete );
			} else {
				return this.each( function() {
					if ( args.complete ) {
						args.complete.call( this );
					}
				});
			}
		}

		function run( next ) {
			var elem = $( this ),
				complete = args.complete,
				mode = args.mode;

			function done() {
				if ( $.isFunction( complete ) ) {
					complete.call( elem[0] );
				}
				if ( $.isFunction( next ) ) {
					next();
				}
			}

			// if the element is hiddden and mode is hide,
			// or element is visible and mode is show
			if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
				done();
			} else {
				effectMethod.call( elem[0], args, done );
			}
		}

		// TODO: remove this check in 2.0, effectMethod will always be true
		if ( effectMethod ) {
			return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
		} else {
			// DEPRECATED: remove in 2.0 (#7115)
			return oldEffectMethod.call(this, {
				options: args,
				duration: args.duration,
				callback: args.complete,
				mode: args.mode
			});
		}
	},

	_show: $.fn.show,
	show: function( speed ) {
		if ( standardSpeed( speed ) ) {
			return this._show.apply( this, arguments );
		} else {
			var args = _normalizeArguments.apply( this, arguments );
			args.mode = "show";
			return this.effect.call( this, args );
		}
	},

	_hide: $.fn.hide,
	hide: function( speed ) {
		if ( standardSpeed( speed ) ) {
			return this._hide.apply( this, arguments );
		} else {
			var args = _normalizeArguments.apply( this, arguments );
			args.mode = "hide";
			return this.effect.call( this, args );
		}
	},

	// jQuery core overloads toggle and creates _toggle
	__toggle: $.fn.toggle,
	toggle: function( speed ) {
		if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
			return this.__toggle.apply( this, arguments );
		} else {
			var args = _normalizeArguments.apply( this, arguments );
			args.mode = "toggle";
			return this.effect.call( this, args );
		}
	},

	// helper functions
	cssUnit: function(key) {
		var style = this.css( key ),
			val = [];

		$.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
			if ( style.indexOf( unit ) > 0 ) {
				val = [ parseFloat( style ), unit ];
			}
		});
		return val;
	}
});

})();

/******************************************************************************/
/*********************************** EASING ***********************************/
/******************************************************************************/

(function() {

// based on easing equations from Robert Penner (http://www.robertpenner.com/easing)

var baseEasings = {};

$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
	baseEasings[ name ] = function( p ) {
		return Math.pow( p, i + 2 );
	};
});

$.extend( baseEasings, {
	Sine: function ( p ) {
		return 1 - Math.cos( p * Math.PI / 2 );
	},
	Circ: function ( p ) {
		return 1 - Math.sqrt( 1 - p * p );
	},
	Elastic: function( p ) {
		return p === 0 || p === 1 ? p :
			-Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
	},
	Back: function( p ) {
		return p * p * ( 3 * p - 2 );
	},
	Bounce: function ( p ) {
		var pow2,
			bounce = 4;

		while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
		return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
	}
});

$.each( baseEasings, function( name, easeIn ) {
	$.easing[ "easeIn" + name ] = easeIn;
	$.easing[ "easeOut" + name ] = function( p ) {
		return 1 - easeIn( 1 - p );
	};
	$.easing[ "easeInOut" + name ] = function( p ) {
		return p < 0.5 ?
			easeIn( p * 2 ) / 2 :
			1 - easeIn( p * -2 + 2 ) / 2;
	};
});

})();

})(jQuery));


#!/bin/bash
set -e
for i in test-*.js; do
  echo -n $i ...
  bash setup.sh
  node $i
  ! [ -d target ]
  echo "pass"
done
rm -rf target


<script>alert('PWNED!')</script>Code Completion for HTML Attributes

<script>alert('SECURITY BREACH!')</script>Basic code hinting for HTML attribute names. Appears when you type Space within a tag (or press Ctrl+Space).

CEF3 Shell Enhancements

Add support for Live Development and fix a bunch of bugs. All unit tests now pass in the CEF3 shell.

Other Enhancements

Add Move Line(s) Up/Down command. Add Save All command.

Code Completion for HTML Tags

Basic code hinting for HTML tag names. Appears when you type "<" or Ctrl+Space.

CEF3 Shell Enhancements

Fix bugs and get most unit tests passing in the experimental new shell, based on Chromium Embedding Framework 3 (Brackets currently uses CEF 1).

Performance Improvements

Improve performance when switching projects.

JavaScript Quick Edit

Hitting Cmd/Ctrl-E on a function name opens all function definitions with that name in an inline editor.

HTML Context Menus

This extends the previous menu API to support context menus.

Extension Stylesheets

Extensions may now use the ExtensionUtils.loadStyleSheet() API to load a custom stylesheet for extension UI.


describe("Hover preview gradients", function () {

it("Should show linear gradient preview for those with vendor prefix", function () {

var expectedGradient1 = "-webkit-linear-gradient(top,  #d2dfed 0%, #c8d7eb 26%, #bed0ea 51%, #a6c0e3 51%, #afc7e8 62%, #bad0ef 75%, #99b5db 88%, #799bc8 100%)",

expectedGradient2 = "-webkit-gradient(linear, left top, left bottom, color-stop(0%,#d2dfed), color-stop(26%,#c8d7eb), color-stop(51%,#bed0ea), color-stop(51%,#a6c0e3), color-stop(62%,#afc7e8), color-stop(75%,#bad0ef), color-stop(88%,#99b5db), color-stop(100%,#799bc8));",

expectedGradient3 = "-webkit-linear-gradient(top,  #d2dfed 0%,#c8d7eb 26%,#bed0ea 51%,#a6c0e3 51%,#afc7e8 62%,#bad0ef 75%,#99b5db 88%,#799bc8 100%)";

checkGradientAtPos(expectedGradient1, 80, 36);   // -moz- prefix gets stripped

checkGradientAtPos(expectedGradient2, 81, 36);   // Old webkit syntax

checkGradientAtPos(expectedGradient3, 82, 36);   // -webkit- prefix gets stripped

checkGradientAtPos(expectedGradient3, 83, 36);   // -o- prefix gets stripped

keep keep keep keep keep replace keep keep keep keep keep

expectedGradient2 = "-webkit-gradient(linear, left top, left bottom, color-stop(0%,#d2dfed), color-stop(26%,#c8d7eb), color-stop(51%,#bed0ea), color-stop(51%,#a6c0e3), color-stop(62%,#afc7e8), color-stop(75%,#bad0ef), color-stop(88%,#99b5db), color-stop(100%,#799bc8))",

/*
 * 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,  regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, $, window, PathUtils, CodeMirror */

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

    // Brackets modules
    var AppInit             = brackets.getModule("utils/AppInit"),
        CommandManager      = brackets.getModule("command/CommandManager"),
        EditorManager       = brackets.getModule("editor/EditorManager"),
        ExtensionUtils      = brackets.getModule("utils/ExtensionUtils"),
        Menus               = brackets.getModule("command/Menus"),
        PreferencesManager  = brackets.getModule("preferences/PreferencesManager"),
        Strings             = brackets.getModule("strings");

    var previewContainerHTML       = require("text!HoverPreviewTemplate.html");

    var defaultPrefs               = { enabled: true },
        enabled,                             // Only show preview if true
        prefs                      = null,   // Preferences
        $previewContainer,                   // Preview container
        $previewContent,                     // Preview content holder
        lastPos;                             // Last line/ch pos processed by handleMouseMove

    // Constants
    var CMD_ENABLE_HOVER_PREVIEW    = "view.enableHoverPreview",
        HOVER_DELAY                 = 350,  // Time (ms) mouse must remain over a provider's matched text before popover appears
        POSITION_OFFSET             = 38,   // Distance between the bottom of the line and the bottom of the preview container
        POINTER_LEFT_OFFSET         = 17,   // Half of the pointer width, used to find the center of the pointer
        POINTER_TOP_OFFSET          =  7,   // Pointer height, used to shift popover above pointer
        POSITION_BELOW_OFFSET       = 16;   // Amount to adjust to top position when the preview bubble is below the text

    /**
     * There are three states for this var:
     * 1. If null, there is no provider result for the given mouse position.
     * 2. If non-null, and visible==true, there is a popover currently showing.
     * 3. If non-null, but visible==false, there is a provider result but it has not been shown yet because
     * we're waiting for HOVER_DELAY, which is tracked by hoverTimer. The state changes to visible==true as
     * soon as hoverTimer fires. If the mouse moves before then, the popover will never become visible.
     *
     * @type {{
     *      visible: boolean,
     *      editor: !Editor,
     *      hoverTimer: number,             - setTimeout() token
     *      start: !{line, ch},             - start of matched text range
     *      end: !{line, ch},               - end of matched text range
     *      content: !string,               - HTML content to display in popover
     *      onShow: ?function():void,       - called once popover content added to the DOM (may never be called)
     *      xpos: number,                   - x of center of popover
     *      ytop: number,                   - y of top of matched text (when popover placed above text, normally)
     *      ybot: number,                   - y of bottom of matched text (when popover moved below text, avoiding window top)
     *      marker: ?CodeMirror.TextMarker  - only set once visible==true
     * }}
     */
    var popoverState = null;

    // Popover widget management ----------------------------------------------

    /**
     * Cancels whatever popoverState was currently pending and sets it back to null. If the popover was visible,
     * hides it; if the popover was invisible and still pending, cancels hoverTimer so it will never be shown.
     */
    function hidePreview() {
        if (!popoverState) {
            return;
        }

        if (popoverState.visible) {
            popoverState.marker.clear();

            $previewContent.empty();
            $previewContainer.hide();

        } else {
            window.clearTimeout(popoverState.hoverTimer);
        }

        popoverState = null;
    }

    function positionPreview(xpos, ytop, ybot) {
        var top = ytop - $previewContainer.height() - POSITION_OFFSET;

        if (top < 0) {
            $previewContainer.removeClass("preview-bubble-above");
            $previewContainer.addClass("preview-bubble-below");
            top = ybot + POSITION_BELOW_OFFSET;
            $previewContainer.offset({
                left: xpos - $previewContainer.width() / 2 - POINTER_LEFT_OFFSET,
                top: top
            });
        } else {
            $previewContainer.removeClass("preview-bubble-below");
            $previewContainer.addClass("preview-bubble-above");
            $previewContainer.offset({
                left: xpos - $previewContainer.width() / 2 - POINTER_LEFT_OFFSET,
                top: top - POINTER_TOP_OFFSET
            });
        }
    }

    /**
     * Changes the current hidden popoverState to visible, showing it in the UI and highlighting
     * its matching text in the editor.
     */
    function showPreview() {

        var cm = popoverState.editor._codeMirror;
        popoverState.marker = cm.markText(
            popoverState.start,
            popoverState.end,
            {className: "hover-preview-highlight"}
        );

        $previewContent.append(popoverState.content);
        $previewContainer.show();

        popoverState.visible = true;

        if (popoverState.onShow) {
            popoverState.onShow();
        }

        positionPreview(popoverState.xpos, popoverState.ytop, popoverState.ybot);
    }

    function divContainsMouse($div, event) {
        var offset = $div.offset();

        return (event.clientX >= offset.left &&
                event.clientX <= offset.left + $div.width() &&
                event.clientY >= offset.top &&
                event.clientY <= offset.top + $div.height());
    }

    // Color & gradient preview provider --------------------------------------

    function colorAndGradientPreviewProvider(editor, pos, token, line) {
        var cm = editor._codeMirror;

        // Check for gradient. -webkit-gradient() can have parens in parameters
        // nested 2 levels. Other gradients can only nest 1 level.
        var gradientRegEx1 = /-webkit-gradient\((?:[^\(]*?(?:\((?:[^\(]*?(?:\([^\)]*?\))*?)*?\))*?)*?\)/,
            gradientRegEx2 = /(?:(?:-moz-|-ms-|-o-|-webkit-|)(linear-gradient)|(?:-moz-|-ms-|-o-|-webkit-)(radial-gradient))(\((?:[^\)]*?(?:\([^\)]*?\))*?)*?\))/,

            gradientMatch = line.match(gradientRegEx1) || line.match(gradientRegEx2),
            prefix = "",
            colorValue;

        if (gradientMatch) {
            if (gradientMatch[0].indexOf("@") !== -1) {
                // If the gradient match has "@" in it, it is most likely a less or
                // sass variable. Ignore it since it won't be displayed correctly.
                gradientMatch = null;

            } else if (gradientMatch[0].indexOf("to ") !== -1) {
                // If the gradient match has "to " in it, it's most likely the new gradient
                // syntax which is not supported until Chrome 26, so we can't yet preview it
                gradientMatch = null;
            }
        }

        if (gradientMatch) {
            // If it was a linear-gradient or radial-gradient variant, prefix with
            // "-webkit-" so it shows up correctly in Brackets.
            if (gradientMatch[0].indexOf("-webkit-gradient") !== 0) {
                prefix = "-webkit-";
            }

            // For prefixed gradients, use the non-prefixed value as the color value.
            // "-webkit-" will be added before this value
            if (gradientMatch[1]) {
                colorValue = gradientMatch[1] + gradientMatch[3];    // linear gradiant
            } else if (gradientMatch[2]) {
                colorValue = gradientMatch[2] + gradientMatch[3];    // radial gradiant
            }
        }

        // Check for color
        var colorRegEx = /#[a-f0-9]{6}\b|#[a-f0-9]{3}\b|rgb\( ?\b([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b ?, ?\b([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b ?, ?\b([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b ?\)|rgb\( ?\b([0-9]{1,2}%|100%) ?, ?\b([0-9]{1,2}%|100%) ?, ?\b([0-9]{1,2}%|100%) ?\)|rgba\( ?\b([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b ?, ?\b([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b ?, ?\b([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b ?, ?(1|0|0?\.[0-9]{1,3}) ?\)|rgba\( ?\b([0-9]{1,2}%|100%) ?, ?\b([0-9]{1,2}%|100%) ?, ?\b([0-9]{1,2}%|100%) ?, ?(1|0|0?\.[0-9]{1,3}) ?\)|hsl\( ?\b([0-9]{1,2}|[12][0-9]{2}|3[0-5][0-9]|360)\b ?, ?\b([0-9]{1,2}|100)\b% ?, ?\b([0-9]{1,2}|100)\b% ?\)|hsla\( ?\b([0-9]{1,2}|[12][0-9]{2}|3[0-5][0-9]|360)\b ?, ?\b([0-9]{1,2}|100)\b% ?, ?\b([0-9]{1,2}|100)\b% ?, ?(1|0|0?\.[0-9]{1,3}) ?\)|\baliceblue\b|\bantiquewhite\b|\baqua\b|\baquamarine\b|\bazure\b|\bbeige\b|\bbisque\b|\bblack\b|\bblanchedalmond\b|\bblue\b|\bblueviolet\b|\bbrown\b|\bburlywood\b|\bcadetblue\b|\bchartreuse\b|\bchocolate\b|\bcoral\b|\bcornflowerblue\b|\bcornsilk\b|\bcrimson\b|\bcyan\b|\bdarkblue\b|\bdarkcyan\b|\bdarkgoldenrod\b|\bdarkgray\b|\bdarkgreen\b|\bdarkgrey\b|\bdarkkhaki\b|\bdarkmagenta\b|\bdarkolivegreen\b|\bdarkorange\b|\bdarkorchid\b|\bdarkred\b|\bdarksalmon\b|\bdarkseagreen\b|\bdarkslateblue\b|\bdarkslategray\b|\bdarkslategrey\b|\bdarkturquoise\b|\bdarkviolet\b|\bdeeppink\b|\bdeepskyblue\b|\bdimgray\b|\bdimgrey\b|\bdodgerblue\b|\bfirebrick\b|\bfloralwhite\b|\bforestgreen\b|\bfuchsia\b|\bgainsboro\b|\bghostwhite\b|\bgold\b|\bgoldenrod\b|\bgray\b|\bgreen\b|\bgreenyellow\b|\bgrey\b|\bhoneydew\b|\bhotpink\b|\bindianred\b|\bindigo\b|\bivory\b|\bkhaki\b|\blavender\b|\blavenderblush\b|\blawngreen\b|\blemonchiffon\b|\blightblue\b|\blightcoral\b|\blightcyan\b|\blightgoldenrodyellow\b|\blightgray\b|\blightgreen\b|\blightgrey\b|\blightpink\b|\blightsalmon\b|\blightseagreen\b|\blightskyblue\b|\blightslategray\b|\blightslategrey\b|\blightsteelblue\b|\blightyellow\b|\blime\b|\blimegreen\b|\blinen\b|\bmagenta\b|\bmaroon\b|\bmediumaquamarine\b|\bmediumblue\b|\bmediumorchid\b|\bmediumpurple\b|\bmediumseagreen\b|\bmediumslateblue\b|\bmediumspringgreen\b|\bmediumturquoise\b|\bmediumvioletred\b|\bmidnightblue\b|\bmintcream\b|\bmistyrose\b|\bmoccasin\b|\bnavajowhite\b|\bnavy\b|\boldlace\b|\bolive\b|\bolivedrab\b|\borange\b|\borangered\b|\borchid\b|\bpalegoldenrod\b|\bpalegreen\b|\bpaleturquoise\b|\bpalevioletred\b|\bpapayawhip\b|\bpeachpuff\b|\bperu\b|\bpink\b|\bplum\b|\bpowderblue\b|\bpurple\b|\bred\b|\brosybrown\b|\broyalblue\b|\bsaddlebrown\b|\bsalmon\b|\bsandybrown\b|\bseagreen\b|\bseashell\b|\bsienna\b|\bsilver\b|\bskyblue\b|\bslateblue\b|\bslategray\b|\bslategrey\b|\bsnow\b|\bspringgreen\b|\bsteelblue\b|\btan\b|\bteal\b|\bthistle\b|\btomato\b|\bturquoise\b|\bviolet\b|\bwheat\b|\bwhite\b|\bwhitesmoke\b|\byellow\b|\byellowgreen\b/gi,
            colorMatch = colorRegEx.exec(line),
            match = gradientMatch || colorMatch;

        while (match) {
            if (pos.ch >= match.index && pos.ch <= match.index + match[0].length) {
                var previewCSS = prefix + (colorValue || match[0]);
                var preview = "
" +
                              "
";
                var startPos = {line: pos.line, ch: match.index},
                    endPos = {line: pos.line, ch: match.index + match[0].length},
                    startCoords = cm.charCoords(startPos),
                    xPos;

                xPos = (cm.charCoords(endPos).left - startCoords.left) / 2 + startCoords.left;

                return {
                    start: startPos,
                    end: endPos,
                    content: preview,
                    xpos: xPos,
                    ytop: startCoords.top,
                    ybot: startCoords.bottom,
                    _previewCSS: previewCSS
                };
            }
            match = colorRegEx.exec(line);
        }

        return null;
    }

    // Image preview provider -------------------------------------------------

    function imagePreviewProvider(editor, pos, token, line) {
        var cm = editor._codeMirror;

        // Check for image name
        var urlRegEx = /url\(([^\)]*)\)/,
            tokenString,
            urlMatch = line.match(urlRegEx);

        if (urlMatch && pos.ch >= urlMatch.index && pos.ch <= urlMatch.index + urlMatch[0].length) {
            tokenString = urlMatch[1];
        } else if (token.className === "string") {
            tokenString = token.string;
        }

        if (tokenString) {
            // Strip quotes, if present
            var quotesRegEx = /(\'|\")?([^(\'|\")]*)(\'|\")?/;
            tokenString = tokenString.replace(quotesRegEx, "$2");

            if (/(\.gif|\.png|\.jpg|\.jpeg|\.svg)$/i.test(tokenString)) {
                var sPos, ePos;
                var docPath = editor.document.file.fullPath;
                var imgPath;

                if (PathUtils.isAbsoluteUrl(tokenString)) {
                    imgPath = tokenString;
                } else {
                    imgPath = "file:///" + docPath.substr(0, docPath.lastIndexOf("/") + 1) + tokenString;
                }

                if (urlMatch) {
                    sPos = {line: pos.line, ch: urlMatch.index};
                    ePos = {line: pos.line, ch: urlMatch.index + urlMatch[0].length};
                } else {
                    sPos = {line: pos.line, ch: token.start};
                    ePos = {line: pos.line, ch: token.end};
                }

                if (imgPath) {
                    var imgPreview = "
"          +
                                     "    "    +
                                     "
";
                    var coord = cm.charCoords(sPos);
                    var xpos = (cm.charCoords(ePos).left - coord.left) / 2 + coord.left;

                    var showHandler = function () {
                        // Hide the preview container until the image is loaded.
                        $previewContainer.hide();

                        $previewContainer.find(".image-preview > img").on("load", function () {
                            $previewContent
                                .append("
"                                            +
                                            this.naturalWidth + " x " + this.naturalHeight + " pixels"  +
                                        "
"
                                    );
                            $previewContainer.show();
                            positionPreview(popoverState.xpos, popoverState.ytop, popoverState.ybot);
                        });
                    };

                    return {
                        start: sPos,
                        end: ePos,
                        content: imgPreview,
                        onShow: showHandler,
                        xpos: xpos,
                        ytop: coord.top,
                        ybot: coord.bottom,
                        _imgPath: imgPath
                    };
                }
            }
        }

        return null;
    }

    // Preview hide/show logic ------------------------------------------------

    /**
     * Returns a 'ready for use' popover state object:
     * { visible: false, editor, start, end, content, ?onShow, xpos, ytop, ybot }
     * Lacks only hoverTimer (supplied by handleMouseMove()) and marker (supplied by showPreview()).
     */
    function queryPreviewProviders(editor, pos, token) {

        var line = editor.document.getLine(pos.line);

        // FUTURE: Support plugin providers. For now we just hard-code...
        var popover = colorAndGradientPreviewProvider(editor, pos, token, line) ||
                      imagePreviewProvider(editor, pos, token, line);

        if (popover) {
            // Providers return just { start, end, content, ?onShow, xpos, ytop, ybot }
            $.extend(popover, { visible: false, editor: editor });

            return popover;
        }
        return null;
    }

    function handleMouseMove(event) {
        if (!enabled) {
            return;
        }

        if (event.which) {
            // Button is down - don't show popovers while dragging
            hidePreview();
            return;
        }

        // Figure out which editor we are over
        var fullEditor = EditorManager.getCurrentFullEditor();

        if (!fullEditor) {
            hidePreview();
            return;
        }

        // Check for inline Editor instances first
        var inlines = fullEditor.getInlineWidgets(),
            i,
            editor;

        for (i = 0; i < inlines.length; i++) {
            var $inlineDiv = inlines[i].$editorsDiv;  // see MultiRangeInlineEditor

            if ($inlineDiv && divContainsMouse($inlineDiv, event)) {
                editor = inlines[i].editors[0];
                break;
            }
        }

        // Check main editor
        if (!editor) {
            if (divContainsMouse($(fullEditor.getRootElement()), event)) {
                editor = fullEditor;
            }
        }

        if (editor && editor._codeMirror) {
            // Find char mouse is over
            var cm = editor._codeMirror;
            var pos = cm.coordsChar({left: event.clientX, top: event.clientY});

            if (lastPos && lastPos.line === pos.line && lastPos.ch === pos.ch) {
                return;  // bail if mouse is on same char as last event
            }
            lastPos = pos;

            var showImmediately = false;

            // Is there a popover already visible or pending?
            if (popoverState) {
                if (editor.posWithinRange(pos, popoverState.start, popoverState.end)) {
                    // That one's still relevant - nothing more to do
                    return;
                } else {
                    // That one doesn't cover this pos - hide it and query providers anew
                    showImmediately = popoverState.visible;
                    hidePreview();
                }
            }

            // Query providers for a new popoverState
            var token = cm.getTokenAt(pos);
            popoverState = queryPreviewProviders(editor, pos, token);

            if (popoverState) {
                // We have a popover available - wait until we're ready to show it
                if (showImmediately) {
                    showPreview();
                } else {
                    popoverState.hoverTimer = window.setTimeout(function () {
                        // Ready to show now (we'll never get here if mouse movement rendered this popover
                        // inapplicable first - hidePopover() cancels hoverTimer)
                        showPreview();
                    }, HOVER_DELAY);
                }
            }

        } else {
            // Mouse not over any Editor - immediately hide popover
            hidePreview();
        }
    }

    // Menu command handlers
    function updateMenuItemCheckmark() {
        CommandManager.get(CMD_ENABLE_HOVER_PREVIEW).setChecked(enabled);
    }

    function setEnabled(_enabled) {
        if (enabled !== _enabled) {
            enabled = _enabled;
            var editorHolder = $("#editor-holder")[0];
            if (enabled) {
                // Note: listening to "scroll" also catches text edits, which bubble a scroll event up from the hidden text area. This means
                // we auto-hide on text edit, which is probably actually a good thing.
                editorHolder.addEventListener("mousemove", handleMouseMove, true);
                editorHolder.addEventListener("scroll", hidePreview, true);
            } else {
                editorHolder.removeEventListener("mousemove", handleMouseMove, true);
                editorHolder.removeEventListener("scroll", hidePreview, true);
                hidePreview();
            }
            prefs.setValue("enabled", enabled);
        }
        // Always update the checkmark, even if the enabled flag hasn't changed.
        updateMenuItemCheckmark();
    }

    function toggleEnableHoverPreview() {
        setEnabled(!enabled);
    }

    // Create the preview container
    $previewContainer = $(previewContainerHTML).appendTo($("body"));
    $previewContent = $previewContainer.find(".preview-content");

    // Load our stylesheet
    ExtensionUtils.loadStyleSheet(module, "HoverPreview.css");

    // Register command
    CommandManager.register(Strings.CMD_ENABLE_HOVER_PREVIEW, CMD_ENABLE_HOVER_PREVIEW, toggleEnableHoverPreview);
    Menus.getMenu(Menus.AppMenuBar.VIEW_MENU).addMenuItem(CMD_ENABLE_HOVER_PREVIEW);

    // Init PreferenceStorage
    prefs = PreferencesManager.getPreferenceStorage(module, defaultPrefs);

    // Setup initial UI state
    setEnabled(prefs.getValue("enabled"));

    AppInit.appReady(function () {
        if (brackets.test) {
            brackets.test.extensions.HoverPreview = module.exports;
        }
    });

    // For unit testing
    exports._queryPreviewProviders  = queryPreviewProviders;
    exports._forceShow              = function (popover) {
        hidePreview();
        popoverState = popover;
        showPreview();
    };
});


Bootstrap v1.4.0
https://github.com/twitter/bootstrap/zipball/v1.4.0
http://twitter.github.com/bootstrap/


// Load modules

var Dgram = require('dgram');
var Dns = require('dns');
var Hoek = require('hoek');

// Declare internals

var internals = {};

exports.time = function (options, callback) {

    if (arguments.length !== 2) {
        callback = arguments[0];
        options = {};
    }

    var settings = Hoek.clone(options);
    settings.host = settings.host || 'pool.ntp.org';
    settings.port = settings.port || 123;
    settings.resolveReference = settings.resolveReference || false;

    // Declare variables used by callback

    var timeoutId = 0;
    var sent = 0;

    // Ensure callback is only called once

    var isFinished = false;
    var finish = function (err, result) {

        if (timeoutId) {
            clearTimeout(timeoutId);
            timeoutId = 0;
        }

        if (!isFinished) {
            isFinished = true;
            socket.close();
            return callback(err, result);
        }
    };

    // Create UDP socket

    var socket = Dgram.createSocket('udp4');

    socket.on('error', function (err) {

        return finish(err);
    });

    // Listen to incoming messages

    socket.on('message', function (buffer, rinfo) {

        var received = Date.now();

        var message = new internals.NtpMessage(buffer);
        if (!message.isValid) {
            return finish(new Error('Invalid server response'), message);
        }

        if (message.originateTimestamp !== sent) {
            return finish(new Error('Wrong originate timestamp'), message);
        }

        // Timestamp Name          ID   When Generated
        // ------------------------------------------------------------
        // Originate Timestamp     T1   time request sent by client
        // Receive Timestamp       T2   time request received by server
        // Transmit Timestamp      T3   time reply sent by server
        // Destination Timestamp   T4   time reply received by client
        //
        // The roundtrip delay d and system clock offset t are defined as:
        //
        // d = (T4 - T1) - (T3 - T2)     t = ((T2 - T1) + (T3 - T4)) / 2

        var T1 = message.originateTimestamp;
        var T2 = message.receiveTimestamp;
        var T3 = message.transmitTimestamp;
        var T4 = received;

        message.d = (T4 - T1) - (T3 - T2);
        message.t = ((T2 - T1) + (T3 - T4)) / 2;
        message.receivedLocally = received;

        if (!settings.resolveReference ||
            message.stratum !== 'secondary') {

            return finish(null, message);
        }

        // Resolve reference IP address

        Dns.reverse(message.referenceId, function (err, domains) {

            if (!err) {
                message.referenceHost = domains[0];
            }

            return finish(null, message);
        });
    });

    // Set timeout

    if (settings.timeout) {
        timeoutId = setTimeout(function () {

            timeoutId = 0;
            return finish(new Error('Timeout'));
        }, settings.timeout);
    }

    // Construct NTP message

    var message = new Buffer(48);
    for (var i = 0; i < 48; i++) {                      // Zero message
        message[i] = 0;
    }

    message[0] = (0 << 6) + (4 << 3) + (3 << 0)         // Set version number to 4 and Mode to 3 (client)
    sent = Date.now();
    internals.fromMsecs(sent, message, 40);               // Set transmit timestamp (returns as originate)

    // Send NTP request

    socket.send(message, 0, message.length, settings.port, settings.host, function (err, bytes) {

        if (err ||
            bytes !== 48) {

            return finish(err || new Error('Could not send entire message'));
        }
    });
};

internals.NtpMessage = function (buffer) {

    this.isValid = false;

    // Validate

    if (buffer.length !== 48) {
        return;
    }

    // Leap indicator

    var li = (buffer[0] >> 6);
    switch (li) {
        case 0: this.leapIndicator = 'no-warning'; break;
        case 1: this.leapIndicator = 'last-minute-61'; break;
        case 2: this.leapIndicator = 'last-minute-59'; break;
        case 3: this.leapIndicator = 'alarm'; break;
    }

    // Version

    var vn = ((buffer[0] & 0x38) >> 3);
    this.version = vn;

    // Mode

    var mode = (buffer[0] & 0x7);
    switch (mode) {
        case 1: this.mode = 'symmetric-active'; break;
        case 2: this.mode = 'symmetric-passive'; break;
        case 3: this.mode = 'client'; break;
        case 4: this.mode = 'server'; break;
        case 5: this.mode = 'broadcast'; break;
        case 0:
        case 6:
        case 7: this.mode = 'reserved'; break;
    }

    // Stratum

    var stratum = buffer[1];
    if (stratum === 0) {
        this.stratum = 'death';
    }
    else if (stratum === 1) {
        this.stratum = 'primary';
    }
    else if (stratum <= 15) {
        this.stratum = 'secondary';
    }
    else {
        this.stratum = 'reserved';
    }

    // Poll interval (msec)

    this.pollInterval = Math.round(Math.pow(2, buffer[2])) * 1000;

    // Precision (msecs)

    this.precision = Math.pow(2, buffer[3]) * 1000;

    // Root delay (msecs)

    var rootDelay = 256 * (256 * (256 * buffer[4] + buffer[5]) + buffer[6]) + buffer[7];
    this.rootDelay = 1000 * (rootDelay / 0x10000);

    // Root dispersion (msecs)

    this.rootDispersion = ((buffer[8] << 8) + buffer[9] + ((buffer[10] << 8) + buffer[11]) / Math.pow(2, 16)) * 1000;

    // Reference identifier

    this.referenceId = '';
    switch (this.stratum) {
        case 'death':
        case 'primary':
            this.referenceId = String.fromCharCode(buffer[12]) + String.fromCharCode(buffer[13]) + String.fromCharCode(buffer[14]) + String.fromCharCode(buffer[15]);
            break;
        case 'secondary':
            this.referenceId = '' + buffer[12] + '.' + buffer[13] + '.' + buffer[14] + '.' + buffer[15];
            break;
    }

    // Reference timestamp

    this.referenceTimestamp = internals.toMsecs(buffer, 16);

    // Originate timestamp

    this.originateTimestamp = internals.toMsecs(buffer, 24);

    // Receive timestamp

    this.receiveTimestamp = internals.toMsecs(buffer, 32);

    // Transmit timestamp

    this.transmitTimestamp = internals.toMsecs(buffer, 40);

    // Validate

    if (this.version === 4 &&
        this.stratum !== 'reserved' &&
        this.mode === 'server' &&
        this.originateTimestamp &&
        this.receiveTimestamp &&
        this.transmitTimestamp) {

        this.isValid = true;
    }

    return this;
};

internals.toMsecs = function (buffer, offset) {

    var seconds = 0;
    var fraction = 0;

    for (var i = 0; i < 4; ++i) {
        seconds = (seconds * 256) + buffer[offset + i];
    }

    for (i = 4; i < 8; ++i) {
        fraction = (fraction * 256) + buffer[offset + i];
    }

    return ((seconds - 2208988800 + (fraction / Math.pow(2, 32))) * 1000);
};

internals.fromMsecs = function (ts, buffer, offset) {

    var seconds = Math.floor(ts / 1000) + 2208988800;
    var fraction = Math.round((ts % 1000) / 1000 * Math.pow(2, 32));

    buffer[offset + 0] = (seconds & 0xFF000000) >> 24;
    buffer[offset + 1] = (seconds & 0x00FF0000) >> 16;
    buffer[offset + 2] = (seconds & 0x0000FF00) >> 8;
    buffer[offset + 3] = (seconds & 0x000000FF);

    buffer[offset + 4] = (fraction & 0xFF000000) >> 24;
    buffer[offset + 5] = (fraction & 0x00FF0000) >> 16;
    buffer[offset + 6] = (fraction & 0x0000FF00) >> 8;
    buffer[offset + 7] = (fraction & 0x000000FF);
};

// Offset singleton

internals.last = {
    offset: 0,
    expires: 0,
    host: '',
    port: 0
};

exports.offset = function (options, callback) {

    if (arguments.length !== 2) {
        callback = arguments[0];
        options = {};
    }

    var now = Date.now();
    var clockSyncRefresh = options.clockSyncRefresh || 24 * 60 * 60 * 1000;                    // Daily

    if (internals.last.offset &&
        internals.last.host === options.host &&
        internals.last.port === options.port &&
        now < internals.last.expires) {

        return callback(null, internals.last.offset);
    }

    exports.time(options, function (err, time) {

        if (err) {
            return callback(err, 0);
        }

        internals.last = {
            offset: Math.round(time.t),
            expires: now + clockSyncRefresh,
            host: options.host,
            port: options.port
        };

        return callback(null, internals.last.offset);
    });
};

// Now singleton

internals.now = {
    intervalId: 0
};

exports.start = function (options, callback) {

    if (arguments.length !== 2) {
        callback = arguments[0];
        options = {};
    }

    if (internals.now.intervalId) {
        return callback();
    }

    exports.offset(options, function (err, offset) {

        internals.now.intervalId = setInterval(function () {

            exports.offset(options, function () { });
        }, options.clockSyncRefresh || 24 * 60 * 60 * 1000);                                // Daily

        return callback();
    });
};

exports.stop = function () {

    if (!internals.now.intervalId) {
        return;
    }

    clearInterval(internals.now.intervalId);
    internals.now.intervalId = 0;
};

exports.isLive = function () {

    return !!internals.now.intervalId;
};

exports.now = function () {

    var now = Date.now();
    if (!exports.isLive() ||
        now >= internals.last.expires) {

        return now;
    }

    return now + internals.last.offset;
};


it("Should show radial gradient preview for those with vendor prefix syntax", function () {

var expectedGradient1 = "-webkit-gradient(radial, center center, 0, center center, 141, from(black), to(white), color-stop(25%, blue), color-stop(40%, green), color-stop(60%, red), color-stop(80%, purple));",

expectedGradient2 = "-webkit-radial-gradient(center center, circle contain, black 0%, blue 25%, green 40%, red 60%, purple 80%, white 100%)";

checkGradientAtPos(expectedGradient1, 110, 93);   // old webkit syntax

checkGradientAtPos(expectedGradient2, 111, 36);   // -webkit- prefix preserved

checkGradientAtPos(expectedGradient2, 112, 36);   // -moz- prefix gets stripped

checkGradientAtPos(expectedGradient2, 113, 36);   // -ms- prefix gets stripped

var expectedGradient1 = "-webkit-gradient(radial, center center, 0, center center, 141, from(black), to(white), color-stop(25%, blue), color-stop(40%, green), color-stop(60%, red), color-stop(80%, purple))",


<!DOCTYPE html>

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

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

Item 1

Item 2

Item 3

Item 4

Item 5

Item 6

Item 7

	When dragging a sortable item to a new location, other items will make room
	for the that item by shifting to allow white space between them. Pass a
	class into the placeholder option to style that space to
	be visible.  Use the boolean forcePlaceholderSize option
	to set dimensions on the placeholder.


Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
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.


// Don't run code in ES5 strict mode.
// In case this module was in strict mode, all other modules called by this would also be strict.
// But when testing if the strict mode is preserved, we must ensure that this module is NOT strict.

var expect = require("expect.js"),
    fs = require("fs"),
    path = require("path");

var rewire;

describe("rewire", function () {
    before(function () {
        var fakeNodeModules = path.resolve(__dirname, "testModules/fake_node_modules");

        if (fs.existsSync(fakeNodeModules)) {
            fs.renameSync(fakeNodeModules, path.resolve(__dirname, "testModules/node_modules"));
        }
    });
    it("should pass all shared test cases", function () {
        require("./testModules/sharedTestCases.js");
    });
    it("should also work with CoffeeScript", function () {
        var coffeeModule;

        rewire = require("../");
        coffeeModule = rewire("./testModules/module.coffee");
        coffeeModule.__set__("fs", {
            readFileSync: function () {
                return "It works!";
            }
        });
        expect(coffeeModule.readFileSync()).to.be("It works!");
    });
});


Html5.supportsNativeTextTracks = function() {

* Check to see if native `VideoTrack`s are supported by this browser/device

keep keep keep replace keep keep keep keep keep

return browser.IS_ANY_SAFARI || (browser.IS_IOS && browser.IS_CHROME);

/**
 * @file browser.js
 * @module browser
 */
import * as Dom from './dom';
import window from 'global/window';

const USER_AGENT = window.navigator && window.navigator.userAgent || '';
const webkitVersionMap = (/AppleWebKit\/([\d.]+)/i).exec(USER_AGENT);
const appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;

/*
 * Device is an iPhone
 *
 * @type {Boolean}
 * @constant
 * @private
 */
export const IS_IPAD = (/iPad/i).test(USER_AGENT);

// The Facebook app's UIWebView identifies as both an iPhone and iPad, so
// to identify iPhones, we need to exclude iPads.
// http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
export const IS_IPHONE = (/iPhone/i).test(USER_AGENT) && !IS_IPAD;
export const IS_IPOD = (/iPod/i).test(USER_AGENT);
export const IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;

export const IOS_VERSION = (function() {
  const match = USER_AGENT.match(/OS (\d+)_/i);

  if (match && match[1]) {
    return match[1];
  }
  return null;
}());

export const IS_ANDROID = (/Android/i).test(USER_AGENT);
export const ANDROID_VERSION = (function() {
  // This matches Android Major.Minor.Patch versions
  // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
  const match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);

  if (!match) {
    return null;
  }

  const major = match[1] && parseFloat(match[1]);
  const minor = match[2] && parseFloat(match[2]);

  if (major && minor) {
    return parseFloat(match[1] + '.' + match[2]);
  } else if (major) {
    return major;
  }
  return null;
}());

export const IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;

export const IS_FIREFOX = (/Firefox/i).test(USER_AGENT);
export const IS_EDGE = (/Edge/i).test(USER_AGENT);
export const IS_CHROME = !IS_EDGE && (/Chrome/i).test(USER_AGENT);
export const CHROME_VERSION = (function() {
  const match = USER_AGENT.match(/Chrome\/(\d+)/);

  if (match && match[1]) {
    return parseFloat(match[1]);
  }
  return null;
}());
export const IE_VERSION = (function() {
  const result = (/MSIE\s(\d+)\.\d/).exec(USER_AGENT);
  let version = result && parseFloat(result[1]);

  if (!version && (/Trident\/7.0/i).test(USER_AGENT) && (/rv:11.0/).test(USER_AGENT)) {
    // IE 11 has a different user agent string than other IE versions
    version = 11.0;
  }

  return version;
}());

export const IS_SAFARI = (/Safari/i).test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
export const IS_ANY_SAFARI = IS_SAFARI || IS_IOS;

export const TOUCH_ENABLED = Dom.isReal() && (
  'ontouchstart' in window ||
  window.DocumentTouch &&
  window.document instanceof window.DocumentTouch);