// Reset _ctrlDown if it was set to be ignored and the shorcuts with Ctrl key was handled

if (_ctrlDown === CtrlDownStates.DETECTED_AND_IGNORED &&

!e.repeat && e.ctrlKey && e.keyIdentifier === "Control") {

if (_ctrlDown === CtrlDownStates.NOT_YET_DETECTED && e.ctrlKey && e.keyIdentifier === "Control") {

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

if (_ctrlDown !== CtrlDownStates.DETECTED_AND_IGNORED && e.ctrlKey && e.keyIdentifier === "Control") {

/*
 * Copyright (c) 2012 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, beforeEach, afterEach, it, expect, brackets, spyOn, runs, waits */
/*unittests: KeyBindingManager */

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

    require("utils/Global");

    // Load dependent modules
    var CommandManager      = require("command/CommandManager"),
        Dialogs             = require("widgets/Dialogs"),
        KeyBindingManager   = require("command/KeyBindingManager"),
        KeyEvent            = require("utils/KeyEvent"),
        SpecRunnerUtils     = require("spec/SpecRunnerUtils"),
        Strings             = require("strings"),
        _                   = require("thirdparty/lodash");

    var platform = brackets.platform;

    var testPath            = SpecRunnerUtils.getTestPath("/spec/KeyBindingManager-test-files");

    var executed,
        testCommandFn       = function () { executed = true; };

    var defaultKeyBindings = {
            "Ctrl-L": "edit.selectLine",
            "Ctrl-Alt-L": "edit.splitSelIntoLines",
            "Alt-Shift-Down": "edit.addCursorToNextLine",
            "Alt-Shift-Up": "edit.addCursorToPrevLine",
            "F8": "navigate.gotoFirstProblem",
            "Ctrl-Alt-O": "file.openFolder",
            "Ctrl-Alt-H": "view.hideSidebar",
            "Ctrl-Shift-O": "navigate.quickOpen",
            "Ctrl-T": "navigate.gotoDefinition"
        },
        macDefaultKeyBindings = {
            "Ctrl-L": "edit.selectLine",
            "Alt-Cmd-L": "edit.splitSelIntoLines",
            "Alt-Shift-Down": "edit.addCursorToNextLine",
            "Alt-Shift-Up": "edit.addCursorToPrevLine",
            "Cmd-'": "navigate.gotoFirstProblem",
            "Alt-Cmd-O": "file.openFolder",
            "Shift-Cmd-H": "view.hideSidebar",
            "Shift-Cmd-O": "navigate.quickOpen",
            "Cmd-T": "navigate.gotoDefinition"
        };

    function key(k, displayKey, explicitPlatform) {
        return {
            key                 : k,
            displayKey          : displayKey || k,
            explicitPlatform    : explicitPlatform
        };
    }

    function keyBinding(k, commandID, displayKey, explicitPlatform) {
        var obj = key(k, displayKey, explicitPlatform);
        obj.commandID = commandID;

        return obj;
    }

    function keyMap(keyBindings) {
        var map = {};

        keyBindings.forEach(function (k) {
            map[k.key] = k;
        });

        return map;
    }

    function populateDefaultKeyMap() {
        var defaults = (platform === "mac") ? macDefaultKeyBindings : defaultKeyBindings,
            index = 0;

        _.forEach(defaults, function (commandID, key) {
            ++index;
            CommandManager.register("test command" + index.toString(), commandID, testCommandFn);
        });
    }

    function getDefaultKeyMap() {
        var bindings = [],
            defaults = (platform === "mac") ? macDefaultKeyBindings : defaultKeyBindings,
            displayKey = "",
            explicitPlatform;

        _.forEach(defaults, function (commandID, key) {
            displayKey = KeyBindingManager._getDisplayKey(key);
            if (platform === "mac") {
                explicitPlatform = undefined;
                if (commandID === "edit.selectLine" || commandID === "view.hideSidebar" ||
                        commandID === "navigate.gotoFirstProblem") {
                    explicitPlatform = "mac";
                }
            }
            bindings.push(keyBinding(key, commandID, displayKey, explicitPlatform));
        });
        return keyMap(bindings);
    }

    describe("KeyBindingManager", function () {

        beforeEach(function () {
            CommandManager._testReset();
            KeyBindingManager._reset();
            brackets.platform = "test";
        });

        afterEach(function () {
            CommandManager._testRestore();
            brackets.platform = platform;
        });

        describe("addBinding", function () {

            it("should require command and key binding arguments", function () {
                KeyBindingManager.addBinding();
                expect(KeyBindingManager.getKeymap()).toEqual({});

                KeyBindingManager.addBinding("test.foo");
                expect(KeyBindingManager.getKeymap()).toEqual({});
                expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([]);
            });

            it("should ignore invalid bindings", function () {
                expect(KeyBindingManager.addBinding("test.foo", "Ktrl-Shift-A")).toBeNull();
                expect(KeyBindingManager.addBinding("test.foo", "Ctrl+R")).toBeNull();
                expect(KeyBindingManager.getKeymap()).toEqual({});
            });

            it("should add single bindings to the keymap", function () {
                var result = KeyBindingManager.addBinding("test.foo", "Ctrl-A"),
                    keyTest = key("Ctrl-A");

                expect(result).toEqual(keyTest);
                expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([keyTest]);

                result = KeyBindingManager.addBinding("test.bar", "Ctrl-B");
                keyTest = key("Ctrl-B");
                expect(result).toEqual(keyTest);
                expect(KeyBindingManager.getKeyBindings("test.bar")).toEqual([keyTest]);

                result = KeyBindingManager.addBinding("test.cat", "Ctrl-C", "bark");
                expect(result).toBeNull();

                result = KeyBindingManager.addBinding("test.dog", "Ctrl-D", "test");
                keyTest = key("Ctrl-D", null, "test");
                expect(result).toEqual(keyTest);
                expect(KeyBindingManager.getKeyBindings("test.dog")).toEqual([keyTest]);

                // only "test" platform bindings
                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.foo"),
                    keyBinding("Ctrl-B", "test.bar"),
                    keyBinding("Ctrl-D", "test.dog", null, "test")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should use displayKey to override display of the shortcut", function () {
                KeyBindingManager.addBinding("test.foo", key("Ctrl-=", "Ctrl-+"));

                // only "test" platform bindings
                var expected = keyMap([
                    keyBinding("Ctrl-=", "test.foo", "Ctrl-+")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should add multiple bindings to the keymap", function () {
                // use a fake platform
                brackets.platform = "test1";

                var results = KeyBindingManager.addBinding(
                    "test.foo",
                    [ {key: "Ctrl-A", platform: "test1"}, {key: "Ctrl-1", platform: "all"} ]
                );
                expect(results).toEqual([
                    key("Ctrl-A", null, "test1"),
                    key("Ctrl-1", null, "all")
                ]);
                expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([
                    key("Ctrl-A", null, "test1"),
                    key("Ctrl-1", null, "all")
                ]);

                results = KeyBindingManager.addBinding("test.bar", [{key: "Ctrl-B"}, {key: "Ctrl-2", platform: "test2"}]);
                expect(results).toEqual([
                    key("Ctrl-B")
                ]);
                expect(KeyBindingManager.getKeyBindings("test.bar")).toEqual([
                    key("Ctrl-B")
                ]);

                // only "test1" platform and cross-platform bindings
                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.foo", null, "test1"),
                    keyBinding("Ctrl-1", "test.foo", null, "all"),
                    keyBinding("Ctrl-B", "test.bar")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should allow the command argument to be a string or an object", function () {
                var result = KeyBindingManager.addBinding("test.foo", "Ctrl-A"),
                    keyTest = key("Ctrl-A");

                expect(result).toEqual(keyTest);
                expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([keyTest]);

                var commandObj = CommandManager.register("Bar", "test.bar", function () { return; });

                result = KeyBindingManager.addBinding(commandObj, "Ctrl-B");
                keyTest = key("Ctrl-B");

                expect(result).toEqual(keyTest);
                expect(KeyBindingManager.getKeyBindings("test.bar")).toEqual([keyTest]);

                // only "test" platform bindings
                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.foo"),
                    keyBinding("Ctrl-B", "test.bar")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should not allow a generic key binding to be replaced with another generic binding", function () {
                KeyBindingManager.addBinding("test.foo", "Ctrl-A");
                KeyBindingManager.addBinding("test.bar", "Ctrl-A");

                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.foo")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should allow a platform-specific key binding to override a generic binding", function () {
                KeyBindingManager.addBinding("test.foo", "Ctrl-A");
                KeyBindingManager.addBinding("test.bar", "Ctrl-A", "test");

                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.bar", null, "test")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should keep a platform-specific key binding if a generic binding is added later", function () {
                KeyBindingManager.addBinding("test.foo", "Ctrl-A", "test");
                KeyBindingManager.addBinding("test.bar", "Ctrl-A");

                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.foo", null, "test")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should allow a command to map to multiple key bindings", function () {
                KeyBindingManager.addBinding("test.foo", "Ctrl-A");
                KeyBindingManager.addBinding("test.foo", "Ctrl-B");

                // only "test1" platform and cross-platform bindings
                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.foo"),
                    keyBinding("Ctrl-B", "test.foo")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should support the Ctrl key on mac", function () {
                brackets.platform = "mac";

                KeyBindingManager.addBinding("test.cmd", "Cmd-A", "mac");
                KeyBindingManager.addBinding("test.ctrl", "Ctrl-A", "mac");
                KeyBindingManager.addBinding("test.ctrlAlt", "Ctrl-Alt-A", "mac");
                KeyBindingManager.addBinding("test.cmdCtrlAlt", "Cmd-Ctrl-A", "mac");

                var expected = keyMap([
                    keyBinding("Cmd-A", "test.cmd", null, "mac"),
                    keyBinding("Ctrl-A", "test.ctrl", null, "mac"),
                    keyBinding("Ctrl-Alt-A", "test.ctrlAlt", null, "mac"),
                    keyBinding("Ctrl-Cmd-A", "test.cmdCtrlAlt", null, "mac") // KeyBindingManager changes the order
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should use windows key bindings on linux", function () {
                var original = KeyBindingManager.useWindowsCompatibleBindings;

                this.after(function () {
                    KeyBindingManager.useWindowsCompatibleBindings = original;
                });

                KeyBindingManager.useWindowsCompatibleBindings = true;
                brackets.platform = "linux";

                // create a windows-specific binding
                KeyBindingManager.addBinding("test.cmd", "Ctrl-A", "win");

                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.cmd", null, "win")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);

                // create a generic binding to replace the windows binding
                KeyBindingManager.addBinding("test.cmd", "Ctrl-B");

                expected = keyMap([
                    keyBinding("Ctrl-B", "test.cmd", null)
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should support windows compatible bindings", function () {
                var original = KeyBindingManager.useWindowsCompatibleBindings;

                this.after(function () {
                    KeyBindingManager.useWindowsCompatibleBindings = original;
                });

                KeyBindingManager.useWindowsCompatibleBindings = true;
                brackets.platform = "linux";

                // create a generic binding
                KeyBindingManager.addBinding("test.cmd", "Ctrl-A");

                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.cmd")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);

                // create a linux-only binding to replace the windows binding
                KeyBindingManager.addBinding("test.cmd", "Ctrl-B", "linux");

                expected = keyMap([
                    keyBinding("Ctrl-B", "test.cmd", null, "linux")
                ]);

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });
        });

        describe("removeBinding", function () {

            it("should handle an empty keymap gracefully", function () {
                KeyBindingManager.removeBinding("Ctrl-A");
                expect(KeyBindingManager.getKeymap()).toEqual({});
            });

            it("should require a key to remove", function () {
                KeyBindingManager.addBinding("test.foo", "Ctrl-A");
                KeyBindingManager.addBinding("test.bar", "Ctrl-B");

                // keymap should be unchanged
                var expected = keyMap([
                    keyBinding("Ctrl-A", "test.foo"),
                    keyBinding("Ctrl-B", "test.bar")
                ]);

                KeyBindingManager.removeBinding();

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

            it("should remove a key from the key map", function () {
                KeyBindingManager.addBinding("test.foo", "Ctrl-A");
                KeyBindingManager.addBinding("test.foo", "Ctrl-B");

                // Ctrl-A should be removed
                var expected = keyMap([
                    keyBinding("Ctrl-B", "test.foo")
                ]);

                KeyBindingManager.removeBinding("Ctrl-A");

                expect(KeyBindingManager.getKeymap()).toEqual(expected);
                expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([key("Ctrl-B")]);

                KeyBindingManager.removeBinding("Ctrl-B");
                expect(KeyBindingManager.getKeyBindings("test.foo")).toEqual([]);
            });

            it("should remove a key from the key map for the specified platform", function () {
                brackets.platform = "test1";

                KeyBindingManager.addBinding("test.foo", "Ctrl-A", "test1");

                // remove Ctrl-A, only for platform "test1"
                KeyBindingManager.removeBinding("Ctrl-A", "test1");
                expect(KeyBindingManager.getKeymap()).toEqual({});
            });

            it("should exclude a specified platform key binding for a cross-platform command", function () {
                brackets.platform = "test1";

                // all platforms
                KeyBindingManager.addBinding("test.foo", "Ctrl-B");

                var expected = keyMap([
                    keyBinding("Ctrl-B", "test.foo")
                ]);

                // remove Ctrl-B, only for platform "test2"
                KeyBindingManager.removeBinding("Ctrl-B", "test2");
                expect(KeyBindingManager.getKeymap()).toEqual(expected);
            });

        });

        describe("Load User Key Map", function () {

            it("should show an error when loading an image file as a user key map file", function () {
                runs(function () {
                    var imageTestFilesPath = SpecRunnerUtils.getTestPath("/spec/test-image-files");
                    KeyBindingManager._setUserKeyMapFilePath(imageTestFilesPath + "/eye.jpg");
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        expect(message).toEqual(Strings.ERROR_LOADING_KEYMAP);
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should show an error when loading a corrupted key map file", function () {
                runs(function () {
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/invalid.json");
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        expect(message).toEqual(Strings.ERROR_KEYMAP_CORRUPT);
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should show an error when loading a key map file with only whitespaces", function () {
                runs(function () {
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/whitespace.json");
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        expect(message).toEqual(Strings.ERROR_KEYMAP_CORRUPT);
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should NOT show any error when loading a user key map file with an empty object", function () {
                runs(function () {
                    KeyBindingManager._initCommandAndKeyMaps();
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/empty.json");
                    KeyBindingManager._loadUserKeyMap();
                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).not.toHaveBeenCalled();
                });
            });

            it("should NOT show any error when loading a zero-byte user key map file", function () {
                runs(function () {
                    KeyBindingManager._initCommandAndKeyMaps();
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/blank.json");
                    KeyBindingManager._loadUserKeyMap();
                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).not.toHaveBeenCalled();
                });
            });

            it("should show an error when attempting to reassign a special command", function () {
                runs(function () {
                    CommandManager.register("test copy command", "edit.copy", testCommandFn);
                    KeyBindingManager._initCommandAndKeyMaps();
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/reassignCopy.json");
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        var msgPrefix = Strings.ERROR_RESTRICTED_COMMANDS.replace("{0}", "");
                        expect(message).toMatch(msgPrefix);
                        expect(message).toMatch("edit.copy");
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should show an error when attempting to reassign a restricted shortcut (either bind to a special command or a mac system shortcut)", function () {
                var testFilePath = (platform === "mac") ? (testPath + "/macRestrictedShortcut.json") : (testPath + "/restrictedShortcut.json");
                runs(function () {
                    brackets.platform = platform;
                    populateDefaultKeyMap();
                    KeyBindingManager._initCommandAndKeyMaps();
                    KeyBindingManager._setUserKeyMapFilePath(testFilePath);
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        var msgPrefix = Strings.ERROR_RESTRICTED_SHORTCUTS.replace("{0}", "");
                        expect(message).toMatch(msgPrefix);
                        if (platform === "mac") {
                            expect(message).toMatch("cmd-z");
                            expect(message).toMatch("Cmd-m");
                            expect(message).toMatch("cmd-h");
                        } else {
                            expect(message).toMatch("ctrl-z");
                        }
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should show an error when attempting to assign multiple shortcuts to the same command", function () {
                runs(function () {
                    brackets.platform = platform;
                    populateDefaultKeyMap();
                    KeyBindingManager._initCommandAndKeyMaps();
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/multipleShortcuts.json");
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        var msgPrefix = Strings.ERROR_MULTIPLE_SHORTCUTS.replace("{0}", "");
                        expect(message).toMatch(msgPrefix);
                        expect(message).toMatch("file.openFolder");
                        expect(message).toMatch("view.hideSidebar");
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should show an error when attempting to set duplicate shortcuts", function () {
                runs(function () {
                    brackets.platform = platform;
                    populateDefaultKeyMap();
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/duplicateShortcuts.json");
                    KeyBindingManager._initCommandAndKeyMaps();
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        var msgPrefix = Strings.ERROR_DUPLICATE_SHORTCUTS.replace("{0}", "");
                        expect(message).toMatch(msgPrefix);
                        expect(message).toMatch("Ctrl-2");
                        expect(message).toMatch("Alt-Ctrl-4");
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should show an error when parsing invalid shortcuts", function () {
                runs(function () {
                    brackets.platform = platform;
                    populateDefaultKeyMap();
                    KeyBindingManager._initCommandAndKeyMaps();
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/invalidKeys.json");
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        var msgPrefix = Strings.ERROR_INVALID_SHORTCUTS.replace("{0}", "");
                        expect(message).toMatch(msgPrefix);
                        expect(message).toMatch("command-2");
                        expect(message).toMatch("Home");
                        expect(message).toMatch("Option-Cmd-Backspace");
                        expect(message).toMatch("ctrl-kk");
                        expect(message).toMatch("cmd-Delete");
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should show an error when attempting to set shortcuts to non-existent commands", function () {
                runs(function () {
                    // The command map has to be empty for this test case. So we are intentionally NOT calling
                    // populateDefaultKeyMap() before loading the user key map.
                    KeyBindingManager._initCommandAndKeyMaps();
                    KeyBindingManager._setUserKeyMapFilePath(testPath + "/keymap.json");
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        var msgPrefix = Strings.ERROR_NONEXISTENT_COMMANDS.replace("{0}", "");
                        expect(message).toMatch(msgPrefix);
                        expect(message).toMatch("file.openFolder");
                        expect(message).toMatch("view.hideSidebar");
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    expect(Dialogs.showModalDialog).toHaveBeenCalled();
                });
            });

            it("should update key map with the user specified key bindings", function () {
                var testFilePath = (platform === "mac") ? (testPath + "/macKeymap.json") : (testPath + "/keymap.json");
                runs(function () {
                    brackets.platform = platform;
                    var defKeyMap = getDefaultKeyMap();
                    populateDefaultKeyMap();
                    KeyBindingManager._initCommandAndKeyMaps();
                    expect(KeyBindingManager.getKeymap()).toEqual(defKeyMap);
                    KeyBindingManager._setUserKeyMapFilePath(testFilePath);
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    var keymap = KeyBindingManager.getKeymap(),
                        reassignedKey1 = (platform === "mac") ? "Alt-Cmd-Backspace" : "Ctrl-Alt-Backspace",
                        reassignedKey2 = (platform === "mac") ? "Cmd-T" : "Ctrl-T";
                    expect(Dialogs.showModalDialog).not.toHaveBeenCalled();
                    expect(keymap["Ctrl-2"].commandID).toEqual("file.openFolder");
                    expect(keymap["Alt-Cmd-O"]).toBeFalsy();
                    expect(keymap["Alt-Ctrl-O"]).toBeFalsy();

                    expect(keymap[reassignedKey1].commandID).toEqual("view.hideSidebar");
                    expect(keymap["Shift-Cmd-H"]).toBeFalsy();
                    expect(keymap["Alt-Ctrl-H"]).toBeFalsy();

                    expect(keymap["Ctrl-L"].commandID).toEqual("navigate.gotoDefinition");
                    expect(keymap[reassignedKey2]).toBeFalsy();

                    expect(keymap["Alt-Cmd-L"]).toBeFalsy();
                    expect(keymap["Alt-Ctrl-L"]).toBeFalsy();
                });
            });

            it("should restore original key bindings when the user key map is updated", function () {
                var testFilePath1 = (platform === "mac") ? (testPath + "/macKeymap.json") : (testPath + "/keymap.json"),
                    testFilePath2 = (platform === "mac") ? (testPath + "/macKeymap1.json") : (testPath + "/keymap1.json");
                runs(function () {
                    brackets.platform = platform;
                    var defKeyMap = getDefaultKeyMap();
                    populateDefaultKeyMap();
                    KeyBindingManager._initCommandAndKeyMaps();
                    expect(KeyBindingManager.getKeymap()).toEqual(defKeyMap);
                    KeyBindingManager._setUserKeyMapFilePath(testFilePath1);
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    // Loading a different key map file to simulate the user updating an existing key map.
                    KeyBindingManager._setUserKeyMapFilePath(testFilePath2);
                    KeyBindingManager._loadUserKeyMap();
                    waits(300);

                    spyOn(Dialogs, 'showModalDialog').andCallFake(function (dlgClass, title, message, buttons) {
                        return {done: function (callback) { callback(Dialogs.DIALOG_BTN_OK); } };
                    });
                });
                runs(function () {
                    var keymap = KeyBindingManager.getKeymap(),
                        reassignedKey1 = (platform === "mac") ? "Alt-Cmd-Backspace" : "Ctrl-Alt-Backspace",
                        reassignedKey2 = (platform === "mac") ? "Alt-Cmd-O" : "Ctrl-Alt-O",
                        reassignedKey3 = (platform === "mac") ? "Cmd-T" : "Ctrl-T";

                    expect(Dialogs.showModalDialog).not.toHaveBeenCalled();
                    expect(keymap["Ctrl-2"].commandID).toEqual("view.hideSidebar");

                    // Previous user key binding to "view.hideSidebar" is gone.
                    expect(keymap[reassignedKey1]).toBeFalsy();

                    // Default key binding for "file.openFolder" is restored.
                    expect(keymap[reassignedKey2].commandID).toEqual("file.openFolder");

                    expect(keymap["Ctrl-L"].commandID).toEqual("navigate.gotoDefinition");
                    expect(keymap[reassignedKey3]).toBeFalsy();
                });
            });
        });

        describe("handleKey", function () {

            it("should execute a command", function () {
                var fooCalled = false;
                CommandManager.register("Foo", "test.foo", function () {
                    fooCalled = true;
                });

                KeyBindingManager.addBinding("test.foo", "Ctrl-A");
                expect(fooCalled).toBe(false);

                KeyBindingManager._handleKey("Ctrl-A");
                expect(fooCalled).toBe(true);
            });

        });

        describe("handle AltGr key", function () {
            var commandCalled, ctrlAlt1Event, ctrlAltEvents;
            var ctrlEvent = {
                ctrlKey: true,
                keyIdentifier: "Control",
                keyCode: KeyEvent.DOM_VK_CONTROL,
                immediatePropagationStopped: false,
                propagationStopped: false,
                defaultPrevented: false,
                stopImmediatePropagation: function () {
                    this.immediatePropagationStopped = true;
                },
                stopPropagation: function () {
                    this.propagationStopped = true;
                },
                preventDefault: function () {
                    this.defaultPrevented = true;
                }
            };

            function makeCtrlAltKeyEvents() {
                var altGrEvents = [],
                    altEvent = _.cloneDeep(ctrlEvent);

                altEvent.keyIdentifier = "Alt";
                altEvent.altKey = true;
                altEvent.keyCode = KeyEvent.DOM_VK_ALT;

                altGrEvents.push(_.cloneDeep(ctrlEvent));
                altGrEvents.push(altEvent);

                return altGrEvents;
            }

            function makeCtrlAlt1KeyEvent() {
                return {
                    ctrlKey: true,
                    altKey: true,
                    keyCode: "1".charCodeAt(0),
                    immediatePropagationStopped: false,
                    propagationStopped: false,
                    defaultPrevented: false,
                    stopImmediatePropagation: function () {
                        this.immediatePropagationStopped = true;
                    },
                    stopPropagation: function () {
                        this.propagationStopped = true;
                    },
                    preventDefault: function () {
                        this.defaultPrevented = true;
                    }
                };
            }

            beforeEach(function () {
                commandCalled = false;
                ctrlAlt1Event = makeCtrlAlt1KeyEvent();
                ctrlAltEvents = makeCtrlAltKeyEvents();
                CommandManager.register("FakeUnitTestCommand", "unittest.fakeCommand", function () {
                    commandCalled = true;
                });
                KeyBindingManager.addBinding("unittest.fakeCommand", "Ctrl-Alt-1");

                // Modify platform to "win" since right Alt key detection is done only on Windows.
                brackets.platform = "win";
            });

            afterEach(function () {
                // Restore the platform.
                brackets.platform = "test";
            });

            it("should block command execution if right Alt key is pressed", function () {
                // Simulate a right Alt key down with the specific sequence of keydown events.
                ctrlAltEvents.forEach(function (e) {
                    e.timeStamp = new Date();
                    KeyBindingManager._handleKeyEvent(e);
                });

                // Simulate the command shortcut
                KeyBindingManager._handleKeyEvent(ctrlAlt1Event);
                expect(commandCalled).toBe(false);

                // In this case, the event should not have been stopped, because KBM didn't handle it.
                expect(ctrlAlt1Event.immediatePropagationStopped).toBe(false);
                expect(ctrlAlt1Event.propagationStopped).toBe(false);
                expect(ctrlAlt1Event.defaultPrevented).toBe(false);

                // Now explicitly remove the keyup event listener created by _detectAltGrKeyDown function.
                KeyBindingManager._onCtrlUp(ctrlEvent);
            });

            it("should block command execution when right Alt key is pressed following a Ctrl shortcut execution", function () {
                var ctrlA = _.cloneDeep(ctrlEvent);
                ctrlA.keyIdentifier = null;
                ctrlA.keyCode = "A".charCodeAt(0);

                // Simulate a Ctrl-A shortcut
                KeyBindingManager._handleKeyEvent(ctrlA);

                // Simulate a right Alt key down with the specific sequence of keydown events.
                ctrlAltEvents.forEach(function (e) {
                    e.timeStamp = new Date();
                    KeyBindingManager._handleKeyEvent(e);
                });

                // Simulate the command shortcut
                KeyBindingManager._handleKeyEvent(ctrlAlt1Event);
                expect(commandCalled).toBe(false);

                // In this case, the event should not have been stopped, because KBM didn't handle it.
                expect(ctrlAlt1Event.immediatePropagationStopped).toBe(false);
                expect(ctrlAlt1Event.propagationStopped).toBe(false);
                expect(ctrlAlt1Event.defaultPrevented).toBe(false);

                // Now explicitly remove the keyup event listener created by _detectAltGrKeyDown function.
                KeyBindingManager._onCtrlUp(ctrlEvent);
            });

            it("should not block command execution if interval between Ctrl & Alt events are more than 30 ms.", function () {
                var lastTS;

                // Simulate a Ctrl-Alt keys down with the specific sequence of keydown events.
                ctrlAltEvents.forEach(function (e) {
                    if (!lastTS) {
                        e.timeStamp = new Date();
                        lastTS = e.timeStamp;
                    } else {
                        e.timeStamp = lastTS + 50;
                    }
                    KeyBindingManager._handleKeyEvent(e);
                });

                // Simulate the command shortcut
                KeyBindingManager._handleKeyEvent(ctrlAlt1Event);
                expect(commandCalled).toBe(true);

                // In this case, the event should have been stopped (but not immediately) because
                // KBM handled it.
                expect(ctrlAlt1Event.immediatePropagationStopped).toBe(false);
                expect(ctrlAlt1Event.propagationStopped).toBe(true);
                expect(ctrlAlt1Event.defaultPrevented).toBe(true);
            });

            it("should not block command execution when the right Alt key is not used", function () {
                // Simulate the command shortcut
                KeyBindingManager._handleKeyEvent(ctrlAlt1Event);
                expect(commandCalled).toBe(true);

                // In this case, the event should have been stopped (but not immediately) because
                // KBM handled it.
                expect(ctrlAlt1Event.immediatePropagationStopped).toBe(false);
                expect(ctrlAlt1Event.propagationStopped).toBe(true);
                expect(ctrlAlt1Event.defaultPrevented).toBe(true);
            });
        });

        describe("global hooks", function () {
            var commandCalled, hook1Called, hook2Called, ctrlAEvent;

            function keydownHook1(event) {
                hook1Called = true;
                return true;
            }

            function keydownHook2(event) {
                hook2Called = true;
                return true;
            }

            function makeKeyEvent() {
                // We don't create a real native event object here--just a fake
                // object with enough info for the key translation to work
                // properly--since our mock hooks don't actually look at it
                // anyway.
                return {
                    ctrlKey: true,
                    keyCode: "A".charCodeAt(0),
                    immediatePropagationStopped: false,
                    propagationStopped: false,
                    defaultPrevented: false,
                    stopImmediatePropagation: function () {
                        this.immediatePropagationStopped = true;
                    },
                    stopPropagation: function () {
                        this.propagationStopped = true;
                    },
                    preventDefault: function () {
                        this.defaultPrevented = true;
                    }
                };
            }

            beforeEach(function () {
                commandCalled = false;
                hook1Called = false;
                hook2Called = false;
                ctrlAEvent = makeKeyEvent();
                CommandManager.register("FakeUnitTestCommand", "unittest.fakeCommand", function () {
                    commandCalled = true;
                });
                KeyBindingManager.addBinding("unittest.fakeCommand", "Ctrl-A");
            });

            it("should block command execution if a global hook is added that prevents it", function () {
                KeyBindingManager.addGlobalKeydownHook(keydownHook1);
                KeyBindingManager._handleKeyEvent(ctrlAEvent);
                expect(hook1Called).toBe(true);
                expect(commandCalled).toBe(false);

                // In this case, the event should not have been stopped, because our hook didn't stop it
                // and KBM didn't handle it.
                expect(ctrlAEvent.immediatePropagationStopped).toBe(false);
                expect(ctrlAEvent.propagationStopped).toBe(false);
                expect(ctrlAEvent.defaultPrevented).toBe(false);
            });

            it("should not block command execution if a global hook is added then removed", function () {
                KeyBindingManager.addGlobalKeydownHook(keydownHook1);
                KeyBindingManager.removeGlobalKeydownHook(keydownHook1);
                KeyBindingManager._handleKeyEvent(ctrlAEvent);
                expect(hook1Called).toBe(false);
                expect(commandCalled).toBe(true);

                // In this case, the event should have been stopped (but not immediately) because
                // KBM handled it.
                expect(ctrlAEvent.immediatePropagationStopped).toBe(false);
                expect(ctrlAEvent.propagationStopped).toBe(true);
                expect(ctrlAEvent.defaultPrevented).toBe(true);
            });

            it("should call the most recently added hook first", function () {
                KeyBindingManager.addGlobalKeydownHook(keydownHook1);
                KeyBindingManager.addGlobalKeydownHook(keydownHook2);
                KeyBindingManager._handleKeyEvent(ctrlAEvent);
                expect(hook2Called).toBe(true);
                expect(hook1Called).toBe(false);
                expect(commandCalled).toBe(false);

                // In this case, the event should not have been stopped, because our hook didn't stop it
                // and KBM didn't handle it.
                expect(ctrlAEvent.immediatePropagationStopped).toBe(false);
                expect(ctrlAEvent.propagationStopped).toBe(false);
                expect(ctrlAEvent.defaultPrevented).toBe(false);
            });
        });

    });
});


::-webkit-scrollbar {
    width: 12px;
}::-webkit-scrollbar-thumb:window-inactive{
    background: white;
}

::-webkit-scrollbar-thumb
    {
    background: white;
}


<!DOCTYPE html>

	<meta charset="utf-8">
	<title>jQuery UI Tabs - Open on mouseover</title>
	<link rel="stylesheet" href="../../themes/base/jquery.ui.all.css">

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

Nunc tincidunt

Proin dolor

Aenean lacinia

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.

Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.

Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.

Toggle sections open/closed on mouseover with the event option. The default value for event is "click."

event: 'mouseover'


<!doctype html>

	<meta charset="utf-8">
	<title>jQuery UI Tooltip - Track the mouse</title>
	<link rel="stylesheet" href="../../themes/base/jquery.ui.all.css">

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

Tooltips can be attached to any element. When you hover
the element with your mouse, the title attribute is displayed in a little box next to the element, just like a native tooltip.

But as it's not a native tooltip, it can be styled. Any themes built with
ThemeRoller
will also style tooltips accordingly.

Tooltips are also useful for form elements, to show some additional information in the context of each field.

Your age:<input id="age" title="We ask for your age only for statistical purposes.">

Hover the field to see the tooltip.

Here the tooltips are positioned relative to the mouse, and follow the mouse while it moves above the element.


} else if (e.ctrlKey && e.keyIdentifier === "Control") {

// We get here if the user is holding down left/right Control key. Set it to false

// so that we don't misidentify the combination of Ctrl and Alt keys as AltGr key.

} else if (e.repeat && e.ctrlKey && e.keyIdentifier === "Control") {


var binary = require('../');
var test = require('tap').test;
var EventEmitter = require('events').EventEmitter;

test('scan', function (t) {
    t.plan(4);

    var em = new EventEmitter;
    binary(em)
        .word8('a')
        .scan('l1', new Buffer('\r\n'))
        .scan('l2', '\r\n')
        .word8('z')
        .tap(function (vars) {
            t.same(vars.a, 99);
            t.same(vars.l1.toString(), 'foo bar');
            t.same(vars.l2.toString(), 'baz');
            t.same(vars.z, 42);
        })
    ;

    setTimeout(function () {
        em.emit('data', new Buffer([99,0x66,0x6f,0x6f,0x20]));
    }, 20);

    setTimeout(function () {
        em.emit('data', new Buffer('bar\r'));
    }, 40);

    setTimeout(function () {
        em.emit('data', new Buffer('\nbaz\r\n*'));
    }, 60);
});


// 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.js');
var assert = require('assert');
var R = require('../../lib/_stream_readable');
var util = require('util');

// tiny node-tap lookalike.
var tests = [];
var count = 0;

function test(name, fn) {
  count++;
  tests.push([name, fn]);
}

function run() {
  var next = tests.shift();
  if (!next)
    return console.error('ok');

  var name = next[0];
  var fn = next[1];
  console.log('# %s', name);
  fn({
    same: assert.deepEqual,
    equal: assert.equal,
    end: function () {
      count--;
      run();
    }
  });
}

// ensure all tests have run
process.on("exit", function () {
  assert.equal(count, 0);
});

process.nextTick(run);

/////

util.inherits(TestReader, R);

function TestReader(n, opts) {
  R.call(this, opts);

  this.pos = 0;
  this.len = n || 100;
}

TestReader.prototype._read = function(n) {
  setTimeout(function() {

    if (this.pos >= this.len) {
      return this.push(null);
    }

    n = Math.min(n, this.len - this.pos);
    if (n <= 0) {
      return this.push(null);
    }

    this.pos += n;
    var ret = new Buffer(n);
    ret.fill('a');

    console.log("this.push(ret)", ret)

    return this.push(ret);
  }.bind(this), 1);
};

test('setEncoding utf8', function(t) {
  var tr = new TestReader(100);
  tr.setEncoding('utf8');
  var out = [];
  var expect =
    [ 'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa' ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(10)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('setEncoding hex', function(t) {
  var tr = new TestReader(100);
  tr.setEncoding('hex');
  var out = [];
  var expect =
    [ '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161' ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(10)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('setEncoding hex with read(13)', function(t) {
  var tr = new TestReader(100);
  tr.setEncoding('hex');
  var out = [];
  var expect =
    [ "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "16161" ];

  tr.on('readable', function flow() {
    console.log("readable once")
    var chunk;
    while (null !== (chunk = tr.read(13)))
      out.push(chunk);
  });

  tr.on('end', function() {
    console.log("END")
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('encoding: utf8', function(t) {
  var tr = new TestReader(100, { encoding: 'utf8' });
  var out = [];
  var expect =
    [ 'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa' ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(10)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('encoding: hex', function(t) {
  var tr = new TestReader(100, { encoding: 'hex' });
  var out = [];
  var expect =
    [ '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161' ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(10)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('encoding: hex with read(13)', function(t) {
  var tr = new TestReader(100, { encoding: 'hex' });
  var out = [];
  var expect =
    [ "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "16161" ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(13)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});


/*
 * Copyright (c) 2012 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, $, brackets, window, Mustache */

/**
 * Utilities for creating and managing standard modal dialogs.
 */
define(function (require, exports, module) {
    "use strict";

    require("utils/Global");

    var KeyBindingManager = require("command/KeyBindingManager"),
        KeyEvent          = require("utils/KeyEvent"),
        Strings           = require("strings"),
        DialogTemplate    = require("text!htmlContent/dialog-template.html");

    /**
     * Dialog Buttons IDs
     * @const {string}
     */
    var DIALOG_BTN_CANCEL           = "cancel",
        DIALOG_BTN_OK               = "ok",
        DIALOG_BTN_DONTSAVE         = "dontsave",
        DIALOG_BTN_SAVE_AS          = "save_as",
        DIALOG_CANCELED             = "_canceled",
        DIALOG_BTN_DOWNLOAD         = "download";

    /**
     * Dialog Buttons Class Names
     * @const {string}
     */
    var DIALOG_BTN_CLASS_PRIMARY    = "primary",
        DIALOG_BTN_CLASS_NORMAL     = "",
        DIALOG_BTN_CLASS_LEFT       = "left";

    /**
     * The z-index used for the dialogs. Each new dialog increase this number by 2
     * @type {number}
     */
    var zIndex = 1050;

    /**
     * @private
     * Dismises a modal dialog
     * @param {$.Element} $dlg
     * @param {string} buttonId
     */
    function _dismissDialog($dlg, buttonId) {
        $dlg.data("buttonId", buttonId);
        $dlg.modal("hide");
    }

    /**
     * @private
     * If autoDismiss is true, then dismisses the dialog. Otherwise just raises an event that the
     * given button was clicked.
     * @param {$.Element} $dlg The dialog element to be dismissed.
     * @param {string} buttonId The ID of the button that was clicked.
     * @param {boolean} autoDismiss Whether to autodismiss the dialog on a button click.
     */
    function _processButton($dlg, buttonId, autoDismiss) {
        if (autoDismiss) {
            _dismissDialog($dlg, buttonId);
        } else {
            $dlg.triggerHandler("buttonClick", buttonId);
        }
    }

    /**
     * @private
     * Returns true if the modal dialog has a button with the given ID
     * @param {$.Element} $dlg
     * @param {string} buttonId
     * @return {boolean}
     */
    function _hasButton($dlg, buttonId) {
        return ($dlg.find("[data-button-id='" + buttonId + "']").length > 0);
    }

    /**
     * @private
     * Handles the use of Tab so that it stays inside the Dialog
     * @param {$.Event} event
     * @param {$.Element} $dlg
     */
    function _handleTab(event, $dlg) {
        var $inputs = $(":input:enabled, a", $dlg).filter(":visible");

        function stopEvent() {
            event.stopPropagation();
            event.preventDefault();
        }

        if ($(event.target).closest($dlg).length) {
            // If it's the first or last tabbable element, focus the last/first element
            if ((!event.shiftKey && event.target === $inputs[$inputs.length - 1]) ||
                    (event.shiftKey && event.target === $inputs[0])) {
                $inputs.filter(event.shiftKey ? ":last" : ":first").focus();
                stopEvent();

            // If there is no element to focus, don't let it focus outside of the dialog
            } else if (!$inputs.length) {
                stopEvent();
            }

        // If the focus left the dialog, focus the first element in the dialog
        } else {
            $inputs.first().focus();
            stopEvent();
        }
    }

    /**
     * Handles the keyDown event for the dialogs
     * @param {$.Event} e
     * @param {boolean} autoDismiss
     * @return {boolean}
     */
    var _keydownHook = function (e, autoDismiss) {
        var $primaryBtn     = this.find(".primary"),
            buttonId        = null,
            which           = String.fromCharCode(e.which),
            $focusedElement = this.find(".dialog-button:focus, a:focus");

        function stopEvent() {
            e.preventDefault();
            e.stopPropagation();
        }

        // There might be a textfield in the dialog's UI; don't want to mistake normal typing for dialog dismissal
        var inTextArea    = (e.target.tagName === "TEXTAREA"),
            inTypingField = inTextArea || ($(e.target).filter(":text, :password").length > 0);

        if (e.which === KeyEvent.DOM_VK_TAB) {
            // We don't want to stopEvent() in this case since we might want the default behavior.
            // _handleTab takes care of stopping/preventing default as necessary.
            _handleTab(e, this);
        } else if (e.which === KeyEvent.DOM_VK_ESCAPE) {
            buttonId = DIALOG_BTN_CANCEL;
        } else if (e.which === KeyEvent.DOM_VK_RETURN && (!inTextArea || e.ctrlKey)) {
            // Enter key in single-line text input always dismisses; in text area, only Ctrl+Enter dismisses
            // Click primary
            stopEvent();
            if (e.target.tagName === "BUTTON") {
                this.find(e.target).click();
            } else {
                $primaryBtn.click();
            }
        } else if (e.which === KeyEvent.DOM_VK_SPACE) {
            if ($focusedElement.length) {
                // Space bar on focused button or link
                stopEvent();
                $focusedElement.click();
            }
        } else if (brackets.platform === "mac") {
            // CMD+D Don't Save
            if (e.metaKey && (which === "D")) {
                if (_hasButton(this, DIALOG_BTN_DONTSAVE)) {
                    buttonId = DIALOG_BTN_DONTSAVE;
                }
            // FIXME (issue #418) CMD+. Cancel swallowed by native shell
            } else if (e.metaKey && (e.which === KeyEvent.DOM_VK_PERIOD)) {
                buttonId = DIALOG_BTN_CANCEL;
            }
        } else { // if (brackets.platform === "win") {
            // 'N' Don't Save
            if (which === "N" && !inTypingField) {
                if (_hasButton(this, DIALOG_BTN_DONTSAVE)) {
                    buttonId = DIALOG_BTN_DONTSAVE;
                }
            }
        }

        if (buttonId) {
            stopEvent();
            _processButton(this, buttonId, autoDismiss);
        }

        // Stop any other global hooks from processing the event (but
        // allow it to continue bubbling if we haven't otherwise stopped it).
        return true;
    };

    /**
     * @constructor
     * @private
     *
     * @param {$.Element} $dlg The dialog jQuery element
     * @param {$.Promise} promise A promise that will be resolved with the ID of the clicked button when the dialog
     *     is dismissed. Never rejected.
     */
    function Dialog($dlg, promise) {
        this._$dlg    = $dlg;
        this._promise = promise;
    }

    /**
     * The dialog jQuery element
     * @type {$.Element}
     */
    Dialog.prototype.getElement = function () {
        return this._$dlg;
    };

    /**
     * The dialog promise
     * @type {$.Promise}
     */
    Dialog.prototype.getPromise = function () {
        return this._promise;
    };

    /**
     * Closes the dialog if is visible
     */
    Dialog.prototype.close = function () {
        if (this._$dlg.is(":visible")) {   // Bootstrap breaks if try to hide dialog that's already hidden
            _dismissDialog(this._$dlg, DIALOG_CANCELED);
        }
    };

    /**
     * Adds a done callback to the dialog promise
     */
    Dialog.prototype.done = function (callback) {
        this._promise.done(callback);
    };

    /**
     * Don't allow dialog to exceed viewport size
     */
    function setDialogMaxSize() {
        var maxWidth, maxHeight,
            $dlgs = $(".modal-inner-wrapper > .instance");

        // Verify 1 or more modal dialogs are showing
        if ($dlgs.length > 0) {
            maxWidth  = $("body").width();
            maxHeight = $("body").height();

            $dlgs.css({
                "max-width":  maxWidth,
                "max-height": maxHeight,
                "overflow":   "auto"
            });
        }
    }

    /**
     * Creates a new modal dialog from a given template.
     * The template can either be a string or a jQuery object representing a DOM node that is *not* in the current DOM.
     *
     * @param {string} template A string template or jQuery object to use as the dialog HTML.
     * @param {boolean=} autoDismiss Whether to automatically dismiss the dialog when one of the buttons
     *      is clicked. Default true. If false, you'll need to manually handle button clicks and the Esc
     *      key, and dismiss the dialog yourself when ready by calling `close()` on the returned dialog.
     * @return {Dialog}
     */
    function showModalDialogUsingTemplate(template, autoDismiss) {
        if (autoDismiss === undefined) {
            autoDismiss = true;
        }

        $("body").append("

");

        var result  = new $.Deferred(),
            promise = result.promise(),
            $dlg    = $(template)
                .addClass("instance")
                .appendTo(".modal-inner-wrapper:last");

        // Don't allow dialog to exceed viewport size
        setDialogMaxSize();

        // Save the dialog promise for unit tests
        $dlg.data("promise", promise);

        var keydownHook = function (e) {
            return _keydownHook.call($dlg, e, autoDismiss);
        };

        // Pipe dialog-closing notification back to client code
        $dlg.one("hidden", function () {
            var buttonId = $dlg.data("buttonId");
            if (!buttonId) {    // buttonId will be undefined if closed via Bootstrap's "x" button
                buttonId = DIALOG_BTN_CANCEL;
            }

            // Let call stack return before notifying that dialog has closed; this avoids issue #191
            // if the handler we're triggering might show another dialog (as long as there's no
            // fade-out animation)
            window.setTimeout(function () {
                result.resolve(buttonId);
            }, 0);

            // Remove the dialog instance from the DOM.
            $dlg.remove();

            // Remove our global keydown handler.
            KeyBindingManager.removeGlobalKeydownHook(keydownHook);

            //Remove wrapper
            $(".modal-wrapper:last").remove();
        }).one("shown", function () {
            var $primaryBtn = $dlg.find(".primary:enabled"),
                $otherBtn   = $dlg.find(".modal-footer .dialog-button:enabled:eq(0)");

            // Set focus to the primary button, to any other button, or to the dialog depending
            // if there are buttons
            if ($primaryBtn.length) {
                $primaryBtn.focus();
            } else if ($otherBtn.length) {
                $otherBtn.focus();
            } else {
                document.activeElement.blur();
            }

            // Push our global keydown handler onto the global stack of handlers.
            KeyBindingManager.addGlobalKeydownHook(keydownHook);
        });

        // Click handler for buttons
        $dlg.one("click", ".dialog-button", function (e) {
            _processButton($dlg, $(this).attr("data-button-id"), autoDismiss);
        });

        // Run the dialog
        $dlg
            .modal({
                backdrop: "static",
                show:     true,
                selector: ".modal-inner-wrapper:last",
                keyboard: false // handle the ESC key ourselves so we can deal with nested dialogs
            })
            // Updates the z-index of the modal dialog and the backdrop
            .css("z-index", zIndex + 1)
            .next()
            .css("z-index", zIndex);

        zIndex += 2;

        return (new Dialog($dlg, promise));
    }

    /**
     * Creates a new general purpose modal dialog using the default template and the template variables given
     * as parameters as described.
     *
     * @param {string} dlgClass A class name identifier for the dialog. Typically one of DefaultDialogs.*
     * @param {string=} title The title of the dialog. Can contain HTML markup. Defaults to "".
     * @param {string=} message The message to display in the dialog. Can contain HTML markup. Defaults to "".
     * @param {Array.<{className: string, id: string, text: string}>=} buttons An array of buttons where each button
     *      has a class, id and text property. The id is used in "data-button-id". Defaults to a single Ok button.
     *      Typically className is one of DIALOG_BTN_CLASS_*, id is one of DIALOG_BTN_*
     * @param {boolean=} autoDismiss Whether to automatically dismiss the dialog when one of the buttons
     *      is clicked. Default true. If false, you'll need to manually handle button clicks and the Esc
     *      key, and dismiss the dialog yourself when ready by calling `close()` on the returned dialog.
     * @return {Dialog}
     */
    function showModalDialog(dlgClass, title, message, buttons, autoDismiss) {
        var templateVars = {
            dlgClass: dlgClass,
            title:    title   || "",
            message:  message || "",
            buttons:  buttons || [{ className: DIALOG_BTN_CLASS_PRIMARY, id: DIALOG_BTN_OK, text: Strings.OK }]
        };
        var template = Mustache.render(DialogTemplate, templateVars);

        return showModalDialogUsingTemplate(template, autoDismiss);
    }

    /**
     * Immediately closes any dialog instances with the given class. The dialog callback for each instance will
     * be called with the special buttonId DIALOG_CANCELED (note: callback is run asynchronously).
     * @param {string} dlgClass The class name identifier for the dialog.
     * @param {string=} buttonId The button id to use when closing the dialog. Defaults to DIALOG_CANCELED
     */
    function cancelModalDialogIfOpen(dlgClass, buttonId) {
        $("." + dlgClass + ".instance").each(function () {
            if ($(this).is(":visible")) {   // Bootstrap breaks if try to hide dialog that's already hidden
                _dismissDialog($(this), buttonId || DIALOG_CANCELED);
            }
        });
    }

    window.addEventListener("resize", setDialogMaxSize);

    exports.DIALOG_BTN_CANCEL            = DIALOG_BTN_CANCEL;
    exports.DIALOG_BTN_OK                = DIALOG_BTN_OK;
    exports.DIALOG_BTN_DONTSAVE          = DIALOG_BTN_DONTSAVE;
    exports.DIALOG_BTN_SAVE_AS           = DIALOG_BTN_SAVE_AS;
    exports.DIALOG_CANCELED              = DIALOG_CANCELED;
    exports.DIALOG_BTN_DOWNLOAD          = DIALOG_BTN_DOWNLOAD;

    exports.DIALOG_BTN_CLASS_PRIMARY     = DIALOG_BTN_CLASS_PRIMARY;
    exports.DIALOG_BTN_CLASS_NORMAL      = DIALOG_BTN_CLASS_NORMAL;
    exports.DIALOG_BTN_CLASS_LEFT        = DIALOG_BTN_CLASS_LEFT;

    exports.showModalDialog              = showModalDialog;
    exports.showModalDialogUsingTemplate = showModalDialogUsingTemplate;
    exports.cancelModalDialogIfOpen      = cancelModalDialogIfOpen;
});


// Now explicitly remove the keyup event listener created by _detectAltGrKeyDown function.

it("should block command execution when right Alt key is pressed following a Ctrl shortcut execution", function () {

keep keep keep keep keep replace replace replace keep

var ctrlEvent1 = _.cloneDeep(ctrlEvent);


/*
 * Copyright (c) 2012 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, forin: true, maxerr: 50, regexp: true */
/*global define, $ */

/**
 * ScriptAgent tracks all executed scripts, defines internal breakpoints, and
 * interfaces with the remote debugger.
 */
define(function ScriptAgent(require, exports, module) {
    "use strict";

    var Inspector = require("LiveDevelopment/Inspector/Inspector");
    var DOMAgent = require("LiveDevelopment/Agents/DOMAgent");

    var _load; // the load promise
    var _urlToScript; // url -> script info
    var _idToScript; // id -> script info
    var _insertTrace; // the last recorded trace of a DOM insertion

    // TODO: should the parameter to this be an ID rather than a URL?
    /** Get the script information for a given url
     * @param {string} url
     */
    function scriptWithId(url) {
        return _idToScript[url];
    }

    // TODO: Strip off query/hash strings from URL (see CSSAgent._canonicalize())
    /** Get the script information for a given url
     * @param {string} url
     */
    function scriptForURL(url) {
        return _urlToScript[url];
    }

    // DOMAgent Event: Document root loaded
    function _onGetDocument(event, res) {
        Inspector.DOMDebugger.setDOMBreakpoint(res.root.nodeId, "subtree-modified");
        _load.resolve();
    }

    // WebInspector Event: DOM.childNodeInserted
    function _onChildNodeInserted(event, res) {
        // res = {parentNodeId, previousNodeId, node}
        if (_insertTrace) {
            var node = DOMAgent.nodeWithId(res.node.nodeId);
            node.trace = _insertTrace;
            _insertTrace = undefined;
        }
    }

    // TODO: Strip off query/hash strings from URL (see CSSAgent._canonicalize())
    // WebInspector Event: Debugger.scriptParsed
    function _onScriptParsed(event, res) {
        // res = {scriptId, url, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL}
        _idToScript[res.scriptId] = res;
        _urlToScript[res.url] = res;
    }

    // WebInspector Event: Debugger.scriptFailedToParse
    function _onScriptFailedToParse(event, res) {
        // res = {url, scriptSource, startLine, errorLine, errorMessage}
    }

    // WebInspector Event: Debugger.paused
    function _onPaused(event, res) {
        // res = {callFrames, reason, data}
        switch (res.reason) {

        // Exception
        case "exception":
            Inspector.Debugger.resume();
            // var callFrame = res.callFrames[0];
            // var script = scriptWithId(callFrame.location.scriptId);
            break;

        // DOMBreakpoint
        case "DOM":
            Inspector.Debugger.resume();
            if (res.data.type === "subtree-modified" && res.data.insertion === true) {
                _insertTrace = res.callFrames;
            }
            break;
        }

    }

    function _reset() {
        _urlToScript = {};
        _idToScript = {};
    }

    /**
     * @private
     * WebInspector Event: Page.frameNavigated
     * @param {jQuery.Event} event
     * @param {frame: Frame} res
     */
    function _onFrameNavigated(event, res) {
        // Clear maps when navigating to a new page, but not if an iframe was loaded
        if (!res.frame.parentId) {
            _reset();
        }
    }

    /** Initialize the agent */
    function load() {
        _reset();
        _load = new $.Deferred();

        var enableResult = new $.Deferred();

        Inspector.Debugger.enable().done(function () {
            Inspector.Debugger.setPauseOnExceptions("uncaught").done(function () {
                enableResult.resolve();
            });
        });

        Inspector.Page.on("frameNavigated.ScriptAgent", _onFrameNavigated);
        DOMAgent.on("getDocument.ScriptAgent", _onGetDocument);
        Inspector.Debugger
            .on("scriptParsed.ScriptAgent", _onScriptParsed)
            .on("scriptFailedToParse.ScriptAgent", _onScriptFailedToParse)
            .on("paused.ScriptAgent", _onPaused);
        Inspector.DOM.on("childNodeInserted.ScriptAgent", _onChildNodeInserted);

        return $.when(_load.promise(), enableResult.promise());
    }

    /** Clean up */
    function unload() {
        _reset();
        Inspector.Page.off(".ScriptAgent");
        DOMAgent.off(".ScriptAgent");
        Inspector.Debugger.off(".ScriptAgent");
        Inspector.DOM.off(".ScriptAgent");
    }

    // Export public functions
    exports.scriptWithId = scriptWithId;
    exports.scriptForURL = scriptForURL;
    exports.load = load;
    exports.unload = unload;
});


oauth-sign

oauth-sign
==========

OAuth 1 signing. Formerly a vendor lib in mikeal/request, now a standalone module.