<commit_before>/* © Utrecht University and DialogueTrainer */

/*
    This file contains all functionality pertaining to the mini map shown in the upper right corner in the editor
*/

var MiniMap;

(function()
{
    "use strict";

    var zoom = 1,
        // Show details? Set to false to speed up editor processing in large scenarios
        detailed = true;

    MiniMap =
    {
        zoom: zoom,
        detailed: detailed,
        initialise: initialise,
        attachScrollListener: attachScrollListener,
        update: update,
        activate: activate,
        deactivate: deactivate
    };

    // Turned on for this session? (based on e.g. browser)
    var enabled = true,
    // Allowed by the context? If not, hidden completely, including controls
    active = true,
    // Turned on by the user? If not, hidden, but controls are still visible
    shown = false;

    function initialise()
    {
        recallShown();
        if (!shown)
        {
            $("#minimap").hide();
            $("#enableMinimap").prop("checked", false);
        }

        $("#miniwrap").css("display","block");
        $("#minimap").css("overflow", "hidden");

        $("#minimap").on("click", function(e)
        {
            updateScrollPosition(e);
        });
        $("#mainCell #main").on("scroll", function()
        {
            update(false);
        });

        // Zoomed in scroll event listeners for minimap are created by attachScrollListener,
        // called by createEmptyTree()!

        // Listeners for minimap checkboxes
        $("#enableMinimap").on("click", function()
        {
            if (this.checked)
            {
                setAndStoreShown(true);
                update(true);
                showAnimated($("#minimap"));
            }
            else
            {
                hideAnimated($("#minimap"));
                setAndStoreShown(false);
            }
        });
        $("#simpleMinimap").on("click", function()
        {
            if (this.checked)
            {
                detailed = false;
                $("#scaledDiv").empty();
                update(false);
            }
            else
            {
                detailed = true;
                update(true);
            }
        });
    }

    function attachScrollListener(treeDiv)
    {
        if (!enabled) return;

        treeDiv.on('scroll', function(e)
        {
            update(false);
        });
    }

    function updateScrollPosition(e)
    {
        var minimapSelectorX = e.pageX - $("#minimap").offset().left - $("#minimapSelector").width() / 2;
        var minimapSelectorY = e.pageY - $("#minimap").offset().top - $("#minimapSelector").height() / 2;

        // Limit the minimum/maximum positions of the selector
        var maxX = $("#minimap").width() - $("#minimapSelector").width();
        var maxY = $("#minimap").height() - $("#minimapSelector").height();
        if (minimapSelectorX < 0) minimapSelectorX = 0;
        if (minimapSelectorY < 0) minimapSelectorY = 0;
        if (minimapSelectorX > maxX) minimapSelectorX = maxX;
        if (minimapSelectorY > maxY) minimapSelectorY = maxY;

        $("#minimapSelector").css("top", minimapSelectorY+"px");
        $("#minimapSelector").css("left", minimapSelectorX+"px");

        minimapSelectorX /= zoom;
        minimapSelectorY /= zoom;

        if (!Zoom.isZoomed())   // Nothing is zoomed
        {
            $("#mainCell > #main").scrollLeft(minimapSelectorX);
            $("#mainCell > #main").scrollTop(minimapSelectorY);
        }
        else
        {
            $("#mainCell .treeContainer.zoom .treeDiv").scrollLeft(minimapSelectorX);
            $("#mainCell .treeContainer.zoom .treeDiv").scrollTop(minimapSelectorY);
        }

        update(false);
    }

    // Scale the minimap according to the main div dimensions
    // and optionally refresh the map by cloning the main div
    function update(refresh)
    {
        if (!(enabled && active && shown)) return;

        var realWidth, realHeight;
        var viewportWidth, viewportHeight, viewportX, viewportY, zoomFactor;

        var main = $("#mainCell > #main");

        if (!Zoom.isZoomed())   // Nothing is zoomed
        {
            realWidth = main[0].scrollWidth;
            realHeight = main[0].scrollHeight;
            viewportX = main.scrollLeft();
            viewportY = main.scrollTop();
            viewportWidth = main[0].clientWidth;
            viewportHeight = main[0].clientHeight;
        }
        else
        {
            var treeDiv = $("#mainCell .treeContainer.zoom .treeDiv");
            realWidth = treeDiv[0].scrollWidth;
            realHeight = treeDiv[0].scrollHeight + $("#mainCell .subjectDiv")[0].scrollHeight;
            viewportX = treeDiv.scrollLeft();
            viewportY = treeDiv.scrollTop() + $("#mainCell .subjectDiv")[0].scrollHeight;
            viewportWidth = treeDiv[0].clientWidth;
            viewportHeight = treeDiv[0].clientHeight;
        }

        $("#scaledDiv").css("width", realWidth+"px");
        $("#scaledDiv").css("height", realHeight+"px");

        // Show a cloned and scaled down version of the main div in the minimap
        if (detailed && refresh)
        {
            $("#scaledDiv").empty();
            var clonedMain = main.clone();
            // Remove inappropriate elements from the cloned main
            clonedMain.find("#gridIndicator").remove();
            if (Zoom.isZoomed()) clonedMain.find(".treeContainer").not(".zoom").remove();
            else clonedMain.find(".treeDiv").remove();
            // Remove all event handlers and IDs from the cloned main
            clonedMain.find("*").addBack().off().removeAttr("id");
            $("#scaledDiv").append(clonedMain);
        }

        // Standard width/height for minimap container
        var width = 300;
        var widthFactor = width / realWidth;

        zoomFactor = widthFactor;
        // minimap gets taller than the allowed screenheight
        if (zoomFactor * realHeight > $("#sidebar").height())
        {
            zoomFactor = $("#sidebar").height()/(realHeight+realHeight*0.1);
        }

        var minimapSize = {
            width: realWidth * zoomFactor + "px",
            height: realHeight * zoomFactor + "px"
        };
        $("#minimap").css(minimapSize);
        $("#minimap").data("savedSize", minimapSize);

        if (detailed && refresh)
        {
            $("#scaledDiv .treeContainer.zoom").css("left", 0 + "px");
            $("#scaledDiv .treeContainer.zoom").css("top", 0 + "px");
            $("#scaledDiv .treeContainer.zoom .treeDiv").css("height", realHeight + "px");
            $("#scaledDiv .treeContainer.zoom .treeDiv").css("width", realWidth + "px");
        }

        $("#scaledDiv").css({
            transform: "scale(" + zoomFactor + ")",
            transformOrigin: "top left"
        });

        // Scale and reposition the minimap selector
        $("#minimapSelector").css({
            width: viewportWidth * zoomFactor + "px",
            height: viewportHeight * zoomFactor + "px",
            top: viewportY * zoomFactor + "px",
            left: viewportX * zoomFactor + "px"
        });

        zoom = zoomFactor;
    }

    function recallShown()
    {
        var recalledShown = localStorage.getItem('MiniMap.shown');
        if (recalledShown !== null) shown = Boolean(JSON.parse(recalledShown));
    }

    function setAndStoreShown(newShown)
    {
        shown = Boolean(newShown);
        try
        {
            localStorage.setItem('MiniMap.shown', JSON.stringify(shown));
        }
        catch (e)
        {
            // storage is an enhancement, so ignore failure
        }
    }

    function showAnimated(element)
    {
        // slide Down with animation stopper
        var savedSize = element.data('savedSize');
        var finalHeight = savedSize ? savedSize.height : element.css('height', 'auto').height();
        element.height(0).show().stop(true,false).animate({height: finalHeight}, 400);
    }

    function hideAnimated(element)
    {
        // slide Up with animation stopper
        element.stop(true,false).animate({height: 0}, 400,
            'swing', function(){ element.hide(); });
    }

    function activate()
    {
        if (!enabled) return;

        if (!active) showAnimated($("#miniwrap"));
        active = true;
        update(true);
    }

    function deactivate()
    {
        if (!enabled) return;

        if (active) hideAnimated($("#miniwrap"));
        active = false;
    }
})();
<commit_msg>Throttled minimap scroll event<commit_after>/* © Utrecht University and DialogueTrainer */

/*
    This file contains all functionality pertaining to the mini map shown in the upper right corner in the editor
*/

var MiniMap;

(function()
{
    "use strict";

    var zoom = 1,
        // Show details? Set to false to speed up editor processing in large scenarios
        detailed = true;

    MiniMap =
    {
        zoom: zoom,
        detailed: detailed,
        initialise: initialise,
        attachScrollListener: attachScrollListener,
        update: update,
        activate: activate,
        deactivate: deactivate
    };

    // Turned on for this session? (based on e.g. browser)
    var enabled = true,
    // Allowed by the context? If not, hidden completely, including controls
    active = true,
    // Turned on by the user? If not, hidden, but controls are still visible
    shown = false;

    function initialise()
    {
        recallShown();
        if (!shown)
        {
            $("#minimap").hide();
            $("#enableMinimap").prop("checked", false);
        }

        $("#miniwrap").css("display","block");
        $("#minimap").css("overflow", "hidden");

        $("#minimap").on("click", function(e)
        {
            updateScrollPosition(e);
        });

        var scrollTimeout = null;
        $("#mainCell #main").on("scroll", function(e)
        {
            // Throttle scroll event
            if (scrollTimeout === null)
            {
                scrollTimeout = setTimeout(function()
                {
                    scrollTimeout = null;
                    update(false);
                }, 25);
            }
        });

        // Zoomed in scroll event listeners for minimap are created by attachScrollListener,
        // called by createEmptyTree()!

        // Listeners for minimap checkboxes
        $("#enableMinimap").on("click", function()
        {
            if (this.checked)
            {
                setAndStoreShown(true);
                update(true);
                showAnimated($("#minimap"));
            }
            else
            {
                hideAnimated($("#minimap"));
                setAndStoreShown(false);
            }
        });
        $("#simpleMinimap").on("click", function()
        {
            if (this.checked)
            {
                detailed = false;
                $("#scaledDiv").empty();
                update(false);
            }
            else
            {
                detailed = true;
                update(true);
            }
        });
    }

    function attachScrollListener(treeDiv)
    {
        if (!enabled) return;

        var scrollTimeout = null;
        treeDiv.on('scroll', function(e)
        {
            // Throttle scroll event
            if (scrollTimeout === null)
            {
                scrollTimeout = setTimeout(function()
                {
                    scrollTimeout = null;
                    update(false);
                }, 25);
            }
        });
    }

    function updateScrollPosition(e)
    {
        var minimapSelectorX = e.pageX - $("#minimap").offset().left - $("#minimapSelector").width() / 2;
        var minimapSelectorY = e.pageY - $("#minimap").offset().top - $("#minimapSelector").height() / 2;

        // Limit the minimum/maximum positions of the selector
        var maxX = $("#minimap").width() - $("#minimapSelector").width();
        var maxY = $("#minimap").height() - $("#minimapSelector").height();
        if (minimapSelectorX < 0) minimapSelectorX = 0;
        if (minimapSelectorY < 0) minimapSelectorY = 0;
        if (minimapSelectorX > maxX) minimapSelectorX = maxX;
        if (minimapSelectorY > maxY) minimapSelectorY = maxY;

        $("#minimapSelector").css("top", minimapSelectorY+"px");
        $("#minimapSelector").css("left", minimapSelectorX+"px");

        minimapSelectorX /= zoom;
        minimapSelectorY /= zoom;

        if (!Zoom.isZoomed())   // Nothing is zoomed
        {
            $("#mainCell > #main").scrollLeft(minimapSelectorX);
            $("#mainCell > #main").scrollTop(minimapSelectorY);
        }
        else
        {
            $("#mainCell .treeContainer.zoom .treeDiv").scrollLeft(minimapSelectorX);
            $("#mainCell .treeContainer.zoom .treeDiv").scrollTop(minimapSelectorY);
        }

        update(false);
    }

    // Scale the minimap according to the main div dimensions
    // and optionally refresh the map by cloning the main div
    function update(refresh)
    {
        if (!(enabled && active && shown)) return;

        var realWidth, realHeight;
        var viewportWidth, viewportHeight, viewportX, viewportY, zoomFactor;

        var main = $("#mainCell > #main");

        if (!Zoom.isZoomed())   // Nothing is zoomed
        {
            realWidth = main[0].scrollWidth;
            realHeight = main[0].scrollHeight;
            viewportX = main.scrollLeft();
            viewportY = main.scrollTop();
            viewportWidth = main[0].clientWidth;
            viewportHeight = main[0].clientHeight;
        }
        else
        {
            var treeDiv = $("#mainCell .treeContainer.zoom .treeDiv");
            realWidth = treeDiv[0].scrollWidth;
            realHeight = treeDiv[0].scrollHeight + $("#mainCell .subjectDiv")[0].scrollHeight;
            viewportX = treeDiv.scrollLeft();
            viewportY = treeDiv.scrollTop() + $("#mainCell .subjectDiv")[0].scrollHeight;
            viewportWidth = treeDiv[0].clientWidth;
            viewportHeight = treeDiv[0].clientHeight;
        }

        $("#scaledDiv").css("width", realWidth+"px");
        $("#scaledDiv").css("height", realHeight+"px");

        // Show a cloned and scaled down version of the main div in the minimap
        if (detailed && refresh)
        {
            $("#scaledDiv").empty();
            var clonedMain = main.clone();
            // Remove inappropriate elements from the cloned main
            clonedMain.find("#gridIndicator").remove();
            if (Zoom.isZoomed()) clonedMain.find(".treeContainer").not(".zoom").remove();
            else clonedMain.find(".treeDiv").remove();
            // Remove all event handlers and IDs from the cloned main
            clonedMain.find("*").addBack().off().removeAttr("id");
            $("#scaledDiv").append(clonedMain);
        }

        // Standard width/height for minimap container
        var width = 300;
        var widthFactor = width / realWidth;

        zoomFactor = widthFactor;
        // minimap gets taller than the allowed screenheight
        if (zoomFactor * realHeight > $("#sidebar").height())
        {
            zoomFactor = $("#sidebar").height()/(realHeight+realHeight*0.1);
        }

        var minimapSize = {
            width: realWidth * zoomFactor + "px",
            height: realHeight * zoomFactor + "px"
        };
        $("#minimap").css(minimapSize);
        $("#minimap").data("savedSize", minimapSize);

        if (detailed && refresh)
        {
            $("#scaledDiv .treeContainer.zoom").css("left", 0 + "px");
            $("#scaledDiv .treeContainer.zoom").css("top", 0 + "px");
            $("#scaledDiv .treeContainer.zoom .treeDiv").css("height", realHeight + "px");
            $("#scaledDiv .treeContainer.zoom .treeDiv").css("width", realWidth + "px");
        }

        $("#scaledDiv").css({
            transform: "scale(" + zoomFactor + ")",
            transformOrigin: "top left"
        });

        // Scale and reposition the minimap selector
        $("#minimapSelector").css({
            width: viewportWidth * zoomFactor + "px",
            height: viewportHeight * zoomFactor + "px",
            top: viewportY * zoomFactor + "px",
            left: viewportX * zoomFactor + "px"
        });

        zoom = zoomFactor;
    }

    function recallShown()
    {
        var recalledShown = localStorage.getItem('MiniMap.shown');
        if (recalledShown !== null) shown = Boolean(JSON.parse(recalledShown));
    }

    function setAndStoreShown(newShown)
    {
        shown = Boolean(newShown);
        try
        {
            localStorage.setItem('MiniMap.shown', JSON.stringify(shown));
        }
        catch (e)
        {
            // storage is an enhancement, so ignore failure
        }
    }

    function showAnimated(element)
    {
        // slide Down with animation stopper
        var savedSize = element.data('savedSize');
        var finalHeight = savedSize ? savedSize.height : element.css('height', 'auto').height();
        element.height(0).show().stop(true,false).animate({height: finalHeight}, 400);
    }

    function hideAnimated(element)
    {
        // slide Up with animation stopper
        element.stop(true,false).animate({height: 0}, 400,
            'swing', function(){ element.hide(); });
    }

    function activate()
    {
        if (!enabled) return;

        if (!active) showAnimated($("#miniwrap"));
        active = true;
        update(true);
    }

    function deactivate()
    {
        if (!enabled) return;

        if (active) hideAnimated($("#miniwrap"));
        active = false;
    }
})();
<|endoftext|>


<commit_before>var util2 = require('util2');
var myutil = require('myutil');
var Emitter = require('emitter');
var Accel = require('ui/accel');
var WindowStack = require('ui/windowstack');
var Propable = require('ui/propable');
var Stage = require('ui/stage');
var simply = require('ui/simply');

var buttons = [
  'back',
  'up',
  'select',
  'down',
];

/**
 * Enable fullscreen in the Pebble UI.
 * Fullscreen removes the Pebble status bar, giving slightly more vertical display height.
 * @memberOf simply
 * @param {boolean} fullscreen - Whether to enable fullscreen mode.
 */

/**
 * Enable scrolling in the Pebble UI.
 * When scrolling is enabled, up and down button presses are no longer forwarded to JavaScript handlers.
 * Single select, long select, and accel tap events are still available to you however.
 * @memberOf simply
 * @param {boolean} scrollable - Whether to enable a scrollable view.
 */

var configProps = [
  'fullscreen',
  'style',
  'scrollable',
  'backgroundColor',
];

var actionProps = [
  'up',
  'select',
  'back',
  'backgroundColor',
];

var accessorProps = configProps;

var defaults = {
  backgroundColor: 'black',
  fullscreen: false,
  scrollable: false,
};

var nextId = 1;

var Window = function(windowDef) {
  this.state = myutil.shadow(defaults, windowDef || {});
  this.state.id = nextId++;
  this._buttonInit();
  this._items = [];
  this._dynamic = true;
};

Window._codeName = 'window';

util2.copy(Emitter.prototype, Window.prototype);

util2.copy(Propable.prototype, Window.prototype);

util2.copy(Stage.prototype, Window.prototype);

Propable.makeAccessors(accessorProps, Window.prototype);

Window.prototype._id = function() {
  return this.state.id;
};

Window.prototype._hide = function(broadcast) {
  if (broadcast === false) { return; }
  simply.impl.windowHide(this._id());
};

Window.prototype.hide = function() {
  WindowStack.remove(this, true);
  return this;
};

Window.prototype._show = function(pushing) {
  this._prop(this.state, true, pushing);
  this._buttonConfig({});
  if (this._dynamic) {
    Stage.prototype._show.call(this, pushing);
  }
};

Window.prototype.show = function() {
  WindowStack.push(this);
  return this;
};

Window.prototype._insert = function() {
  if (this._dynamic) {
    Stage.prototype._insert.apply(this, arguments);
  }
};

Window.prototype._remove = function() {
  if (this._dynamic) {
    Stage.prototype._remove.apply(this, arguments);
  }
};

Window.prototype._clearAction = function() {
  actionProps.forEach(myutil.unset.bind(this, this.state.action));
};

Window.prototype._clear = function(flags) {
  flags = myutil.toFlags(flags);
  if (myutil.flag(flags, 'action')) {
    this._clearAction();
  }
};

Window.prototype.prop = function(field, value, clear) {
  if (arguments.length === 0) {
    return util2.copy(this.state);
  }
  if (arguments.length === 1 && typeof field !== 'object') {
    return this.state[field];
  }
  if (typeof field === 'object') {
    clear = value;
  }
  if (clear) {
    this._clear('all');
  }
  var windowDef = myutil.toObject(field, value);
  util2.copy(windowDef, this.state);
  this._prop(windowDef);
  return this;
};

Window.prototype._action = function(visible) {
  if (this === WindowStack.top()) {
    simply.impl.window({ action: typeof visible === 'boolean' ? visible : this.state.action }, 'action');
  }
};

Window.prototype.action = function(field, value, clear) {
  var action = this.state.action;
  if (!action) {
    action = this.state.action = {};
  }
  if (arguments.length === 0) {
    return action;
  }
  if (arguments.length === 1 && typeof field === 'string') {
    return action[field];
  }
  if (typeof field !== 'string') {
    clear = value;
  }
  if (clear) {
    this._clear('action');
  }
  if (typeof field !== 'boolean') {
    util2.copy(myutil.toObject(field, value), this.state.action);
  }
  this._action(field);
  return this;
};

var isBackEvent = function(type, subtype) {
  return ((type === 'click' || type === 'longClick') && subtype === 'back');
};

Window.prototype.onAddHandler = function(type, subtype) {
  if (isBackEvent(type, subtype)) {
    this._buttonAutoConfig();
  }
  if (type === 'accelData') {
    Accel.autoSubscribe();
  }
};

Window.prototype.onRemoveHandler = function(type, subtype) {
  if (!type || isBackEvent(type, subtype)) {
    this._buttonAutoConfig();
  }
  if (!type || type === 'accelData') {
    Accel.autoSubscribe();
  }
};

Window.prototype._buttonInit = function() {
  this._button = {
    config: {},
    configMode: 'auto',
  };
  for (var i = 0, ii = buttons.length; i < ii; i++) {
    var button = buttons[i];
    if (button !== 'back') {
      this._button.config[buttons[i]] = true;
    }
  }
};

/**
 * The button configuration parameter for {@link simply.buttonConfig}.
 * The button configuration allows you to enable to disable buttons without having to register or unregister handlers if that is your preferred style.
 * You may also enable the back button manually as an alternative to registering a click handler with 'back' as its subtype using {@link simply.on}.
 * @typedef {object} simply.buttonConf
 * @property {boolean} [back] - Whether to enable the back button. Initializes as false. Simply.js can also automatically register this for you based on the amount of click handlers with subtype 'back'.
 * @property {boolean} [up] - Whether to enable the up button. Initializes as true. Note that this is disabled when using {@link simply.scrollable}.
 * @property {boolean} [select] - Whether to enable the select button. Initializes as true.
 * @property {boolean} [down] - Whether to enable the down button. Initializes as true. Note that this is disabled when using {@link simply.scrollable}.
 */

/**
 * Changes the button configuration.
 * See {@link simply.buttonConfig}
 * @memberOf simply
 * @param {simply.buttonConfig} buttonConf - An object defining the button configuration.
 */
Window.prototype._buttonConfig = function(buttonConf, auto) {
  if (buttonConf === undefined) {
    var config = {};
    for (var i = 0, ii = buttons.length; i < ii; ++i) {
      var name = buttons[i];
      config[name] = this._button.config[name];
    }
    return config;
  }
  for (var k in buttonConf) {
    if (buttons.indexOf(k) !== -1) {
      if (k === 'back') {
        this._button.configMode = buttonConf.back && !auto ? 'manual' : 'auto';
      }
      this._button.config[k] = buttonConf[k];
    }
  }
  if (simply.impl.windowButtonConfig) {
    return simply.impl.windowButtonConfig(this._button.config);
  }
};

Window.prototype.buttonConfig = function(buttonConf) {
  this._buttonConfig(buttonConf);
};

Window.prototype._buttonAutoConfig = function() {
  if (!this._button || this._button.configMode !== 'auto') {
    return;
  }
  var singleBackCount = this.listenerCount('click', 'back');
  var longBackCount = this.listenerCount('longClick', 'back');
  var useBack = singleBackCount + longBackCount > 0;
  if (useBack !== this._button.config.back) {
    this._button.config.back = useBack;
    return this._buttonConfig(this._button.config, true);
  }
};

Window.prototype._toString = function() {
  return '[' + this.constructor._codeName + ' ' + this._id() + ']';
};

Window.emit = function(type, subtype, e, klass) {
  var wind = e.window = WindowStack.top();
  if (klass) {
    e[klass._codeName] = wind;
  }
  if (wind && wind.emit(type, subtype, e) === false) {
    return false;
  }
};

/**
 * Simply.js button click event. This can either be a single click or long click.
 * Use the event type 'click' or 'longClick' to subscribe to these events.
 * @typedef simply.clickEvent
 * @property {string} button - The button that was pressed: 'back', 'up', 'select', or 'down'. This is also the event subtype.
 */

Window.emitClick = function(type, button) {
  var e = {
    button: button,
  };
  return Window.emit(type, button, e);
};

module.exports = Window;
<commit_msg>Fix Window _action to use windowActionBar<commit_after>var util2 = require('util2');
var myutil = require('myutil');
var Emitter = require('emitter');
var Accel = require('ui/accel');
var WindowStack = require('ui/windowstack');
var Propable = require('ui/propable');
var Stage = require('ui/stage');
var simply = require('ui/simply');

var buttons = [
  'back',
  'up',
  'select',
  'down',
];

/**
 * Enable fullscreen in the Pebble UI.
 * Fullscreen removes the Pebble status bar, giving slightly more vertical display height.
 * @memberOf simply
 * @param {boolean} fullscreen - Whether to enable fullscreen mode.
 */

/**
 * Enable scrolling in the Pebble UI.
 * When scrolling is enabled, up and down button presses are no longer forwarded to JavaScript handlers.
 * Single select, long select, and accel tap events are still available to you however.
 * @memberOf simply
 * @param {boolean} scrollable - Whether to enable a scrollable view.
 */

var configProps = [
  'fullscreen',
  'style',
  'scrollable',
  'backgroundColor',
];

var actionProps = [
  'up',
  'select',
  'back',
  'backgroundColor',
];

var accessorProps = configProps;

var defaults = {
  backgroundColor: 'black',
  fullscreen: false,
  scrollable: false,
};

var nextId = 1;

var Window = function(windowDef) {
  this.state = myutil.shadow(defaults, windowDef || {});
  this.state.id = nextId++;
  this._buttonInit();
  this._items = [];
  this._dynamic = true;
};

Window._codeName = 'window';

util2.copy(Emitter.prototype, Window.prototype);

util2.copy(Propable.prototype, Window.prototype);

util2.copy(Stage.prototype, Window.prototype);

Propable.makeAccessors(accessorProps, Window.prototype);

Window.prototype._id = function() {
  return this.state.id;
};

Window.prototype._hide = function(broadcast) {
  if (broadcast === false) { return; }
  simply.impl.windowHide(this._id());
};

Window.prototype.hide = function() {
  WindowStack.remove(this, true);
  return this;
};

Window.prototype._show = function(pushing) {
  this._prop(this.state, true, pushing);
  this._buttonConfig({});
  if (this._dynamic) {
    Stage.prototype._show.call(this, pushing);
  }
};

Window.prototype.show = function() {
  WindowStack.push(this);
  return this;
};

Window.prototype._insert = function() {
  if (this._dynamic) {
    Stage.prototype._insert.apply(this, arguments);
  }
};

Window.prototype._remove = function() {
  if (this._dynamic) {
    Stage.prototype._remove.apply(this, arguments);
  }
};

Window.prototype._clearAction = function() {
  actionProps.forEach(myutil.unset.bind(this, this.state.action));
};

Window.prototype._clear = function(flags) {
  flags = myutil.toFlags(flags);
  if (myutil.flag(flags, 'action')) {
    this._clearAction();
  }
};

Window.prototype.prop = function(field, value, clear) {
  if (arguments.length === 0) {
    return util2.copy(this.state);
  }
  if (arguments.length === 1 && typeof field !== 'object') {
    return this.state[field];
  }
  if (typeof field === 'object') {
    clear = value;
  }
  if (clear) {
    this._clear('all');
  }
  var windowDef = myutil.toObject(field, value);
  util2.copy(windowDef, this.state);
  this._prop(windowDef);
  return this;
};

Window.prototype._action = function(actionDef) {
  if (this === WindowStack.top()) {
    simply.impl.windowActionBar(actionDef);
  }
};

Window.prototype.action = function(field, value, clear) {
  var action = this.state.action;
  if (!action) {
    action = this.state.action = {};
  }
  if (arguments.length === 0) {
    return action;
  }
  if (arguments.length === 1 && typeof field === 'string') {
    return action[field];
  }
  if (typeof field !== 'string') {
    clear = value;
  }
  if (clear) {
    this._clear('action');
  }
  if (typeof field !== 'boolean') {
    util2.copy(myutil.toObject(field, value), this.state.action);
  }
  this._action(field);
  return this;
};

var isBackEvent = function(type, subtype) {
  return ((type === 'click' || type === 'longClick') && subtype === 'back');
};

Window.prototype.onAddHandler = function(type, subtype) {
  if (isBackEvent(type, subtype)) {
    this._buttonAutoConfig();
  }
  if (type === 'accelData') {
    Accel.autoSubscribe();
  }
};

Window.prototype.onRemoveHandler = function(type, subtype) {
  if (!type || isBackEvent(type, subtype)) {
    this._buttonAutoConfig();
  }
  if (!type || type === 'accelData') {
    Accel.autoSubscribe();
  }
};

Window.prototype._buttonInit = function() {
  this._button = {
    config: {},
    configMode: 'auto',
  };
  for (var i = 0, ii = buttons.length; i < ii; i++) {
    var button = buttons[i];
    if (button !== 'back') {
      this._button.config[buttons[i]] = true;
    }
  }
};

/**
 * The button configuration parameter for {@link simply.buttonConfig}.
 * The button configuration allows you to enable to disable buttons without having to register or unregister handlers if that is your preferred style.
 * You may also enable the back button manually as an alternative to registering a click handler with 'back' as its subtype using {@link simply.on}.
 * @typedef {object} simply.buttonConf
 * @property {boolean} [back] - Whether to enable the back button. Initializes as false. Simply.js can also automatically register this for you based on the amount of click handlers with subtype 'back'.
 * @property {boolean} [up] - Whether to enable the up button. Initializes as true. Note that this is disabled when using {@link simply.scrollable}.
 * @property {boolean} [select] - Whether to enable the select button. Initializes as true.
 * @property {boolean} [down] - Whether to enable the down button. Initializes as true. Note that this is disabled when using {@link simply.scrollable}.
 */

/**
 * Changes the button configuration.
 * See {@link simply.buttonConfig}
 * @memberOf simply
 * @param {simply.buttonConfig} buttonConf - An object defining the button configuration.
 */
Window.prototype._buttonConfig = function(buttonConf, auto) {
  if (buttonConf === undefined) {
    var config = {};
    for (var i = 0, ii = buttons.length; i < ii; ++i) {
      var name = buttons[i];
      config[name] = this._button.config[name];
    }
    return config;
  }
  for (var k in buttonConf) {
    if (buttons.indexOf(k) !== -1) {
      if (k === 'back') {
        this._button.configMode = buttonConf.back && !auto ? 'manual' : 'auto';
      }
      this._button.config[k] = buttonConf[k];
    }
  }
  if (simply.impl.windowButtonConfig) {
    return simply.impl.windowButtonConfig(this._button.config);
  }
};

Window.prototype.buttonConfig = function(buttonConf) {
  this._buttonConfig(buttonConf);
};

Window.prototype._buttonAutoConfig = function() {
  if (!this._button || this._button.configMode !== 'auto') {
    return;
  }
  var singleBackCount = this.listenerCount('click', 'back');
  var longBackCount = this.listenerCount('longClick', 'back');
  var useBack = singleBackCount + longBackCount > 0;
  if (useBack !== this._button.config.back) {
    this._button.config.back = useBack;
    return this._buttonConfig(this._button.config, true);
  }
};

Window.prototype._toString = function() {
  return '[' + this.constructor._codeName + ' ' + this._id() + ']';
};

Window.emit = function(type, subtype, e, klass) {
  var wind = e.window = WindowStack.top();
  if (klass) {
    e[klass._codeName] = wind;
  }
  if (wind && wind.emit(type, subtype, e) === false) {
    return false;
  }
};

/**
 * Simply.js button click event. This can either be a single click or long click.
 * Use the event type 'click' or 'longClick' to subscribe to these events.
 * @typedef simply.clickEvent
 * @property {string} button - The button that was pressed: 'back', 'up', 'select', or 'down'. This is also the event subtype.
 */

Window.emitClick = function(type, button) {
  var e = {
    button: button,
  };
  return Window.emit(type, button, e);
};

module.exports = Window;
<|endoftext|>


<commit_before>var JSDOCatalog = require('./metadata.js');
var FilterParser = require('../filter-parser.js');
var rsvp = require('rsvp');

function JSDOHandler(akera) {

  var self = this;

  this.akera = akera;
  this.crudHandler = akera.getAkeraHandler().getDataAccess();
  this.akeraMetadata = akera.getAkeraHandler().getMetaData();
  this.metadata = new JSDOCatalog(this.akeraMetadata);

  this.init = function(config, router) {
    self.asDataset = config.jsdo && config.jsdo.asDataset === true;
    router.get(config.route + 'jsdo/metadata', self.getCatalog);
    router.get(config.route + 'jsdo/metadata/:db', self.getCatalog);
    router.get(config.route + 'jsdo/metadata/:db/:table', self.getCatalog);
    router.get(config.route + 'jsdo/:db/:table*', self.doSelect);
    router.post(config.route + 'jsdo/:db/:table', self.doCreate);
    router.put(config.route + 'jsdo/:db/:table/count', self.doCount);
    router.put(config.route + 'jsdo/:db/:table', self.doUpdate);
    router['delete'](config.route + 'jsdo/:db/:table', self.doDelete);
  };

  this.getCatalog = function(req, res) {
    var tableName = req.params.table;
    var dbName = req.params.db;

    self.metadata.getCatalog(dbName, tableName, self.asDataset === true,
      req.broker).then(function(catalog) {
      res.status(200).json(catalog);
    }, function(err) {
      self.akera.error(err, res);
    });
  };

  this.doSelect = function(req, res) {
    var tableName = req.params.db + '.' + req.params.table;

    var filter = {};
    var where = FilterParser.convert(req.query.filter);
    var limit = null;
    var offset = null;

    try {
      var rollbaseFilter = JSON.parse(req.query.filter);

      limit = getNumber(rollbaseFilter.top);
      offset = getNumber(rollbaseFilter.skip);

      if (rollbaseFilter.orderBy) {
        filter.sort = rollbaseFilter.orderBy.split(',').map(function(sort) {
          var by = sort.trim().split(' ');

          if (by.length === 1)
            return by;

          var sortBy = {};
          sortBy[by[0]] = by[by.length - 1] === 'desc';

          return sortBy;
        });
      }
    } catch (err) {}

    if (where) {
      filter.where = where;
    }

    limit = limit || getNumber(req.query.top);
    offset = offset || getNumber(req.query.skip);

    if (limit) {
      filter.limit = parseInt(limit);
    }

    if (offset) {
      filter.offset = parseInt(offset);
    }

    if (req.query.sort) {
      if (typeof (req.query.sort) === 'string' && req.query.sort !== '') {
        try {
          req.query.sort = JSON.parse(req.query.sort);
        } catch (e) {
          filter.sort = {
            field : req.query.sort
          };
        }
      }
      if (req.query.sort instanceof Array) {
        if (req.query.sort.length > 0) {
          filter.sort = req.query.sort.map(function(sortCriteria) {
            var sortCondition = {};
            sortCondition[sortCriteria.field] = sortCriteria.dir !== 'asc';
            return sortCondition;
          });
        }
      }
    }

    if (!self.asDataset) {
      _getPkFromQueryString(req).then(function(pkMap) {
        filter.pk = pkMap;
        return self.crudHandler.read(req.broker, tableName, filter);
      }).then(function(rows) {
        _sendReadResponse(rows, req, res);
      })['catch'](function(err) {
        self.akera.error(err, res);
      });
    } else {
      self.crudHandler.read(req.broker, tableName, filter).then(function(rows) {
        _sendReadResponse(rows, req, res);
      })['catch'](function(err) {
        self.akera.error(err, res);
      });
    }
  };

  this.doCreate = function(req, res) {
    if (req.body) {
      var tableName = req.params.db + '.' + req.params.table;

      delete req.body._id;
      var newObject;
      try {
        newObject = self.asDataset === true ? _getDatasetRow(req, 'created')
          : req.body;
      } catch (e) {
        return self.akera.error(e, res);
      }
      self.crudHandler.create(req.broker, tableName, newObject).then(
        function(row) {
          _sendReadResponse(row, req, res);
        }, function(err) {
          self.akera.error(err, res);
        });
    } else {
      self.akera.error(new Error('No data provided'), res);
    }
  };

  this.doUpdate = function(req, res) {
    if (req.body) {
      var tableName = req.params.db + '.' + req.params.table;

      delete req.body._id;
      var pkFn = self.asDataset ? _getPkFromBeforeImage : _getPkFromQueryString;
      pkFn(req).then(
        function(pkMap) {
          return self.crudHandler.update(req.broker, tableName, pkMap,
            self.asDataset ? _getDatasetRow(req, 'modified') : req.body);
        }).then(function(rows) {
        _sendReadResponse(rows, req, res);
      })['catch'](function(err) {
        self.akera.error(err, res);
      });
    }
  };

  this.doDelete = function(req, res) {
    var pkFn = self.asDataset ? _getPkFromBeforeImage : _getPkFromQueryString;
    var tableName = req.params.db + '.' + req.params.table;

    pkFn(req).then(function(pkMap) {
      return self.crudHandler.destroy(req.broker, tableName, pkMap);
    }).then(function(result) {
      res.status(200).json(result);
    })['catch'](function(err) {
      self.akera.error(err, res);
    });
  };

  this.doCount = function(req, res) {
    var tableName = req.params.db + '.' + req.params.table;
    var where = FilterParser.convert(req.query.filter);
    var filter = {
      count : true
    };

    if (where) {
      filter.where = where;
    }

    self.crudHandler.read(req.broker, tableName, filter).then(function(count) {
      res.status(200).json({
        response : {
          numRecs : count
        }
      });
    }, function(err) {
      self.akera.error(err, res);
    });
  };

  function getNumber(value) {
    if (typeof value === 'number')
      return value;

    if (typeof value === 'string') {
      value = value.trim();

      if (value.length > 0 && !isNaN(value))
        return parseFloat(value);
    }
  }

  function _getDatasetRow(req, state) {
    var tableName = req.params.table;
    var ds = req.body['ds' + tableName];
    var tts = ds && ds[tableName];

    if (tts instanceof Array) {
      var rows = tts.filter(function(row) {
        return row['prods:rowState'] === state;
      });

      if (rows.length !== 1) {
        if (rows.length > 1)
          throw new Error('More than one record sent in request.');
        else
          throw new Error('No record found in request.');
      }

      var data = rows[0];

      Object.keys(data).forEach(function(field) {
        if (field.startsWith('prods:'))
          delete data[field];
      });

      return data;
    }

    // rollbase doesn't care about before image settings, it just send the data
    if (req.body instanceof Array) {
      if (req.body.length !== 1) {
        if (req.body.length > 1)
          throw new Error('More than one record sent in request.');
        else
          throw new Error('No record found in request.');
      }

      return req.body[0];
    }

    throw new Error('Invalid table name or invalid request body specified.');

  }

  function _getPkFromQueryString(req) {
    return new rsvp.Promise(function(resolve, reject) {
      var pkString = req.params[0];
      if (!pkString)
        return resolve();
      if (pkString.charAt(pkString.length - 1) === '/') {
        pkString = pkString.substring(0, pkString.length - 1);
      }
      if (pkString.charAt(0) === '/') {
        pkString = pkString.substring(1, pkString.length);
      }
      var pk = pkString.split('/');

      _getPrimaryKey(req.broker, req.params.db, req.params.table).then(
        function(primaryKey) {
          if (pk.length !== primaryKey.length) {
            reject(new Error('Invalid primary key values'));
          }
          var pkMap = {};
          for ( var i in pk) {
            pkMap[primaryKey[i]] = pk[i];
          }
          resolve(pkMap);
        }, reject);
    });
  }

  function _getPrimaryKey(broker, db, table) {
    return new rsvp.Promise(function(resolve, reject) {
      self.akeraMetadata.getTable(broker, db, table).then(function(meta) {
        resolve(meta.pk);
      }, reject);
    });
  }

  function _getPkFromBeforeImage(req) {
    return new rsvp.Promise(function(resolve, reject) {
      var tableName = req.params.table;
      var before = req.body['ds' + tableName]['prods:before'][tableName][0];
      _getPrimaryKey(req.broker, req.params.db, tableName).then(
        function(primaryKey) {
          var pkMap = {};
          for ( var i in primaryKey) {
            pkMap[primaryKey[i]] = before[primaryKey[i]];
          }
          resolve(pkMap);
        }, reject);
    });
  }

  function _sendReadResponse(rows, req, res) {
    var tableName = req.params.table;
    var result = {};

    if (self.asDataset === true) {
      var ds = {};

      ds[tableName] = rows instanceof Array ? rows : [ rows ];
      result['ds' + tableName] = ds;
    } else {
      result[tableName] = rows instanceof Array ? rows : [ rows ];
    }

    res.status(200).json(result);
  }

}

module.exports = JSDOHandler;
<commit_msg>use temp-table data (no before image) for rollbase updates<commit_after>var JSDOCatalog = require('./metadata.js');
var FilterParser = require('../filter-parser.js');
var rsvp = require('rsvp');

function JSDOHandler(akera) {

  var self = this;

  this.akera = akera;
  this.crudHandler = akera.getAkeraHandler().getDataAccess();
  this.akeraMetadata = akera.getAkeraHandler().getMetaData();
  this.metadata = new JSDOCatalog(this.akeraMetadata);

  this.init = function(config, router) {
    self.asDataset = config.jsdo && config.jsdo.asDataset === true;
    router.get(config.route + 'jsdo/metadata', self.getCatalog);
    router.get(config.route + 'jsdo/metadata/:db', self.getCatalog);
    router.get(config.route + 'jsdo/metadata/:db/:table', self.getCatalog);
    router.get(config.route + 'jsdo/:db/:table*', self.doSelect);
    router.post(config.route + 'jsdo/:db/:table', self.doCreate);
    router.put(config.route + 'jsdo/:db/:table/count', self.doCount);
    router.put(config.route + 'jsdo/:db/:table', self.doUpdate);
    router['delete'](config.route + 'jsdo/:db/:table', self.doDelete);
  };

  this.getCatalog = function(req, res) {
    var tableName = req.params.table;
    var dbName = req.params.db;

    self.metadata.getCatalog(dbName, tableName, self.asDataset === true,
      req.broker).then(function(catalog) {
      res.status(200).json(catalog);
    }, function(err) {
      self.akera.error(err, res);
    });
  };

  this.doSelect = function(req, res) {
    var tableName = req.params.db + '.' + req.params.table;

    var filter = {};
    var where = FilterParser.convert(req.query.filter);
    var limit = null;
    var offset = null;

    try {
      var rollbaseFilter = JSON.parse(req.query.filter);

      limit = getNumber(rollbaseFilter.top);
      offset = getNumber(rollbaseFilter.skip);

      if (rollbaseFilter.orderBy) {
        filter.sort = rollbaseFilter.orderBy.split(',').map(function(sort) {
          var by = sort.trim().split(' ');

          if (by.length === 1)
            return by;

          var sortBy = {};
          sortBy[by[0]] = by[by.length - 1] === 'desc';

          return sortBy;
        });
      }
    } catch (err) {}

    if (where) {
      filter.where = where;
    }

    limit = limit || getNumber(req.query.top);
    offset = offset || getNumber(req.query.skip);

    if (limit) {
      filter.limit = parseInt(limit);
    }

    if (offset) {
      filter.offset = parseInt(offset);
    }

    if (req.query.sort) {
      if (typeof (req.query.sort) === 'string' && req.query.sort !== '') {
        try {
          req.query.sort = JSON.parse(req.query.sort);
        } catch (e) {
          filter.sort = {
            field : req.query.sort
          };
        }
      }
      if (req.query.sort instanceof Array) {
        if (req.query.sort.length > 0) {
          filter.sort = req.query.sort.map(function(sortCriteria) {
            var sortCondition = {};
            sortCondition[sortCriteria.field] = sortCriteria.dir !== 'asc';
            return sortCondition;
          });
        }
      }
    }

    if (!self.asDataset) {
      _getPkFromQueryString(req).then(function(pkMap) {
        filter.pk = pkMap;
        return self.crudHandler.read(req.broker, tableName, filter);
      }).then(function(rows) {
        _sendReadResponse(rows, req, res);
      })['catch'](function(err) {
        self.akera.error(err, res);
      });
    } else {
      self.crudHandler.read(req.broker, tableName, filter).then(function(rows) {
        _sendReadResponse(rows, req, res);
      })['catch'](function(err) {
        self.akera.error(err, res);
      });
    }
  };

  this.doCreate = function(req, res) {
    if (req.body) {
      var tableName = req.params.db + '.' + req.params.table;

      delete req.body._id;
      var newObject;
      try {
        newObject = self.asDataset === true ? _getDatasetRow(req, 'created')
          : req.body;
      } catch (e) {
        return self.akera.error(e, res);
      }
      self.crudHandler.create(req.broker, tableName, newObject).then(
        function(row) {
          _sendReadResponse(row, req, res);
        }, function(err) {
          self.akera.error(err, res);
        });
    } else {
      self.akera.error(new Error('No data provided'), res);
    }
  };

  this.doUpdate = function(req, res) {
    if (req.body) {
      var tableName = req.params.db + '.' + req.params.table;

      delete req.body._id;
      var pkFn = self.asDataset ? _getPkFromBeforeImage : _getPkFromQueryString;
      pkFn(req).then(
        function(pkMap) {
          return self.crudHandler.update(req.broker, tableName, pkMap,
            self.asDataset ? _getDatasetRow(req, 'modified') : req.body);
        }).then(function(rows) {
        _sendReadResponse(rows, req, res);
      })['catch'](function(err) {
        self.akera.error(err, res);
      });
    }
  };

  this.doDelete = function(req, res) {
    var pkFn = self.asDataset ? _getPkFromBeforeImage : _getPkFromQueryString;
    var tableName = req.params.db + '.' + req.params.table;

    pkFn(req).then(function(pkMap) {
      return self.crudHandler.destroy(req.broker, tableName, pkMap);
    }).then(function(result) {
      res.status(200).json(result);
    })['catch'](function(err) {
      self.akera.error(err, res);
    });
  };

  this.doCount = function(req, res) {
    var tableName = req.params.db + '.' + req.params.table;
    var where = FilterParser.convert(req.query.filter);
    var filter = {
      count : true
    };

    if (where) {
      filter.where = where;
    }

    self.crudHandler.read(req.broker, tableName, filter).then(function(count) {
      res.status(200).json({
        response : {
          numRecs : count
        }
      });
    }, function(err) {
      self.akera.error(err, res);
    });
  };

  function getNumber(value) {
    if (typeof value === 'number')
      return value;

    if (typeof value === 'string') {
      value = value.trim();

      if (value.length > 0 && !isNaN(value))
        return parseFloat(value);
    }
  }

  function _getDatasetRow(req, state) {
    var tableName = req.params.table;
    var ds = req.body['ds' + tableName];
    var tts = ds && ds[tableName];

    if (tts instanceof Array) {
      var rows = tts.filter(function(row) {
        return row['prods:rowState'] === state;
      });

      if (rows.length !== 1) {
        if (rows.length > 1)
          throw new Error('More than one record sent in request.');
        else
          throw new Error('No record found in request.');
      }

      var data = rows[0];

      Object.keys(data).forEach(function(field) {
        if (field.startsWith('prods:'))
          delete data[field];
      });

      return data;
    }

    // rollbase doesn't care about before image settings, it just send the data
    if (req.body instanceof Array) {
      if (req.body.length !== 1) {
        if (req.body.length > 1)
          throw new Error('More than one record sent in request.');
        else
          throw new Error('No record found in request.');
      }

      return req.body[0];
    }

    throw new Error('Invalid table name or invalid request body specified.');

  }

  function _getPkFromQueryString(req) {
    return new rsvp.Promise(function(resolve, reject) {
      var pkString = req.params[0];
      if (!pkString)
        return resolve();
      if (pkString.charAt(pkString.length - 1) === '/') {
        pkString = pkString.substring(0, pkString.length - 1);
      }
      if (pkString.charAt(0) === '/') {
        pkString = pkString.substring(1, pkString.length);
      }
      var pk = pkString.split('/');

      _getPrimaryKey(req.broker, req.params.db, req.params.table).then(
        function(primaryKey) {
          if (pk.length !== primaryKey.length) {
            reject(new Error('Invalid primary key values'));
          }
          var pkMap = {};
          for ( var i in pk) {
            pkMap[primaryKey[i]] = pk[i];
          }
          resolve(pkMap);
        }, reject);
    });
  }

  function _getPrimaryKey(broker, db, table) {
    return new rsvp.Promise(function(resolve, reject) {
      self.akeraMetadata.getTable(broker, db, table).then(function(meta) {
        resolve(meta.pk);
      }, reject);
    });
  }

  function _getPkFromBeforeImage(req) {
    return new rsvp.Promise(
      function(resolve, reject) {
        var tableName = req.params.table;
        var before = req.body['ds' + tableName] ? req.body['ds' + tableName]['prods:before'][tableName][0]
          : req.body[0];

        console.log(before);
        _getPrimaryKey(req.broker, req.params.db, tableName).then(
          function(primaryKey) {
            var pkMap = {};
            for ( var i in primaryKey) {
              pkMap[primaryKey[i]] = before[primaryKey[i]];
            }
            console.log('pk', pkMap);
            resolve(pkMap);
          }, reject);
      });
  }

  function _sendReadResponse(rows, req, res) {
    var tableName = req.params.table;
    var result = {};

    if (self.asDataset === true) {
      var ds = {};

      ds[tableName] = rows instanceof Array ? rows : [ rows ];
      result['ds' + tableName] = ds;
    } else {
      result[tableName] = rows instanceof Array ? rows : [ rows ];
    }

    res.status(200).json(result);
  }

}

module.exports = JSDOHandler;
<|endoftext|>


<commit_before>/*
 * ------------------------------------------
 * XDR控件实现文件
 * @version  1.0
 * @author   genify(caijf@corp.netease.com)
 * ------------------------------------------
 */
var f = function(){
    // variable declaration
    var _  = NEJ.P,
        _o = NEJ.O,
        _f = NEJ.F,
        _h = _('nej.h'),
        _g = _('nej.g'),
        _u = _('nej.u'),
        _j = _('nej.j'),
        _p = _('nej.ut.j'),
        // sn:{req:proxy,onload:function(){},onerror:function(){}}
        _xcache = {},
        _doFilter = _f;
    /**
     * 中断请求

     * 脚本举例
     * [code]
     *   var _j = NEJ.P('nej.j');
     *   var _reqID = _j._$request('http://123.163.com/xhr/',{
     *          type:'json',
     *          method:'POST',
     *          data:{name:'cheng-lin'},
     *          timeout:60000,
     *          onload:function(_data){
     *          },
     *          onerror:function(_error){
     *          }
     *      }
     *   );
     *   // 1秒后中断掉这个请求
     *   setTimeout(function(){
     *     _j._$abort(_reqID);
     *   },1000);
     * [/code]
     * @api    {nej.j._$abort}
     * @param  {String} 请求标识
     * @return {nej.j}
     */
    _j._$abort = function(_sn){
        var _cache = _xcache[_sn];
        if (!_cache) return this;
        _cache.req._$abort();
        return this;
    };
    /**
     * 全局请求过滤器，过滤器中可以通过设置输入事件对象的stopped值阻止继续回调

     * 脚本举例
     * [code]
     *   var _j = NEJ.P('nej.j');
     *   _j._$filter(function(_event){
     *          // 过滤掉404的异常，如果type是onload不做处理
     *      if(_event.type == 'onerror'){
     *          if(_event.result.data == 404)
     *              _event.stopped = false;
     *      }
     *  });
     *  _j._$request('xxxx',{
     *      type:'json',
     *      method:'POST',
     *      data:{name:'cheng-lin'},
     *      timeout:3000,
     *      onload:function(_data){
     *          // TODO
     *      },
     *      onerror:function(_error){
     *          // TODO
     *      }
     *  });
     * [/code]
     *
     * @api    {nej.j._$filter}
     * @param  {Function} 过滤器
     * @return {nej.j}
     */
    _j._$filter = function(_filter){
        _doFilter = _filter||_f;
        return this;
    };
    /**
     * 发送ajax请求

     * 脚本举例
     * [code]
     *   var _url = 'http://123.163.com/webmail/dwr/call/plaincall/DownloadBean.getDownloadUrlByBrandAndModel.dwr';
     *   var _j = NEJ.P('nej.j');
     *   var _requestId = _j._$request(_url,{
     *          sync:true,
     *          type:'json',
     *          data:'hello',
     *          query:'a=1&b=2',
     *          method:'post',
     *          timeout:3000,
     *          mode:0||1||2||3,
     *          onload:function(_data){
     *              // 正常回调处理
     *          },
     *          onerror:function(_error){
     *              // 异常处理
     *          },
     *          onbeforerequest:function(_data){
     *              // 请求发送前，对请求数据处理
     *          }
     *      });
     * [/code]
     * @api    {nej.j._$request}
     * @param  {String}   请求地址
     * @param  {Object}   配置参数
     * @config {Boolean}  sync     是否同步请求
     * @config {String}   type     返回数据格式,text/json/xml
     * @config {Variable} data     要发送的数据
     * @config {Variable} query    查询参数,字符串格式a=b&c=d,对象格式{a:'b',c:'d'}
     * @config {String}   method   请求方式,GET/POST
     * @config {Number}   timeout  超时时间,0 禁止超时监测
     * @config {Object}   headers  头信息表
     * @config {Boolean}  cookie   跨域请求是否带cookie，仅对CORS方式有效
     * @config {Number}   mode     请求模式,针对跨域请求采用的请求方式

     *                             0 - 自动模式，高版本使用HTML5的CORS协议，低版本采用Frame代理方式

     *                             1 - 高版本使用HTML5的CORS协议，低版本采用Flash代理方式

     *                             2 - 全部使用Frame代理方式

     *                             3 - 全部使用Flash代理方式
     * @config {Object}   result  onload回调输入时需包含的额外信息，已处理额外数据
     *                            headers  服务器返回头信息，如{headers:'x-res-0'}或者{headers:['x-res-0','x-res-1']}
     * @return {String} 分配给请求的ID
     *
     * [hr]
     * 载入回调
     * @event  {onload}
     * @param  {Variable|Object} 请求返回数据，根据请求时type指定格式返回，
     *                           如果请求时指定了result参数，则此处输入为包含额外信息的对象，
     *                           数据结果从此对象的data属性中取，如{headers:{'x-res-0':'12345', ...},data:{a:'aaa', ...}}
     *
     * [hr]
     * 出错回调
     * @event  {onerror}
     * @param  {Object}   错误信息
     * @config {Number}   code    错误代码
     * @config {String}   message 错误描述
     * @config {Variable} data    出错时携带数据
     *
     * [hr]
     * 请求之前对数据处理回调
     * @event  {onbeforerequest}
     * @param  {Object} 数据对象
     *
     */
    _j._$request = (function(){
        var _location = (location.protocol+'//'
                        +location.host).toLowerCase();
        // check cross-domain request
        var _isXDomain = function(_url){
            var _origin = _u._$url2origin(_url);
            return !!_origin&&_origin!=_location;
        };
        // check file upload
        var _isUpload = function(_headers){
            return (_headers||_o)[_g._$HEAD_CT]==_g._$HEAD_CT_FILE;
        };
        // get ajax proxy
        var _getProxy = function(_options){
            var _upload = _isUpload(_options.headers);
            if (!_isXDomain(_options.url)&&!_upload)
                return _p._$$XHRProxy._$allocate(_options);
            return _h.__getProxyByMode(_options.mode,_upload,_options);
        };
        // parse ext result
        var _doParseExtData = function(_cache,_result){
            var _data = {
                data:_result
            };
            // parse ext headers
            var _keys = _cache.result.headers;
            if (!!_keys){
                _data.headers = _cache.req._$header(_keys);
            }
            // TODO parse other ext data
            return _data;
        };
        // clear cache
        var _doClear = function(_sn){
            var _cache = _xcache[_sn];
            if (!_cache) return;
            if (!!_cache.req)
                _cache.req._$recycle();
            delete _xcache[_sn];
        };
        // do callback
        var _doCallback = function(_sn,_type){
            var _cache = _xcache[_sn];
            if (!_cache) return;
            var _data = arguments[2];
            if (_type=='onload'&&!!_cache.result){
                _data = _doParseExtData(_cache,_data);
            }
            _doClear(_sn);
            try{
                var _event = {
                    type:_type,
                    result:_data
                };
                _doFilter(_event);
                if (!_event.stopped){
                   (_cache[_type]||_f)(_event.result);
                }
            }catch(ex){
                // ignore
                console.error(ex.message);
                console.error(ex);
            }
        };
        // onload callback
        var _onLoad = function(_sn,_data){
            _doCallback(_sn,'onload',_data);
        };
        // onerror callback
        var _onError = function(_sn,_error){
            _doCallback(_sn,'onerror',_error);
        };
        // function body
        return function(_url,_options){
            _options = _options||{};
            // cache request callback
            var _sn = _u._$uniqueID(),
                _cache = {
                    result:_options.result,
                    onload:_options.onload||_f,
                    onerror:_options.onerror||_f
                };
            _xcache[_sn] = _cache;
            _options.onload = _onLoad._$bind(null,_sn);
            _options.onerror = _onError._$bind(null,_sn);
            // append request query
            if (!!_options.query){
                var _sep = _url.indexOf('?')<0?'?':'&',
                    _query = _options.query;
                if (_u._$isObject(_query))
                    _query = _u._$object2query(_query);
                if (!!_query) _url += _sep+_query;
            }
            _options.url = _url;
            _cache.req = _getProxy(_options);
            _cache.req._$send(_options.data);
            return _sn;
        };
    })();
    /**
     * 文件上传

     * 页面结构举例
     * [code type="html"]
     *   <form id="upload" name="upload" action="http://123.163.com:3000/xhr/uploadCallback">
     *      <input type="text" id="progress" />
     *      <input type="hidden" name="nej_mode" value="2" />
     *      <input type="hidden" name="nej_query" value="http://123.163.com:3000/xhr/progress" />
     *   </form>
     * [/code]
     * 脚本举例
     * [code]
     *   var _j = NEJ.P('nej.j');
     *   var _upload = this._e._$get('upload');
     *   var _progress = this._e._$get('progress');
     *   _j._$upload(_upload,{
     *   mode:2,
     *   cookie:true,
     *   onuploading:function(_data){
     *       // 后台处理http://123.163.com:3000/xhr/progress，返回一个json对象
     *       // 前台会去轮询此接口获取进度
     *       if(!!_data.total&&_data.progress){
     *           _progress.value = _data.progress;
     *       }
     *   },
     *   onload:function(_url){
     *       // 此前会把进度轮询终止掉。如果要显示进度100%，可在此设置一次
     *       // 后台处理http://123.163.com:3000/xhr/uploadCallback，返回url
     *       // 文件上传完成的回调,url为返回的地址
     *   }});
     * [/code]
     * @api    {nej.j._$upload}
     * @param  {HTMLFormElement}   表单对象，待上传的文件及目标地址信息封装在此对象中
     * @param  {Object}            可选配置参数,已处理参数列表如下：
     * @config {String}   type     返回数据格式
     * @config {Variable} query    查询参数
     * @config {Number}   mode     跨域类型，0/2，见_$request接口说明
     * @config {Object}   headers  头信息
     * @config {Boolean}  cookie   跨域请求是否带cookie，仅对CORS方式有效
     * @return {String}            分配给请求的ID
     *
     * [hr]
     *
     * @event  {onload}   载入回调
     * @param  {Variable} 请求返回数据，根据请求时type指定格式返回
     *
     * [hr]
     *
     * @event  {onerror}  出错回调
     * @param  {Object}   错误信息
     * @config {Number}   code    错误代码
     * @config {String}   message 错误描述
     * @config {Variable} data    出错时携带数据
     *
     * [hr]
     *
     * @event  {onuploading} 上传进度回调
     * @param  {Object} 数据对象
     *
     * [hr]
     *
     * @event  {onbeforerequest} 请求之前对数据处理回调
     * @param  {Object} 数据对象
     *
     */
    _j._$upload = function(_form,_options){
        var _option = {
                mode:0,
                type:'json',
                query:null,
                cookie:!1,
                headers:{},
                onload:null,
                onerror:null,
                onuploading:null,
                onbeforerequest:null
            };
        NEJ.EX(_option,_options);
        _option.data = _form;
        _option.method = 'POST';
        _option.timeout = 0;
        _option.headers[_g._$HEAD_CT] =
                        _g._$HEAD_CT_FILE;
        return _j._$request(_form.action,_option);
    };
};
NEJ.define(
    '{lib}util/ajax/xdr.js',[
    '{lib}base/constant.js',
    '{lib}base/util.js',
    '{patch}ajax.js'
],f);<commit_msg>comment<commit_after>/*
 * ------------------------------------------
 * XDR控件实现文件
 * @version  1.0
 * @author   genify(caijf@corp.netease.com)
 * ------------------------------------------
 */
var f = function(){
    // variable declaration
    var _  = NEJ.P,
        _o = NEJ.O,
        _f = NEJ.F,
        _h = _('nej.h'),
        _g = _('nej.g'),
        _u = _('nej.u'),
        _j = _('nej.j'),
        _p = _('nej.ut.j'),
        // sn:{req:proxy,onload:function(){},onerror:function(){}}
        _xcache = {},
        _doFilter = _f;
    /**
     * 中断请求

     * 脚本举例
     * [code]
     *   var _j = NEJ.P('nej.j');
     *   var _reqID = _j._$request('http://123.163.com/xhr/',{
     *          type:'json',
     *          method:'POST',
     *          data:{name:'cheng-lin'},
     *          timeout:60000,
     *          onload:function(_data){
     *          },
     *          onerror:function(_error){
     *          }
     *      }
     *   );
     *   // 1秒后中断掉这个请求
     *   setTimeout(function(){
     *     _j._$abort(_reqID);
     *   },1000);
     * [/code]
     * @api    {nej.j._$abort}
     * @param  {String} 请求标识
     * @return {nej.j}
     */
    _j._$abort = function(_sn){
        var _cache = _xcache[_sn];
        if (!_cache) return this;
        _cache.req._$abort();
        return this;
    };
    /**
     * 全局请求过滤器，过滤器中可以通过设置输入事件对象的stopped值阻止继续回调

     * 脚本举例
     * [code]
     *   var _j = NEJ.P('nej.j');
     *   _j._$filter(function(_event){
     *      // 过滤掉404的异常，如果type是onload不做处理
     *      if(_event.type == 'onerror'){
     *          if(_event.result.data == 404)
     *              _event.stopped = false;
     *      }
     *  });
     *  _j._$request('xxxx',{
     *      type:'json',
     *      method:'POST',
     *      data:{name:'cheng-lin'},
     *      timeout:3000,
     *      onload:function(_data){
     *          // TODO
     *      },
     *      onerror:function(_error){
     *          // TODO
     *      }
     *  });
     * [/code]
     *
     * @api    {nej.j._$filter}
     * @param  {Function} 过滤器
     * @return {nej.j}
     */
    _j._$filter = function(_filter){
        _doFilter = _filter||_f;
        return this;
    };
    /**
     * 发送ajax请求

     * 脚本举例
     * [code]
     *   var _url = 'http://123.163.com/webmail/dwr/call/plaincall/DownloadBean.getDownloadUrlByBrandAndModel.dwr';
     *   var _j = NEJ.P('nej.j');
     *   var _requestId = _j._$request(_url,{
     *          sync:true,
     *          type:'json',
     *          data:'hello',
     *          query:'a=1&b=2',
     *          method:'post',
     *          timeout:3000,
     *          mode:0||1||2||3,
     *          onload:function(_data){
     *              // 正常回调处理
     *          },
     *          onerror:function(_error){
     *              // 异常处理
     *          },
     *          onbeforerequest:function(_data){
     *              // 请求发送前，对请求数据处理
     *          }
     *      });
     * [/code]
     * @api    {nej.j._$request}
     * @param  {String}   请求地址
     * @param  {Object}   配置参数
     * @config {Boolean}  sync     是否同步请求
     * @config {String}   type     返回数据格式,text/json/xml
     * @config {Variable} data     要发送的数据
     * @config {Variable} query    查询参数,字符串格式a=b&c=d,对象格式{a:'b',c:'d'}
     * @config {String}   method   请求方式,GET/POST
     * @config {Number}   timeout  超时时间,0 禁止超时监测
     * @config {Object}   headers  头信息表
     * @config {Boolean}  cookie   跨域请求是否带cookie，仅对CORS方式有效
     * @config {Number}   mode     请求模式,针对跨域请求采用的请求方式

     *                             0 - 自动模式，高版本使用HTML5的CORS协议，低版本采用Frame代理方式

     *                             1 - 高版本使用HTML5的CORS协议，低版本采用Flash代理方式

     *                             2 - 全部使用Frame代理方式

     *                             3 - 全部使用Flash代理方式
     * @config {Object}   result  onload回调输入时需包含的额外信息，已处理额外数据
     *                            headers  服务器返回头信息，如{headers:'x-res-0'}或者{headers:['x-res-0','x-res-1']}
     * @return {String} 分配给请求的ID
     *
     * [hr]
     * 载入回调
     * @event  {onload}
     * @param  {Variable|Object} 请求返回数据，根据请求时type指定格式返回，
     *                           如果请求时指定了result参数，则此处输入为包含额外信息的对象，
     *                           数据结果从此对象的data属性中取，如{headers:{'x-res-0':'12345', ...},data:{a:'aaa', ...}}
     *
     * [hr]
     * 出错回调
     * @event  {onerror}
     * @param  {Object}   错误信息
     * @config {Number}   code    错误代码
     * @config {String}   message 错误描述
     * @config {Variable} data    出错时携带数据
     *
     * [hr]
     * 请求之前对数据处理回调
     * @event  {onbeforerequest}
     * @param  {Object} 数据对象
     *
     */
    _j._$request = (function(){
        var _location = (location.protocol+'//'
                        +location.host).toLowerCase();
        // check cross-domain request
        var _isXDomain = function(_url){
            var _origin = _u._$url2origin(_url);
            return !!_origin&&_origin!=_location;
        };
        // check file upload
        var _isUpload = function(_headers){
            return (_headers||_o)[_g._$HEAD_CT]==_g._$HEAD_CT_FILE;
        };
        // get ajax proxy
        var _getProxy = function(_options){
            var _upload = _isUpload(_options.headers);
            if (!_isXDomain(_options.url)&&!_upload)
                return _p._$$XHRProxy._$allocate(_options);
            return _h.__getProxyByMode(_options.mode,_upload,_options);
        };
        // parse ext result
        var _doParseExtData = function(_cache,_result){
            var _data = {
                data:_result
            };
            // parse ext headers
            var _keys = _cache.result.headers;
            if (!!_keys){
                _data.headers = _cache.req._$header(_keys);
            }
            // TODO parse other ext data
            return _data;
        };
        // clear cache
        var _doClear = function(_sn){
            var _cache = _xcache[_sn];
            if (!_cache) return;
            if (!!_cache.req)
                _cache.req._$recycle();
            delete _xcache[_sn];
        };
        // do callback
        var _doCallback = function(_sn,_type){
            var _cache = _xcache[_sn];
            if (!_cache) return;
            var _data = arguments[2];
            if (_type=='onload'&&!!_cache.result){
                _data = _doParseExtData(_cache,_data);
            }
            _doClear(_sn);
            try{
                var _event = {
                    type:_type,
                    result:_data
                };
                _doFilter(_event);
                if (!_event.stopped){
                   (_cache[_type]||_f)(_event.result);
                }
            }catch(ex){
                // ignore
                console.error(ex.message);
                console.error(ex);
            }
        };
        // onload callback
        var _onLoad = function(_sn,_data){
            _doCallback(_sn,'onload',_data);
        };
        // onerror callback
        var _onError = function(_sn,_error){
            _doCallback(_sn,'onerror',_error);
        };
        // function body
        return function(_url,_options){
            _options = _options||{};
            // cache request callback
            var _sn = _u._$uniqueID(),
                _cache = {
                    result:_options.result,
                    onload:_options.onload||_f,
                    onerror:_options.onerror||_f
                };
            _xcache[_sn] = _cache;
            _options.onload = _onLoad._$bind(null,_sn);
            _options.onerror = _onError._$bind(null,_sn);
            // append request query
            if (!!_options.query){
                var _sep = _url.indexOf('?')<0?'?':'&',
                    _query = _options.query;
                if (_u._$isObject(_query))
                    _query = _u._$object2query(_query);
                if (!!_query) _url += _sep+_query;
            }
            _options.url = _url;
            _cache.req = _getProxy(_options);
            _cache.req._$send(_options.data);
            return _sn;
        };
    })();
    /**
     * 文件上传

     * 页面结构举例
     * [code type="html"]
     *   <form id="upload" name="upload" action="http://123.163.com:3000/xhr/uploadCallback">
     *      <input type="text" id="progress" />
     *      <input type="hidden" name="nej_mode" value="2" />
     *      <input type="hidden" name="nej_query" value="http://123.163.com:3000/xhr/progress" />
     *   </form>
     * [/code]
     * 脚本举例
     * [code]
     *   var _j = NEJ.P('nej.j');
     *   var _upload = this._e._$get('upload');
     *   var _progress = this._e._$get('progress');
     *   _j._$upload(_upload,{
     *   mode:2,
     *   cookie:true,
     *   onuploading:function(_data){
     *       // 后台处理http://123.163.com:3000/xhr/progress，返回一个json对象
     *       // 前台会去轮询此接口获取进度
     *       if(!!_data.total&&_data.progress){
     *           _progress.value = _data.progress;
     *       }
     *   },
     *   onload:function(_url){
     *       // 此前会把进度轮询终止掉。如果要显示进度100%，可在此设置一次
     *       // 后台处理http://123.163.com:3000/xhr/uploadCallback，返回url
     *       // 文件上传完成的回调,url为返回的地址
     *   }});
     * [/code]
     * @api    {nej.j._$upload}
     * @param  {HTMLFormElement}   表单对象，待上传的文件及目标地址信息封装在此对象中
     * @param  {Object}            可选配置参数,已处理参数列表如下：
     * @config {String}   type     返回数据格式
     * @config {Variable} query    查询参数
     * @config {Number}   mode     跨域类型，0/2，见_$request接口说明
     * @config {Object}   headers  头信息
     * @config {Boolean}  cookie   跨域请求是否带cookie，仅对CORS方式有效
     * @return {String}            分配给请求的ID
     *
     * [hr]
     *
     * @event  {onload}   载入回调
     * @param  {Variable} 请求返回数据，根据请求时type指定格式返回
     *
     * [hr]
     *
     * @event  {onerror}  出错回调
     * @param  {Object}   错误信息
     * @config {Number}   code    错误代码
     * @config {String}   message 错误描述
     * @config {Variable} data    出错时携带数据
     *
     * [hr]
     *
     * @event  {onuploading} 上传进度回调
     * @param  {Object} 数据对象
     *
     * [hr]
     *
     * @event  {onbeforerequest} 请求之前对数据处理回调
     * @param  {Object} 数据对象
     *
     */
    _j._$upload = function(_form,_options){
        var _option = {
                mode:0,
                type:'json',
                query:null,
                cookie:!1,
                headers:{},
                onload:null,
                onerror:null,
                onuploading:null,
                onbeforerequest:null
            };
        NEJ.EX(_option,_options);
        _option.data = _form;
        _option.method = 'POST';
        _option.timeout = 0;
        _option.headers[_g._$HEAD_CT] =
                        _g._$HEAD_CT_FILE;
        return _j._$request(_form.action,_option);
    };
};
NEJ.define(
    '{lib}util/ajax/xdr.js',[
    '{lib}base/constant.js',
    '{lib}base/util.js',
    '{patch}ajax.js'
],f);<|endoftext|>


<commit_before>var mongoose = require("mongoose");
var request_schema = mongoose.Schema({
	email: String,
	start_date: Date,
	end_date: Date,
	timestamp: {type: Date, default: Date.now},
	is_pending: Boolean,
	is_approved: Boolean,
	description: String,
	comments: [{
		email: String,
		content: String,
		timestamp: {type: Date, default: Date.now}
	}]
});
modules.exports = mongoose.model("request",request_schema);<commit_msg>last commit in this branch switching to some_requests<commit_after>var mongoose = require("mongoose");
var request_schema = mongoose.Schema({
	email: String,
	start_date: Date,
	end_date: Date,
	timestamp: {type: Date, default: Date.now},
	is_pending: Boolean,
	is_approved: Boolean,
	description: String,
	comments: [{
		email: String,
		content: String,
		timestamp: {type: Date, default: Date.now}
	}]
});
modules.exports = mongoose.model("request",request_schema);
<|endoftext|>


<commit_before>(function() {
    $(document).ajaxSend(function (event, xhr, settings) {
        function getCookie(name) {
            var cookieValue = null;
            if (document.cookie && document.cookie != '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = jQuery.trim(cookies[i]);
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) == (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        }

        function sameOrigin(url) {
            // url could be relative or scheme relative or absolute
            var host = document.location.host; // host + port
            var protocol = document.location.protocol;
            var sr_origin = '//' + host;
            var origin = protocol + sr_origin;
            // Allow absolute or scheme relative URLs to same origin
            return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
                    (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
                // or any other URL that isn't scheme relative or absolute i.e relative.
                    !(/^(\/\/|http:|https:).*/.test(url));
        }

        function safeMethod(method) {
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }

        if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    });

    var helper;
    $(document).ready(function() {
        $("#id_searchname").focusin( function() {
            window.setTimeout( function() {
                if ($("#id_searchname").is(":focus")) {
                    if (helper == undefined) {
                        clippy.load('Clippy', function (agent) {
                            helper = agent;
                            helper.show();
                            helper.speak(django.gettext("Hi! I am Clippy, your Lagerregal assistant. Would you like some assistance totday?"));
                        });
                    }
                } else {
                    helper.animate();
                }
            }, 60000 );
        });

        var getFilterBasePath = function() {
            var index = window.location.pathname.length;
            ['department', 'sorting', 'group', 'filter', 'page'].forEach(function(key) {
                var i = window.location.pathname.indexOf(key)
                if (i !== -1 && i < index) {
                    index = i;
                }
            });
            return window.location.pathname.substr(0, index - 1);
        };

        var getFilterPath = function() {
            var ids = {
                department: ["#id_departmentfilter"],
                sorting: ["#id_viewsorting"],
                group: ["#id_groupfilter"],
                filter: ["#id_viewfilter", "#id_filterstring"],
            };
            var defaults = {
                department: "all",
            };

            var path = '';
            var index = window.location.pathname.length;
            ['department', 'sorting', 'group', 'filter'].forEach(function(key) {
                ids[key].forEach(function(id) {
                    var element = $(id);
                    if (element.length) {
                        var value = element.val() || defaults[key];
                        path += "/" + key + "/" + value;
                    }
                });
            });

            return path;
        };

        var setFilters = function() {
            window.location.pathname = getFilterBasePath() + getFilterPath();
        };

        $('#id_departmentfilter').change(setFilters);
        $('#id_viewsorting').change(setFilters);
        $('#id_groupfilter').change(setFilters);
        $('#id_viewfilter').change(setFilters);
        $('#id_filterstring').change(setFilters);
        $(".pageselect").change(function() {
            var s = "/page/" + $(this).val();
            window.location.pathname = getFilterBasePath() + s + getFilterPath();
        });

        $('[data-timeago]').timeago();
        $('[data-toggle="popover"]').popover();
    });
})();
<commit_msg>Gardening<commit_after>(function() {
    $(document).ajaxSend(function (event, xhr, settings) {
        function getCookie(name) {
            var cookieValue = null;
            if (document.cookie && document.cookie != '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = jQuery.trim(cookies[i]);
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) == (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        }

        function sameOrigin(url) {
            // url could be relative or scheme relative or absolute
            var host = document.location.host; // host + port
            var protocol = document.location.protocol;
            var sr_origin = '//' + host;
            var origin = protocol + sr_origin;
            // Allow absolute or scheme relative URLs to same origin
            return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
                    (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
                // or any other URL that isn't scheme relative or absolute i.e relative.
                    !(/^(\/\/|http:|https:).*/.test(url));
        }

        function safeMethod(method) {
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }

        if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    });

    var helper;
    $(document).ready(function() {
        $("#id_searchname").focusin( function() {
            window.setTimeout( function() {
                if ($("#id_searchname").is(":focus")) {
                    if (helper == undefined) {
                        clippy.load('Clippy', function (agent) {
                            helper = agent;
                            helper.show();
                            helper.speak(django.gettext("Hi! I am Clippy, your Lagerregal assistant. Would you like some assistance totday?"));
                        });
                    }
                } else {
                    helper.animate();
                }
            }, 60000 );
        });

        var getFilterBasePath = function() {
            var index = window.location.pathname.length;
            ['department', 'sorting', 'group', 'filter', 'page'].forEach(function(key) {
                var i = window.location.pathname.indexOf(key)
                if (i !== -1 && i < index) {
                    index = i;
                }
            });
            return window.location.pathname.substr(0, index - 1);
        };

        var getFilterPath = function() {
            var ids = {
                department: ["#id_departmentfilter"],
                sorting: ["#id_viewsorting"],
                group: ["#id_groupfilter"],
                filter: ["#id_viewfilter", "#id_filterstring"],
            };
            var defaults = {
                department: "all",
            };

            var path = '';
            var index = window.location.pathname.length;
            ['department', 'sorting', 'group', 'filter'].forEach(function(key) {
                ids[key].forEach(function(id) {
                    var element = $(id);
                    if (element.length) {
                        var value = element.val() || defaults[key];
                        path += "/" + key + "/" + value;
                    }
                });
            });

            return path;
        };

        var setFilters = function() {
            window.location.pathname = getFilterBasePath() + getFilterPath();
        };

        $('#id_departmentfilter').change(setFilters);
        $('#id_viewsorting').change(setFilters);
        $('#id_groupfilter').change(setFilters);
        $('#id_viewfilter').change(setFilters);
        $('#id_filterstring').change(setFilters);
        $(".pageselect").change(function() {
            var s = "/page/" + $(this).val();
            window.location.pathname = getFilterBasePath() + s + getFilterPath();
        });

        $('[data-timeago]').timeago();
        $('[data-toggle="popover"]').popover();
    });
})();
<|endoftext|>