<commit_before>'use strict';
const chai = require('chai');
const helper = require('../../lib/helpers');
const httpMocks = require('node-mocks-http');
const spies = require('chai-spies');

describe('Helpers test', function() {
  let expect;

  beforeEach(function() {
    expect = chai.expect;
  });

  context('getRules', function() {
    let path;
    let rules;

    beforeEach(function() {
      path = './tests/config/config.json';
    });

    it('Should return an array containing the rules', function() {
      rules = helper.getRules(path, null, false);
      let permissions = rules.get('user');
      expect(rules.has('user')).to.equal(true);
      expect(permissions[0]).to.have.property('resource');
      expect(permissions[0]).to.have.property('methods');
      expect(permissions[0]).to.have.property('action');
    });

    it('Should throw an error', function() {
      try {
        rules = helper.getRules(path, 1, true);
      } catch (err) {
        expect(helper.getRules).to.throw(Error);
      }
    });
  });

  context('getPolicy', function() {
    it('Should return the permissions specified', function() {
      let mockResource = 'users';
      let mockGroup = [{
        resource: 'users',
        methods: '*',
        action: 'allow'
      }];

      let policy = helper.getPolicy(mockGroup, mockResource);

      expect(policy).to.not.be.empty;
      expect(policy).to.have.property('methods');
      expect(policy).to.have.property('action');
    });
  });

  context('getRole', function() {
    let res;

    beforeEach(function(done) {
      res = httpMocks.createResponse();
      done();
    });

    it('Should return the role', function() {
      let session = {};
      let decoded = {
        role: 'user'
      };
      let role = helper.getRole(res, decoded, session);

      expect(role).to.not.be.empty;
      expect(role).to.equal(decoded.role);
    });

    it('Should return the role when session exits', function() {
      let session = {
        role: 'admin'
      };
      let decoded = {};
      let role = helper.getRole(res, decoded, session);

      expect(role).to.not.be.empty;
      expect(role).to.equal(session.role);
    });

    it('Should respond with 404', function() {
      let error = {
        message: 'REQUIRED: Role not found'
      };
      let session = {};
      let decoded = {};
      let role = helper.getRole(res, decoded, session);
      let data = res._getData();

      expect(role).to.be.empty;
      expect(res.statusCode).to.equal(404);
      expect(data.message).to.equal(error.message);
    });
  });

  context('resource', function() {
    let next;

    beforeEach(function() {
      next = function() {
        return;
      };
    });

    it('Should return the resource for a given url', function() {
      let url = '/api/user/4';
      let baseUrl = 'api';
      let resource = helper.resource(next, url, baseUrl);

      expect(resource).to.not.be.empty;
      expect(resource).to.equal('user');
    });

    it('Should call next', function() {
      chai.use(spies);
      let url = '';
      let baseUrl = 'api';
      let spy = chai.spy(next);
      let resource = helper.resource(spy, url, baseUrl);

      expect(resource).to.be.empty;
      expect(spy).to.have.been.called();
    });
  });
});
<commit_msg>refactor(test): refactor testcase for helpers.js - use const over var<commit_after>'use strict';
const chai = require('chai');
const helper = require('../../lib/helpers');
const httpMocks = require('node-mocks-http');
const spies = require('chai-spies');
const expect = chai.expect;

describe('Helpers test', function() {

  context('getRules', function() {
    let path, rules;

    beforeEach(function() {
      path = './tests/config/config.json';
    });

    it('Should return an array containing the rules', function() {
      rules = helper.getRules(path, null, false);
      let permissions = rules.get('user');
      expect(rules.has('user')).to.equal(true);
      expect(permissions[0]).to.have.property('resource');
      expect(permissions[0]).to.have.property('methods');
      expect(permissions[0]).to.have.property('action');
    });

    it('Should throw an error', function() {
      try {
        rules = helper.getRules(path, 1, true);
      } catch (err) {
        expect(helper.getRules).to.throw(Error);
      }
    });
  });

  context('getPolicy', function() {
    it('Should return the permissions specified', function() {
      let mockResource = 'users';
      let mockGroup = [{
        resource: 'users',
        methods: '*',
        action: 'allow'
      }];

      let policy = helper.getPolicy(mockGroup, mockResource);

      expect(policy).to.not.be.empty;
      expect(policy).to.have.property('methods');
      expect(policy).to.have.property('action');
    });
  });

  context('getRole', function() {
    let res;

    beforeEach(function(done) {
      res = httpMocks.createResponse();
      done();
    });

    it('Should return the role', function() {
      let session = {};
      let decoded = {
        role: 'user'
      };
      let role = helper.getRole(res, decoded, session);

      expect(role).to.not.be.empty;
      expect(role).to.equal(decoded.role);
    });

    it('Should return the role when session exits', function() {
      let session = {
        role: 'admin'
      };
      let decoded = {};
      let role = helper.getRole(res, decoded, session);

      expect(role).to.not.be.empty;
      expect(role).to.equal(session.role);
    });

    it('Should respond with 404', function() {
      let error = {
        message: 'REQUIRED: Role not found'
      };
      let session = {};
      let decoded = {};
      let role = helper.getRole(res, decoded, session);
      let data = res._getData();

      expect(role).to.be.empty;
      expect(res.statusCode).to.equal(404);
      expect(data.message).to.equal(error.message);
    });
  });

  context('resource', function() {
    let next;

    beforeEach(function() {
      next = function() {
        return;
      };
    });

    it('Should return the resource for a given url', function() {
      let url = '/api/user/4';
      let baseUrl = 'api';
      let resource = helper.resource(next, url, baseUrl);

      expect(resource).to.not.be.empty;
      expect(resource).to.equal('user');
    });

    it('Should call next', function() {
      chai.use(spies);
      let url = '';
      let baseUrl = 'api';
      let spy = chai.spy(next);
      let resource = helper.resource(spy, url, baseUrl);

      expect(resource).to.be.empty;
      expect(spy).to.have.been.called();
    });
  });
});
<|endoftext|>


<commit_before><commit_msg>Update boss.js<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2010 The Closure Compiler Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @fileoverview Definitions for W3C's Notifications specification.
 * @externs
 */

/**
 * @typedef {{dir: (string|undefined), lang: (string|undefined),
 *            body: (string|undefined), tag: (string|undefined),
 *            icon: (string|undefined)}}
 * @see http://notifications.spec.whatwg.org/#notificationoptions
 */
var NotificationOptions;

/** @interface */
var NotificationOptionsInterface_;
/** @type {string} */ NotificationOptionsInterface_.prototype.dir;
/** @type {string} */ NotificationOptionsInterface_.prototype.lang;
/** @type {string} */ NotificationOptionsInterface_.prototype.body;
/** @type {string} */ NotificationOptionsInterface_.prototype.tag;
/** @type {string} */ NotificationOptionsInterface_.prototype.icon;

/**
 * @param {string} title
 * @param {NotificationOptions=} opt_options
 * @constructor
 * @implements {EventTarget}
 * @see http://notifications.spec.whatwg.org/#notification
 */
function Notification(title, opt_options) {}

/**
 * @type {string}
 */
Notification.permission;

/**
 * @param {NotificationPermissionCallback=} opt_callback
 */
Notification.requestPermission = function(opt_callback) {};

/**
 * @param {boolean=} opt_useCapture
 * @override
 */
Notification.prototype.addEventListener =
    function(type, listener, opt_useCapture) {};

/**
 * @param {boolean=} opt_useCapture
 * @override
 */
Notification.prototype.removeEventListener =
    function(type, listener, opt_useCapture) {};

/** @override */
Notification.prototype.dispatchEvent = function(evt) {};

/**
 * The ID used by clients to uniquely identify notifications to eliminate
 * duplicate notifications.
 * @type {string}
 * @deprecated Use NotificationOptions.tag instead.
 */
Notification.prototype.replaceId;

/**
 * The string used by clients to specify the directionality (rtl/ltr) of the
 * notification.
 * @type {string}
 * @deprecated Use NotificationOptions.titleDir and bodyDir instead.
 */
Notification.prototype.dir;

/**
 * Displays the notification.
 */
Notification.prototype.show = function() {};

/**
 * Prevents the notification from being displayed, or closes it if it is already
 * displayed.
 */
Notification.prototype.cancel = function() {};

/**
 * Prevents the notification from being displayed, or closes it if it is already
 * displayed.
 */
Notification.prototype.close = function() {};

/**
 * An event handler called when notification is closed.
 * @type {?function(Event)}
 */
Notification.prototype.onclose;

/**
 * An event handler called if the notification could not be displayed due to
 * an error (i.e. resource could not be loaded).
 * @type {?function(Event)}
 */
Notification.prototype.onerror;

/**
 * An event handler called when the notification has become visible.
 * @type {?function(Event)}
 * @deprecated Use onshow instead.
 */
Notification.prototype.ondisplay;

/**
 * An event handler called when the notification has become visible.
 * @type {?function(Event)}
 */
Notification.prototype.onshow;

/**
 * An event handler called when the notification has been clicked on.
 * @type {?function(Event)}
 */
Notification.prototype.onclick;

/**
 * @type {function(new:Notification, function(string, NotificationOptions))}
 */
window.Notification;

/**
 * @type {string}
 */
window.Notification.permission;

/**
 * @param {NotificationPermissionCallback=} opt_callback
 */
window.Notification.requestPermission = function(opt_callback) {};

/**
 * @typedef {function(string)}
 * @see http://notifications.spec.whatwg.org/#notificationpermissioncallback
 */
var NotificationPermissionCallback;

/**
 * @constructor
 * @see http://dev.w3.org/2006/webapi/WebNotifications/publish/#dialog-if
 * @deprecated Use Notification instead.
 */
function NotificationCenter() {}

/**
 * Creates a text+icon notification and displays it to the user.
 * @param {string} iconUrl
 * @param {string} title
 * @param {string} body
 * @return {Notification}
 */
NotificationCenter.prototype.createNotification =
    function(iconUrl, title, body) {};

/**
 * Creates an HTML notification and displays it to the user.
 * @param {string} url
 * @return {Notification}
 */
NotificationCenter.prototype.createHTMLNotification = function(url) {};

/**
 * Checks if the user has permission to display notifications.
 * @return {number}
 */
NotificationCenter.prototype.checkPermission = function() {};

/**
 * Requests permission from the user to display notifications.
 * @param {Function=} opt_callback
 * @return {void}
 */
NotificationCenter.prototype.requestPermission = function(opt_callback) {};

/**
 * WebKit browsers expose the NotificationCenter API through
 * window.webkitNotifications.
 * @type {NotificationCenter}
 */
Window.prototype.webkitNotifications;
<commit_msg>Correct the window constructor signature for html5 notifications. The constructor takes the title as arg1 and the options as arg2, instead of a function with those two args. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=61228712<commit_after>/*
 * Copyright 2010 The Closure Compiler Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @fileoverview Definitions for W3C's Notifications specification.
 * @externs
 */

/**
 * @typedef {{dir: (string|undefined), lang: (string|undefined),
 *            body: (string|undefined), tag: (string|undefined),
 *            icon: (string|undefined)}}
 * @see http://notifications.spec.whatwg.org/#notificationoptions
 */
var NotificationOptions;

/** @interface */
var NotificationOptionsInterface_;
/** @type {string} */ NotificationOptionsInterface_.prototype.dir;
/** @type {string} */ NotificationOptionsInterface_.prototype.lang;
/** @type {string} */ NotificationOptionsInterface_.prototype.body;
/** @type {string} */ NotificationOptionsInterface_.prototype.tag;
/** @type {string} */ NotificationOptionsInterface_.prototype.icon;

/**
 * @param {string} title
 * @param {NotificationOptions=} opt_options
 * @constructor
 * @implements {EventTarget}
 * @see http://notifications.spec.whatwg.org/#notification
 */
function Notification(title, opt_options) {}

/**
 * @type {string}
 */
Notification.permission;

/**
 * @param {NotificationPermissionCallback=} opt_callback
 */
Notification.requestPermission = function(opt_callback) {};

/**
 * @param {boolean=} opt_useCapture
 * @override
 */
Notification.prototype.addEventListener =
    function(type, listener, opt_useCapture) {};

/**
 * @param {boolean=} opt_useCapture
 * @override
 */
Notification.prototype.removeEventListener =
    function(type, listener, opt_useCapture) {};

/** @override */
Notification.prototype.dispatchEvent = function(evt) {};

/**
 * The ID used by clients to uniquely identify notifications to eliminate
 * duplicate notifications.
 * @type {string}
 * @deprecated Use NotificationOptions.tag instead.
 */
Notification.prototype.replaceId;

/**
 * The string used by clients to specify the directionality (rtl/ltr) of the
 * notification.
 * @type {string}
 * @deprecated Use NotificationOptions.titleDir and bodyDir instead.
 */
Notification.prototype.dir;

/**
 * Displays the notification.
 */
Notification.prototype.show = function() {};

/**
 * Prevents the notification from being displayed, or closes it if it is already
 * displayed.
 */
Notification.prototype.cancel = function() {};

/**
 * Prevents the notification from being displayed, or closes it if it is already
 * displayed.
 */
Notification.prototype.close = function() {};

/**
 * An event handler called when notification is closed.
 * @type {?function(Event)}
 */
Notification.prototype.onclose;

/**
 * An event handler called if the notification could not be displayed due to
 * an error (i.e. resource could not be loaded).
 * @type {?function(Event)}
 */
Notification.prototype.onerror;

/**
 * An event handler called when the notification has become visible.
 * @type {?function(Event)}
 * @deprecated Use onshow instead.
 */
Notification.prototype.ondisplay;

/**
 * An event handler called when the notification has become visible.
 * @type {?function(Event)}
 */
Notification.prototype.onshow;

/**
 * An event handler called when the notification has been clicked on.
 * @type {?function(Event)}
 */
Notification.prototype.onclick;

/**
 * @type {function(new:Notification, string, NotificationOptions)}
 */
window.Notification;

/**
 * @type {string}
 */
window.Notification.permission;

/**
 * @param {NotificationPermissionCallback=} opt_callback
 */
window.Notification.requestPermission = function(opt_callback) {};

/**
 * @typedef {function(string)}
 * @see http://notifications.spec.whatwg.org/#notificationpermissioncallback
 */
var NotificationPermissionCallback;

/**
 * @constructor
 * @see http://dev.w3.org/2006/webapi/WebNotifications/publish/#dialog-if
 * @deprecated Use Notification instead.
 */
function NotificationCenter() {}

/**
 * Creates a text+icon notification and displays it to the user.
 * @param {string} iconUrl
 * @param {string} title
 * @param {string} body
 * @return {Notification}
 */
NotificationCenter.prototype.createNotification =
    function(iconUrl, title, body) {};

/**
 * Creates an HTML notification and displays it to the user.
 * @param {string} url
 * @return {Notification}
 */
NotificationCenter.prototype.createHTMLNotification = function(url) {};

/**
 * Checks if the user has permission to display notifications.
 * @return {number}
 */
NotificationCenter.prototype.checkPermission = function() {};

/**
 * Requests permission from the user to display notifications.
 * @param {Function=} opt_callback
 * @return {void}
 */
NotificationCenter.prototype.requestPermission = function(opt_callback) {};

/**
 * WebKit browsers expose the NotificationCenter API through
 * window.webkitNotifications.
 * @type {NotificationCenter}
 */
Window.prototype.webkitNotifications;
<|endoftext|>


<commit_before>import * as React from "react";
import {Wrapper} from "./Wrapper";
import {BuildSnapshot} from "./BuildSnapshot";

/**
 * Container Component for BuildSnapshots.
 *
 * @param {*} props input property containing an array of build data to be
 * rendered through BuildSnapshot.
 */
export const BuildSnapshotContainer = (props) => {
  const data = props.data;
  return (
    <Wrapper>
      {data.map(snapshotData => <BuildSnapshot data={snapshotData}/>)}
    </Wrapper>
  );
}
<commit_msg>Reorder dependencies<commit_after>import * as React from "react";
import {BuildSnapshot} from "./BuildSnapshot";
import {Wrapper} from "./Wrapper";

/**
 * Container Component for BuildSnapshots.
 *
 * @param {*} props input property containing an array of build data to be
 * rendered through BuildSnapshot.
 */
export const BuildSnapshotContainer = (props) => {
  const data = props.data;
  return (
    <Wrapper>
      {data.map(snapshotData => <BuildSnapshot data={snapshotData}/>)}
    </Wrapper>
  );
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved.
 *
 * Authors:
 * Zsolt Lattmann
 * Robert Kereskenyi
 */

"use strict";

define(['js/Constants',
    'js/NodePropertyNames',
    'js/Widgets/DiagramDesigner/DiagramDesignerWidget.DecoratorBase',
    '../Core/ModelicaDecorator.Core',
    'text!./ModelicaDecorator.DiagramDesignerWidget.html',
    'css!./ModelicaDecorator.DiagramDesignerWidget'], function (CONSTANTS,
                                                       nodePropertyNames,
                                                       DiagramDesignerWidgetDecoratorBase,
                                                       ModelicaDecoratorCore,
                                                       modelicaDecoratorDiagramDesignerWidgetTemplate) {

    var ModelicaDecoratorDiagramDesignerWidget,
        __parent__ = DiagramDesignerWidgetDecoratorBase,
        DECORATOR_ID = "ModelicaDecoratorDiagramDesignerWidget",
        CONNECTOR_RATIO = 2,
        DIAGRAM_DESIGNER_ICON_WIDTH = 150;

    ModelicaDecoratorDiagramDesignerWidget = function (options) {
        var opts = _.extend( {}, options);

        __parent__.apply(this, [opts]);

        this._modelicaDecoratorCore = new ModelicaDecoratorCore(this.logger);

        this.logger.debug("ModelicaDecoratorDiagramDesignerWidget ctor");
    };

    _.extend(ModelicaDecoratorDiagramDesignerWidget.prototype, __parent__.prototype);
    ModelicaDecoratorDiagramDesignerWidget.prototype.DECORATORID = DECORATOR_ID;

    /*********************** OVERRIDE DECORATORBASE MEMBERS **************************/

    ModelicaDecoratorDiagramDesignerWidget.prototype.$DOMBase = (function () {
        var el = $(modelicaDecoratorDiagramDesignerWidgetTemplate);
        //use the same HTML template as the DefaultDecorator.DiagramDesignerWidget
        //but remove the connector DOM elements since they are not needed in the PartBrowser
        el.find('div.name').remove();
        return el;
    })();

    ModelicaDecoratorDiagramDesignerWidget.prototype.on_addTo = function () {
        this._renderContent();

        //let the parent decorator class do its job first
        __parent__.prototype.on_addTo.apply(this, arguments);
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype._renderContent = function () {
        var control = this._control,
            gmeID = this._metaInfo[CONSTANTS.GME_ID];

        //render GME-ID in the DOM, for debugging
        if (DEBUG) {
            this.$el.attr({"data-id": gmeID});
        }

        //empty out SVG container
        this.$el.find('.svg-container').empty();

        //figure out the necessary SVG based on children type
        this.skinParts.$svg = this._modelicaDecoratorCore.getSVGByGMEId(control, gmeID);
        if (this.skinParts.$svg) {
            this._modelicaDecoratorCore.resizeSVGToWidth(this.skinParts.$svg, DIAGRAM_DESIGNER_ICON_WIDTH);

            this.$el.find('.svg-container').append(this.skinParts.$svg);

            //get the scale of the SVG based on its width/height and viewBox
            var obj = this._modelicaDecoratorCore.getSVGWidthHeightRatioAndScale(this.skinParts.$svg);
            this._svg_scale = obj.scale;

            //render the connectors
            this.skinParts.$connectorContainer = this.$el.find('.connector-container');
            this.skinParts.$connectorContainer.empty();
            this._renderPorts();

            //render text based on model values
            this._renderTexts();
        } else {
            this.$el.find('.svg-container').append(this._modelicaDecoratorCore.getErrorSVG());
        }
    };

//    ModelicaDecoratorDiagramDesignerWidget.prototype.calculateDimension = function () {
//        if (this.hostDesignerItem) {
//            this.hostDesignerItem.width = this.$el.outerWidth(true);
//            this.hostDesignerItem.height = this.$el.outerHeight(true);
//        }
//    };

    ModelicaDecoratorDiagramDesignerWidget.prototype.update = function () {
        //TODO: there might be some optimization here not just blindly rerender everything...
        this._renderContent();
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype.getConnectionAreas = function (id) {
        var result = [],
            edge = 10;

        //by default return the bounding box edge's midpoints

        if (id === undefined || id == this.hostDesignerItem.id) {
            //top left
            result.push( {"id": "0",
                "x": edge,
                "y": 0,
                "w": this.hostDesignerItem.width - 2 * edge,
                "h": 0,
                "orientation": "N",
                "len": 10} );

            result.push( {"id": "1",
                "x": edge,
                "y": this.hostDesignerItem.height,
                "w": this.hostDesignerItem.width - 2 * edge,
                "h": 0,
                "orientation": "S",
                "len": 10} );

            result.push( {"id": "2",
                "x": 0,
                "y": edge,
                "w": 0,
                "h": this.hostDesignerItem.height - 2 * edge,
                "orientation": "W",
                "len": 10} );

            result.push( {"id": "3",
                "x": this.hostDesignerItem.width,
                "y": edge,
                "w": 0,
                "h": this.hostDesignerItem.height - 2 * edge,
                "orientation": "E",
                "len": 10} );
        } else {
            result.push( {"id": id,
                "x": this._portCoordinates[id].x + this._portCoordinates[id].w / 2,
                "y": this._portCoordinates[id].y + this._portCoordinates[id].h / 2,
                "w": 0,
                "h": 0,
                "orientation": "O",
                "len": 10} );
        }

        return result;
    };

    //Shows the 'connectors' - appends them to the DOM
    ModelicaDecoratorDiagramDesignerWidget.prototype.showConnectors = function () {
        this.$connectors.show();
    };

    //Hides the 'connectors' - detaches them from the DOM
    ModelicaDecoratorDiagramDesignerWidget.prototype.hideConnectors = function () {
        this.$connectors.hide();
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype._toolTipBase = $('
 \
            CLASS NAME \
            NAME \
            DESCRIPTION \
            CLASS DESCRIPTION \

');

    ModelicaDecoratorDiagramDesignerWidget.prototype._buildToolTip = function (portConnector, svgPort) {

        var tooltip = this._toolTipBase.clone(),
            svgInfo = $(svgPort).find('#info');

        if (svgInfo.length === 0) {
            return;
        }

        svgInfo = $(svgInfo[0]);

        tooltip.find('.name').text(svgInfo.find('#name').text());
        tooltip.find('.class_name').text(svgInfo.find('#type').text());
        tooltip.find('.desc').text(svgInfo.find('#desc').text());
        tooltip.find('.class_desc').text(svgInfo.find('#classDesc').text());

        svgInfo.remove();

        portConnector.append(tooltip);
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype._renderPorts = function () {
        var client = this._control._client,
                nodeObj = client.getNode(this._metaInfo[CONSTANTS.GME_ID]),
                childrenIDs = nodeObj ?  nodeObj.getChildrenIds() : [],
                len,
                port,
                portId,
                portName,
                svgPort,
                portConnector;

        this._svgPortConnectors = [];

        len = childrenIDs.length;
        while (len--) {
            port = client.getNode(childrenIDs[len]);
            if (port) {
                portName = port.getAttribute(nodePropertyNames.Attributes.name);
                portId =  port.getId();

                svgPort = this.skinParts.$svg.find('#' + portName);

                if (svgPort.length > 0) {
                    svgPort = svgPort[0];

                    portConnector = $('
', {'class':'connector'});

                    this._buildToolTip(portConnector, svgPort);

                    var bbox = svgPort.getBBox();

//                    var portConnectorSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
//                    portConnectorSvg.setAttribute("version", "1.1");
//                    portConnectorSvg.setAttribute("baseProfile", "full");

//                    var defs = $('<defs> \
//<clippath id="rectClipPath"> \
//    <rect id="rectClip" x="0" y="0" width="0" height="0" style="stroke: gray; fill: none;"> \
//</rect></clippath><defs>')[0];

                    //portConnectorSvg.appendChild(defs);

                    //var use = $('<use x="0" y="0" style="clip-path: url(#rectClipPath);" ></use>')[0];
                    //portConnectorSvg.appendChild(use);

                    //defs.appendChild($(svgPort).clone()[0]);

                    //portConnector[0].appendChild(portConnectorSvg);

                    portConnector.css({'left':bbox.x, 'top':bbox.y, 'width':bbox.width, 'height':bbox.height});

                    var rect = $(svgPort).find('rect');

                    if (rect.length > 0) {
                        rect = rect[0];

                        var fillColor = rect.getAttribute('fill');
                        var borderColor = rect.getAttribute('stroke');

                        if (fillColor) {
                            portConnector.css({'background-color': fillColor});
                        }

                        if (borderColor) {
                            portConnector.css({'border-color': borderColor});
                        }
                    }

                    this._portCoordinates = this._portCoordinates || {};

                    this._svgPortConnectors.push([svgPort, portConnector, {}, portId]);

                    // scale it
                    this._portCoordinates[portId] = {
                        'x':bbox.x * this._svg_scale,
                        'y':bbox.y * this._svg_scale,
                        'w':bbox.width * this._svg_scale,
                        'h':bbox.height* this._svg_scale
                    };

                    if (this.hostDesignerItem) {
                        // PART BROWSER DOES NOT HAVE hostDesignerItem
                        this.hostDesignerItem.registerConnectors(portConnector, portId);
                        this.hostDesignerItem.registerSubcomponent(portId, {"GME_ID": portId});
                    }

                    this.skinParts.$connectorContainer.append(portConnector);
                }
                else
                {
                   this.logger.debug('Cannot find svg child object for port: ' + portName);
                }

            } else {
                this.logger.error('Cannot load child object: ' + childrenIDs[len]);
            }
        }

        this.showConnectors();
        this.initializeConnectors();
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype._renderTexts = function () {
        this.__svgTexts = this._modelicaDecoratorCore.renderTextsOnSVG(this.skinParts.$svg, this._control, this._metaInfo[CONSTANTS.GME_ID]);
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype.onRenderGetLayoutInfo = function () {
        var len;

        //let the parent decorator class do its job first
        __parent__.prototype.onRenderGetLayoutInfo.apply(this, arguments);

        if (this._svgPortConnectors) {
            len = this._svgPortConnectors.length;
            while(len--) {
                this._svgPortConnectors[len][2] = this._svgPortConnectors[len][0].getBBox();
            }
        }

        if (this.__svgTexts) {
            len = this.__svgTexts.length;
            while (len--) {
                var clientRect = this.__svgTexts[len][0].getBoundingClientRect();
                var renderedHeight = parseInt(clientRect.height) / this._svg_scale;
                var renderedWidth = parseInt(clientRect.width) / this._svg_scale;
                var bBox = this.__svgTexts[len][1].split(' ');
                var mexHeight = parseInt(bBox[3]) - parseInt(bBox[1]);
                var mexWidth = parseInt(bBox[2]) - parseInt(bBox[0]);
                var scale = Math.min(mexHeight/renderedHeight, mexWidth/renderedWidth);
                var fontSize = parseInt(this.__svgTexts[len][0].getAttribute('font-size'));
                var newFontSize = Math.floor(fontSize * scale);
                this.__svgTexts[len].push(newFontSize);
            }
        }

    };

    ModelicaDecoratorDiagramDesignerWidget.prototype.onRenderSetLayoutInfo = function () {
        var len;

        if (this._svgPortConnectors) {
            len = this._svgPortConnectors.length;
            while(len--) {
                var bbox =this._svgPortConnectors[len][2];
                var portConnector = this._svgPortConnectors[len][1];

                var w = bbox.width * this._svg_scale * CONNECTOR_RATIO;
                var h = bbox.height * this._svg_scale * CONNECTOR_RATIO;
                var x = bbox.x * this._svg_scale - (w - bbox.width * this._svg_scale) / 2 - 1; // border width
                var y = bbox.y * this._svg_scale - (h - bbox.height * this._svg_scale) / 2 - 1; // border width

                portConnector.css({'left':x, 'top':y, 'width':w, 'height':h});
                //var portConnectorSvg = portConnector.find('svg')[0]
                //portConnectorSvg.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
                //portConnectorSvg.setAttribute('width', w);
                //portConnectorSvg.setAttribute('height', h);

                //var rectClip = $(portConnectorSvg).find('#rectClip')[0];
                //rectClip.setAttribute('x', bbox.x);
                //rectClip.setAttribute('y', bbox.y);
                //rectClip.setAttribute('width', bbox.width);
                //rectClip.setAttribute('height', bbox.height);

                //var use = $(portConnectorSvg).find('use')[0];
                //use.setAttribute('x', -bbox.x);
                //use.setAttribute('y', -bbox.y);
                //use.setAttribute('xlink:href', '#' + $(portConnectorSvg).find('g').attr('id'));

                // scale it
                this._portCoordinates[ this._svgPortConnectors[len][3]] = {
                    'x':bbox.x * this._svg_scale,
                    'y':bbox.y * this._svg_scale,
                    'w':bbox.width * this._svg_scale,
                    'h':bbox.height* this._svg_scale
                };
            }
        }

        if (this.__svgTexts) {
            len = this.__svgTexts.length;
            while (len--) {
                if (this.__svgTexts[len].length > 2) {
                    var fontsize = this.__svgTexts[len][2];
                    this.__svgTexts[len][0].setAttribute('font-size', fontsize);
                }
            }
        }

        //let the parent decorator class do its job finally
        __parent__.prototype.onRenderSetLayoutInfo.apply(this, arguments);
    };

    return ModelicaDecoratorDiagramDesignerWidget;
});<commit_msg>Better port icons based on svg content.<commit_after>/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved.
 *
 * Authors:
 * Zsolt Lattmann
 * Robert Kereskenyi
 */

"use strict";

define(['js/Constants',
    'js/NodePropertyNames',
    'js/Widgets/DiagramDesigner/DiagramDesignerWidget.DecoratorBase',
    '../Core/ModelicaDecorator.Core',
    'text!./ModelicaDecorator.DiagramDesignerWidget.html',
    'css!./ModelicaDecorator.DiagramDesignerWidget'], function (CONSTANTS,
                                                       nodePropertyNames,
                                                       DiagramDesignerWidgetDecoratorBase,
                                                       ModelicaDecoratorCore,
                                                       modelicaDecoratorDiagramDesignerWidgetTemplate) {

    var ModelicaDecoratorDiagramDesignerWidget,
        __parent__ = DiagramDesignerWidgetDecoratorBase,
        DECORATOR_ID = "ModelicaDecoratorDiagramDesignerWidget",
        CONNECTOR_RATIO = 2,
        DIAGRAM_DESIGNER_ICON_WIDTH = 150;

    ModelicaDecoratorDiagramDesignerWidget = function (options) {
        var opts = _.extend( {}, options);

        __parent__.apply(this, [opts]);

        this._modelicaDecoratorCore = new ModelicaDecoratorCore(this.logger);

        this.logger.debug("ModelicaDecoratorDiagramDesignerWidget ctor");
    };

    _.extend(ModelicaDecoratorDiagramDesignerWidget.prototype, __parent__.prototype);
    ModelicaDecoratorDiagramDesignerWidget.prototype.DECORATORID = DECORATOR_ID;

    /*********************** OVERRIDE DECORATORBASE MEMBERS **************************/

    ModelicaDecoratorDiagramDesignerWidget.prototype.$DOMBase = (function () {
        var el = $(modelicaDecoratorDiagramDesignerWidgetTemplate);
        //use the same HTML template as the DefaultDecorator.DiagramDesignerWidget
        //but remove the connector DOM elements since they are not needed in the PartBrowser
        el.find('div.name').remove();
        return el;
    })();

    ModelicaDecoratorDiagramDesignerWidget.prototype.on_addTo = function () {
        this._renderContent();

        //let the parent decorator class do its job first
        __parent__.prototype.on_addTo.apply(this, arguments);
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype._renderContent = function () {
        var control = this._control,
            gmeID = this._metaInfo[CONSTANTS.GME_ID];

        //render GME-ID in the DOM, for debugging
        if (DEBUG) {
            this.$el.attr({"data-id": gmeID});
        }

        //empty out SVG container
        this.$el.find('.svg-container').empty();

        //figure out the necessary SVG based on children type
        this.skinParts.$svg = this._modelicaDecoratorCore.getSVGByGMEId(control, gmeID);
        if (this.skinParts.$svg) {
            this._modelicaDecoratorCore.resizeSVGToWidth(this.skinParts.$svg, DIAGRAM_DESIGNER_ICON_WIDTH);

            this.$el.find('.svg-container').append(this.skinParts.$svg);

            //get the scale of the SVG based on its width/height and viewBox
            var obj = this._modelicaDecoratorCore.getSVGWidthHeightRatioAndScale(this.skinParts.$svg);
            this._svg_scale = obj.scale;

            //render the connectors
            this.skinParts.$connectorContainer = this.$el.find('.connector-container');
            this.skinParts.$connectorContainer.empty();
            this._renderPorts();

            //render text based on model values
            this._renderTexts();
        } else {
            this.$el.find('.svg-container').append(this._modelicaDecoratorCore.getErrorSVG());
        }
    };

//    ModelicaDecoratorDiagramDesignerWidget.prototype.calculateDimension = function () {
//        if (this.hostDesignerItem) {
//            this.hostDesignerItem.width = this.$el.outerWidth(true);
//            this.hostDesignerItem.height = this.$el.outerHeight(true);
//        }
//    };

    ModelicaDecoratorDiagramDesignerWidget.prototype.update = function () {
        //TODO: there might be some optimization here not just blindly rerender everything...
        this._renderContent();
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype.getConnectionAreas = function (id) {
        var result = [],
            edge = 10;

        //by default return the bounding box edge's midpoints

        if (id === undefined || id == this.hostDesignerItem.id) {
            //top left
            result.push( {"id": "0",
                "x": edge,
                "y": 0,
                "w": this.hostDesignerItem.width - 2 * edge,
                "h": 0,
                "orientation": "N",
                "len": 10} );

            result.push( {"id": "1",
                "x": edge,
                "y": this.hostDesignerItem.height,
                "w": this.hostDesignerItem.width - 2 * edge,
                "h": 0,
                "orientation": "S",
                "len": 10} );

            result.push( {"id": "2",
                "x": 0,
                "y": edge,
                "w": 0,
                "h": this.hostDesignerItem.height - 2 * edge,
                "orientation": "W",
                "len": 10} );

            result.push( {"id": "3",
                "x": this.hostDesignerItem.width,
                "y": edge,
                "w": 0,
                "h": this.hostDesignerItem.height - 2 * edge,
                "orientation": "E",
                "len": 10} );
        } else {
            result.push( {"id": id,
                "x": this._portCoordinates[id].x + this._portCoordinates[id].w / 2,
                "y": this._portCoordinates[id].y + this._portCoordinates[id].h / 2,
                "w": 0,
                "h": 0,
                "orientation": "O",
                "len": 10} );
        }

        return result;
    };

    //Shows the 'connectors' - appends them to the DOM
    ModelicaDecoratorDiagramDesignerWidget.prototype.showConnectors = function () {
        this.$connectors.show();
    };

    //Hides the 'connectors' - detaches them from the DOM
    ModelicaDecoratorDiagramDesignerWidget.prototype.hideConnectors = function () {
        this.$connectors.hide();
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype._toolTipBase = $('
 \
            CLASS NAME \
            NAME \
            DESCRIPTION \
            CLASS DESCRIPTION \

');

    ModelicaDecoratorDiagramDesignerWidget.prototype._buildToolTip = function (portConnector, svgPort) {

        var tooltip = this._toolTipBase.clone(),
            svgInfo = $(svgPort).find('#info');

        if (svgInfo.length === 0) {
            return;
        }

        svgInfo = $(svgInfo[0]);

        tooltip.find('.name').text(svgInfo.find('#name').text());
        tooltip.find('.class_name').text(svgInfo.find('#type').text());
        tooltip.find('.desc').text(svgInfo.find('#desc').text());
        tooltip.find('.class_desc').text(svgInfo.find('#classDesc').text());

        svgInfo.remove();

        portConnector.append(tooltip);
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype._renderPorts = function () {
        var client = this._control._client,
                nodeObj = client.getNode(this._metaInfo[CONSTANTS.GME_ID]),
                childrenIDs = nodeObj ?  nodeObj.getChildrenIds() : [],
                len,
                port,
                portId,
                portName,
                svgPort,
                portConnector;

        this._svgPortConnectors = [];

        len = childrenIDs.length;
        while (len--) {
            port = client.getNode(childrenIDs[len]);
            if (port) {
                portName = port.getAttribute(nodePropertyNames.Attributes.name);
                portId =  port.getId();

                svgPort = this.skinParts.$svg.find('#' + portName);

                if (svgPort.length > 0) {
                    svgPort = svgPort[0];

                    portConnector = $('
', {'class':'connector'});

                    this._buildToolTip(portConnector, svgPort);

                    var bbox = svgPort.getBBox();

                    var portConnectorSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
                    portConnectorSvg.setAttribute("version", "1.1");
                    portConnectorSvg.setAttribute("baseProfile", "full");
                    portConnectorSvg.appendChild($(svgPort).clone()[0]);
                    portConnector[0].appendChild(portConnectorSvg);

                    portConnector.css({'left':bbox.x, 'top':bbox.y, 'width':bbox.width, 'height':bbox.height});

//
//                    var rect = $(svgPort).find('rect');
//
//                    if (rect.length > 0) {
//                        rect = rect[0];
//
//                        var fillColor = rect.getAttribute('fill');
//                        var borderColor = rect.getAttribute('stroke');
//
//                        if (fillColor) {
//                            portConnector.css({'background-color': fillColor});
//                        }
//
//                        if (borderColor) {
//                            portConnector.css({'border-color': borderColor});
//                        }
//                    }

                    this._portCoordinates = this._portCoordinates || {};

                    this._svgPortConnectors.push([svgPort, portConnector, {}, portId]);

                    // scale it
                    this._portCoordinates[portId] = {
                        'x':bbox.x * this._svg_scale,
                        'y':bbox.y * this._svg_scale,
                        'w':bbox.width * this._svg_scale,
                        'h':bbox.height* this._svg_scale
                    };

                    if (this.hostDesignerItem) {
                        // PART BROWSER DOES NOT HAVE hostDesignerItem
                        this.hostDesignerItem.registerConnectors(portConnector, portId);
                        this.hostDesignerItem.registerSubcomponent(portId, {"GME_ID": portId});
                    }

                    this.skinParts.$connectorContainer.append(portConnector);
                }
                else
                {
                   this.logger.debug('Cannot find svg child object for port: ' + portName);
                }

            } else {
                this.logger.error('Cannot load child object: ' + childrenIDs[len]);
            }
        }

        this.showConnectors();
        this.initializeConnectors();
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype._renderTexts = function () {
        this.__svgTexts = this._modelicaDecoratorCore.renderTextsOnSVG(this.skinParts.$svg, this._control, this._metaInfo[CONSTANTS.GME_ID]);
    };

    ModelicaDecoratorDiagramDesignerWidget.prototype.onRenderGetLayoutInfo = function () {
        var len;

        //let the parent decorator class do its job first
        __parent__.prototype.onRenderGetLayoutInfo.apply(this, arguments);

        if (this._svgPortConnectors) {
            len = this._svgPortConnectors.length;
            while(len--) {
                this._svgPortConnectors[len][2] = this._svgPortConnectors[len][0].getBBox();
            }
        }

        if (this.__svgTexts) {
            len = this.__svgTexts.length;
            while (len--) {
                var clientRect = this.__svgTexts[len][0].getBoundingClientRect();
                var renderedHeight = parseInt(clientRect.height) / this._svg_scale;
                var renderedWidth = parseInt(clientRect.width) / this._svg_scale;
                var bBox = this.__svgTexts[len][1].split(' ');
                var mexHeight = parseInt(bBox[3]) - parseInt(bBox[1]);
                var mexWidth = parseInt(bBox[2]) - parseInt(bBox[0]);
                var scale = Math.min(mexHeight/renderedHeight, mexWidth/renderedWidth);
                var fontSize = parseInt(this.__svgTexts[len][0].getAttribute('font-size'));
                var newFontSize = Math.floor(fontSize * scale);
                this.__svgTexts[len].push(newFontSize);
            }
        }

    };

    ModelicaDecoratorDiagramDesignerWidget.prototype.onRenderSetLayoutInfo = function () {
        var len;

        if (this._svgPortConnectors) {
            len = this._svgPortConnectors.length;
            while(len--) {
                var bbox =this._svgPortConnectors[len][2];
                var portConnector = this._svgPortConnectors[len][1];

                var w = bbox.width * this._svg_scale * CONNECTOR_RATIO;
                var h = bbox.height * this._svg_scale * CONNECTOR_RATIO;
                var x = bbox.x * this._svg_scale - (w - bbox.width * this._svg_scale) / 2 - 1; // border width
                var y = bbox.y * this._svg_scale - (h - bbox.height * this._svg_scale) / 2 - 1; // border width

                portConnector.css({'left':x, 'top':y, 'width':w, 'height':h});
                var portConnectorSvg = portConnector.find('svg')[0]
                portConnectorSvg.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
                portConnectorSvg.setAttribute('width', w);
                portConnectorSvg.setAttribute('height', h);

                var g = $(portConnectorSvg).find('g')[0];
                g.setAttribute('transform', 'translate(' + -bbox.x * this._svg_scale * CONNECTOR_RATIO + ' ' + -bbox.y * this._svg_scale * CONNECTOR_RATIO + ') scale(' + this._svg_scale * CONNECTOR_RATIO + ' ' + this._svg_scale * CONNECTOR_RATIO + ')');

                // scale it
                this._portCoordinates[ this._svgPortConnectors[len][3]] = {
                    'x':bbox.x * this._svg_scale,
                    'y':bbox.y * this._svg_scale,
                    'w':bbox.width * this._svg_scale,
                    'h':bbox.height* this._svg_scale
                };
            }
        }

        if (this.__svgTexts) {
            len = this.__svgTexts.length;
            while (len--) {
                if (this.__svgTexts[len].length > 2) {
                    var fontsize = this.__svgTexts[len][2];
                    this.__svgTexts[len][0].setAttribute('font-size', fontsize);
                }
            }
        }

        //let the parent decorator class do its job finally
        __parent__.prototype.onRenderSetLayoutInfo.apply(this, arguments);
    };

    return ModelicaDecoratorDiagramDesignerWidget;
});<|endoftext|>


<commit_before>//
// Checkout
//

Caboose.Store.Modules.Checkout = (function() {
  self = {
    templates: {
      address: JST['caboose_store/checkout/address'],
      login: JST['caboose_store/checkout/login'],
      payment: JST['caboose_store/checkout/payment'],
      lineItems: JST['caboose_store/checkout/line_items'],
      shipping: JST['caboose_store/checkout/shipping'],
      forms: {
        signin: JST['caboose_store/checkout/forms/signin'],
        register: JST['caboose_store/checkout/forms/register'],
        guest: JST['caboose_store/checkout/forms/guest']
      }
    }
  };

  //
  // Initialize
  //

  self.initialize = function() {
    switch (window.location.pathname.replace(/\/$/, "")) {
      case '/checkout':
      case '/checkout/step-one':
        self.step = 1;
        break;
      case '/checkout/step-two':
        self.step = 2;
        break;
    }

    self.$checkout = $('#checkout')
    if (!self.$checkout.length) return false;
    self.loggedIn = $('body').data('logged-in');

    // TODO refactor this
    if (self.loggedIn) {
      $.post('/checkout/attach-user', function(response) {
        self.fetch(self.render);
      });
    } else {
      self.fetch(self.render);
    }

    self.bindEventHandlers();
  };

  //
  // Fetch
  //

  self.fetch = function(callback) {
    $.get('/cart/items', function(response) {
      self.order = response.order

      if (self.step == 2) {
        $.get('/checkout/shipping', function(response) {
          self.shippingRates = response.rates;
          self.selectedRate = response.selected_rate;
          callback();
        });
      } else {
        callback();
      }
    });
  };

  //
  // Events
  //

  self.bindEventHandlers = function() {
    self.$checkout.on('click', '[data-login-action]', self.loginClickHandler);
    self.$checkout.on('submit', '#checkout-login form', self.loginSubmitHandler);
    self.$checkout.on('change', 'input[type=checkbox][name=use_as_billing]', self.useAsBillingHandler);
    self.$checkout.on('click', '#checkout-continue button', self.continueHandler);
    self.$checkout.on('click', '#checkout-complete button', self.completeHandler);
    self.$checkout.on('change', '#checkout-shipping select', self.shippingChangeHandler);
    self.$checkout.on('change', '#checkout-payment form#payment select', self.expirationChangeHandler);
    self.$checkout.on('submit', '#checkout-payment form#payment', self.paymentSubmitHandler);
    self.$checkout.on('load', '#checkout-payment iframe#relay', self.relayLoadHandler);
    $(window).on('message', self.relayHandler);
  };

  self.loginClickHandler = function(event) {
    $section = self.$login.children('section');

    switch ($(event.target).data('login-action')) {
      case 'signin':
        $section.empty().html(self.templates.forms.signin());
        break;
      case 'register':
        $section.empty().html(self.templates.forms.register());
        break;
      case 'continue':
        $section.empty().html(self.templates.forms.guest());
        break;
    };
  };

  self.loginSubmitHandler = function(event) {
    event.preventDefault();
    var $form = $(event.target);

    $.ajax({
      type: $form.attr('method'),
      url: $form.attr('action'),
      data: $form.serialize(),
      success: function(response) {
        if (response.error || (response.errors && response.errors.length > 0)) {
          if ($form.find('.message').length) {
            $form.find('.message').empty().addClass('error').text(response.error || response.errors[0]);
          } else {
            $form.append($('<span/>').addClass('message error').text(response.error || response.errors[0]));
          }
        } else {
          if (response.logged_in) {
            self.$login.after($('<p/>').addClass('alert').text('You are now signed in').css('text-align', 'center')).remove();
            $.post('/checkout/attach-user');
          } else {
            self.$login.after($('<p/>').addClass('alert').text('Email successfully saved').css('text-align', 'center')).remove();
          }
        }

        self.fetch();
      }
    });
  };

  self.useAsBillingHandler = function(event) {
    if (event.target.checked) {
      self.$address.find('#billing').hide();
    } else {
      self.$address.find('#billing').show();
    }
  };

  self.continueHandler = function(event) {
    $form = self.$address.find('form');

    if (!self.order.email && !self.order.customer_id) {
      alert('Please sign in, register or choose to continue as a guest');
      return false;
    }

    $.ajax({
      type: $form.attr('method'),
      url: $form.attr('action'),
      data: $form.serialize(),
      success: function(response) {
        if (response.success) {
          window.location = '/checkout/step-two';
        } else {
          $form.find('.message').remove();
          $form.find('#' + response.address + ' h3').append($('<span/>').addClass('message error').text(response.errors[0]));
        }
      }
    });
  };

  self.shippingChangeHandler = function(event) {
    if (event.target.value == "") return false;

    $.ajax({
      url: '/checkout/shipping',
      type: 'put',
      data: { shipping_code: event.target.value },
      success: function(response) {
        if (response.success) {
          self.order = response.order;
          self.renderLineItems();
          self.renderPayment();
        }
      }
    });
  };

  self.expirationChangeHandler = function(event) {
    var $form = $('#checkout-payment #payment')
      , month = $form.find('select[name=month]').val()
      , year = $form.find('select[name=year]').val();

    $form.find('#expiration').val(month + year);
  };

  self.completeHandler = function(event) {
    if (self.$payment.length) self.$payment.find('form').submit();
  };

  self.paymentSubmitHandler = function(event) {
    event.preventDefault();

    if (!self.order.shipping_code) {
      alert('Please choose a shipping method');
    } else {
      self.$checkout.off('submit', '#checkout-payment form#payment');
      $(event.target).addClass('loading').submit();
    }
  };

  self.relayHandler = function(event) {
    var data = event.originalEvent.data
      , $form = $('#checkout #checkout-payment #payment');

    console.log('Relay: ', data);
    console.log('Relay Form: ', $form);
    if (!$form.length) return false;
    console.log('still in relay');
    console.log('------------');

    if (data.success == true) {
      window.location = '/checkout/thanks';
    } else {
      if ($form.find('.message').length) {
        $form.find('.message').empty().text(data.message);
      } else {
        $form.append($('<span/>').addClass('message error').text(data.message));
      }

      $form.removeClass('loading');
    }
  };

  //
  // Render
  //

  self.render = function() {
    self.$checkout.find('.loading').remove();
    self.renderLineItems();

    if (self.step == 1) {
      self.renderLogin();
      self.renderAddress();
    } else {
      self.renderShipping();
      self.renderPayment();
    }
  };

  self.renderLineItems = function() {
    self.$lineItems = self.$checkout.find('#checkout-line-items');
    if (!self.$lineItems.length) return false;
    self.$lineItems.empty().html(self.templates.lineItems({ order: self.order }));
  };

  self.renderLogin = function() {
    self.$login = self.$checkout.find('#checkout-login');
    if (self.loggedIn) self.$login.remove();
    if (self.loggedIn || !self.$login.length) return false;
    self.$login.html(self.templates.login());
    if (!self.order.email) self.$login.find('button[data-login-action="signin"]').click();
  };

  self.renderAddress = function() {
    self.$address = self.$checkout.find('#checkout-address');
    if (!self.$address.length) return false;

    self.$address.empty().html(self.templates.address({
      shippingAddress: self.order.shipping_address,
      billingAddress: self.order.billing_address,
      states: window.States
    }));
  };

  self.renderShipping = function() {
    self.$shipping = self.$checkout.find('#checkout-shipping');
    if (!self.$shipping.length) return false;

    self.$shipping.empty().html(self.templates.shipping({
      rates: self.shippingRates,
      selectedRate: self.selectedRate
    }));
  };

  self.renderPayment = function() {
    self.$payment = self.$checkout.find('#checkout-payment');
    if (!self.$payment.length) return false;
    self.$checkout.addClass('loading');

    $.get('/checkout/payment', function(response) {
      self.$payment.empty().html(self.templates.payment({ form: response }));
      self.expirationChangeHandler();
      self.$checkout.removeClass('loading');
    });
  };

  return self
}).call(Caboose.Store);

<commit_msg>Update checkout.js<commit_after>//
// Checkout
//

Caboose.Store.Modules.Checkout = (function() {
  self = {
    templates: {
      address: JST['caboose_store/checkout/address'],
      login: JST['caboose_store/checkout/login'],
      payment: JST['caboose_store/checkout/payment'],
      lineItems: JST['caboose_store/checkout/line_items'],
      shipping: JST['caboose_store/checkout/shipping'],
      forms: {
        signin: JST['caboose_store/checkout/forms/signin'],
        register: JST['caboose_store/checkout/forms/register'],
        guest: JST['caboose_store/checkout/forms/guest']
      }
    }
  };

  //
  // Initialize
  //

  self.initialize = function() {
    switch (window.location.pathname.replace(/\/$/, "")) {
      case '/checkout':
      case '/checkout/step-one':
        self.step = 1;
        break;
      case '/checkout/step-two':
        self.step = 2;
        break;
    }

    self.$checkout = $('#checkout')
    if (!self.$checkout.length) return false;
    self.loggedIn = $('body').data('logged-in');

    // TODO refactor this
    if (self.loggedIn) {
      $.post('/checkout/attach-user', function(response) {
        self.fetch(self.render);
      });
    } else {
      self.fetch(self.render);
    }

    self.bindEventHandlers();
  };

  //
  // Fetch
  //

  self.fetch = function(callback) {
    $.get('/cart/items', function(response) {
      self.order = response.order

      if (self.step == 2) {
        $.get('/checkout/shipping', function(response) {
          self.shippingRates = response.rates;
          self.selectedRate = response.selected_rate;
          callback();
        });
      } else {
        callback();
      }
    });
  };

  //
  // Events
  //

  self.bindEventHandlers = function() {
    self.$checkout.on('click', '[data-login-action]', self.loginClickHandler);
    self.$checkout.on('submit', '#checkout-login form', self.loginSubmitHandler);
    self.$checkout.on('change', 'input[type=checkbox][name=use_as_billing]', self.useAsBillingHandler);
    self.$checkout.on('click', '#checkout-continue button', self.continueHandler);
    self.$checkout.on('click', '#checkout-complete button', self.completeHandler);
    self.$checkout.on('change', '#checkout-shipping select', self.shippingChangeHandler);
    self.$checkout.on('change', '#checkout-payment form#payment select', self.expirationChangeHandler);
    self.$checkout.on('submit', '#checkout-payment form#payment', self.paymentSubmitHandler);
    //self.$checkout.on('load', '#checkout-payment iframe#relay', self.relayLoadHandler);

    self.$checkout.on('load', 'iframe#relay', function(event) {
      console.log(event);
    });
    $(window).on('message', self.relayHandler);
  };

  self.loginClickHandler = function(event) {
    $section = self.$login.children('section');

    switch ($(event.target).data('login-action')) {
      case 'signin':
        $section.empty().html(self.templates.forms.signin());
        break;
      case 'register':
        $section.empty().html(self.templates.forms.register());
        break;
      case 'continue':
        $section.empty().html(self.templates.forms.guest());
        break;
    };
  };

  self.loginSubmitHandler = function(event) {
    event.preventDefault();
    var $form = $(event.target);

    $.ajax({
      type: $form.attr('method'),
      url: $form.attr('action'),
      data: $form.serialize(),
      success: function(response) {
        if (response.error || (response.errors && response.errors.length > 0)) {
          if ($form.find('.message').length) {
            $form.find('.message').empty().addClass('error').text(response.error || response.errors[0]);
          } else {
            $form.append($('<span/>').addClass('message error').text(response.error || response.errors[0]));
          }
        } else {
          if (response.logged_in) {
            self.$login.after($('<p/>').addClass('alert').text('You are now signed in').css('text-align', 'center')).remove();
            $.post('/checkout/attach-user');
          } else {
            self.$login.after($('<p/>').addClass('alert').text('Email successfully saved').css('text-align', 'center')).remove();
          }
        }

        self.fetch();
      }
    });
  };

  self.useAsBillingHandler = function(event) {
    if (event.target.checked) {
      self.$address.find('#billing').hide();
    } else {
      self.$address.find('#billing').show();
    }
  };

  self.continueHandler = function(event) {
    $form = self.$address.find('form');

    if (!self.order.email && !self.order.customer_id) {
      alert('Please sign in, register or choose to continue as a guest');
      return false;
    }

    $.ajax({
      type: $form.attr('method'),
      url: $form.attr('action'),
      data: $form.serialize(),
      success: function(response) {
        if (response.success) {
          window.location = '/checkout/step-two';
        } else {
          $form.find('.message').remove();
          $form.find('#' + response.address + ' h3').append($('<span/>').addClass('message error').text(response.errors[0]));
        }
      }
    });
  };

  self.shippingChangeHandler = function(event) {
    if (event.target.value == "") return false;

    $.ajax({
      url: '/checkout/shipping',
      type: 'put',
      data: { shipping_code: event.target.value },
      success: function(response) {
        if (response.success) {
          self.order = response.order;
          self.renderLineItems();
          self.renderPayment();
        }
      }
    });
  };

  self.expirationChangeHandler = function(event) {
    var $form = $('#checkout-payment #payment')
      , month = $form.find('select[name=month]').val()
      , year = $form.find('select[name=year]').val();

    $form.find('#expiration').val(month + year);
  };

  self.completeHandler = function(event) {
    if (self.$payment.length) self.$payment.find('form').submit();
  };

  self.paymentSubmitHandler = function(event) {
    event.preventDefault();

    if (!self.order.shipping_code) {
      alert('Please choose a shipping method');
    } else {
      self.$checkout.off('submit', '#checkout-payment form#payment');
      $(event.target).addClass('loading').submit();
    }
  };

  self.relayHandler = function(event) {
    var data = event.originalEvent.data
      , $form = $('form#payment');

    console.log('Relay: ', data);
    console.log('Relay Form: ', $form);
    console.log(self.$payment, self.$payment.find('form'));
    console.log('------');
    if (!$form.length) return false;
    console.log('still in relay');
    console.log('------------');

    if (data.success == true) {
      window.location = '/checkout/thanks';
    } else {
      if ($form.find('.message').length) {
        $form.find('.message').empty().text(data.message);
      } else {
        $form.append($('<span/>').addClass('message error').text(data.message));
      }

      $form.removeClass('loading');
    }
  };

  //
  // Render
  //

  self.render = function() {
    self.$checkout.find('.loading').remove();
    self.renderLineItems();

    if (self.step == 1) {
      self.renderLogin();
      self.renderAddress();
    } else {
      self.renderShipping();
      self.renderPayment();
    }
  };

  self.renderLineItems = function() {
    self.$lineItems = self.$checkout.find('#checkout-line-items');
    if (!self.$lineItems.length) return false;
    self.$lineItems.empty().html(self.templates.lineItems({ order: self.order }));
  };

  self.renderLogin = function() {
    self.$login = self.$checkout.find('#checkout-login');
    if (self.loggedIn) self.$login.remove();
    if (self.loggedIn || !self.$login.length) return false;
    self.$login.html(self.templates.login());
    if (!self.order.email) self.$login.find('button[data-login-action="signin"]').click();
  };

  self.renderAddress = function() {
    self.$address = self.$checkout.find('#checkout-address');
    if (!self.$address.length) return false;

    self.$address.empty().html(self.templates.address({
      shippingAddress: self.order.shipping_address,
      billingAddress: self.order.billing_address,
      states: window.States
    }));
  };

  self.renderShipping = function() {
    self.$shipping = self.$checkout.find('#checkout-shipping');
    if (!self.$shipping.length) return false;

    self.$shipping.empty().html(self.templates.shipping({
      rates: self.shippingRates,
      selectedRate: self.selectedRate
    }));
  };

  self.renderPayment = function() {
    self.$payment = self.$checkout.find('#checkout-payment');
    if (!self.$payment.length) return false;
    self.$checkout.addClass('loading');

    $.get('/checkout/payment', function(response) {
      self.$payment.empty().html(self.templates.payment({ form: response }));
      self.expirationChangeHandler();
      self.$checkout.removeClass('loading');
    });
  };

  return self
}).call(Caboose.Store);

<|endoftext|>


<commit_before>// Features

var Feature = Backbone.Model.extend({});

var FeatureList = Backbone.Collection.extend({});

var FeatureItemView = Backbone.View.extend({

  className: 'feature-item draggable',

  attributes: function() {
    return {
      id: this.model.id,
      'data-name': this.model.get('name'),
      'data-description': this.model.get('description'),
      'data-type': this.model.get('type'),
      'data-targets': '[' + this.model.get('targets') + ']',
    }
  },

  initialize: function() {
    this.model.on({
      'hide': function() { this.$el.hide(); },
      'show': function() { this.$el.show(); },
    }, this);
  },

  render: function() {
    this.$el.text(this.model.get('name'));
    this.$el.draggable({
      helper: 'clone',
      revert: 'invalid',
    });
    return this;
  }

});

var FeatureListView = Backbone.View.extend({

  render: function() {
    this.$el.empty();
    this.collection.forEach(this._addFeatureItem, this);
    return this;
  },

  _addFeatureItem: function(featureItem) {
    var featureItemView = new FeatureItemView({ model: featureItem });
    this.$el.append(featureItemView.render().$el);
  },

  events: {
    'mouseenter .feature-item': '_highlight',
    'mouseleave .feature-item': '_unhighlight',
  },

  _highlight: function(e) {
    $(e.currentTarget).addClass('highlighted');
  },

  _unhighlight: function(e) {
    $(e.currentTarget).removeClass('highlighted');
  },

  filterItems: function(input) {
    this.collection.each(function(i) {
      if (!$.matches(i.get('name').toLowerCase(), input)) {
        i.trigger('hide');
      } else {
        i.trigger('show');
      }
    });
  },

});

// Rules

var Rule = Backbone.Model.extend({

  initialize: function() {
    this.urlRoot = '/rules';
  },

  _update: function(field, attrs, success) {
    this.save(attrs,
              { url: this.url() + '/' + field,
                wait: true,
                success: success,
                error: function(model, xhr, options) {
                  var response = $.parseJSON(xhr.responseText);
                  model.trigger('update-error:' + field, response.message);
                },
              });
  },

  updateName: function(newName) {
    var success = function(model, response, options) {
      model.id = newName;
    };
    this._update('name', { name: newName }, success);
  },

  updateDescription: function(newDescription) {
    var success = function(model, response, options) {};
    this._update('description', { description: newDescription }, success);
  },

});

var RuleView = Backbone.View.extend({

  initialize: function() {
    this.model.on({
      'change': this.render,
      'change:name': this._updateURL,
      'update-error:name': function(msg) {
        this._renderAlert('button.name', msg);
      },
      'update-error:description': function(msg) {
        this._renderAlert('button.description', msg);
      },
    }, this);
  },

  render: function() {
    this.$el.empty();
    this._renderName();
    this._renderDescription();
    return this;
  },

  _renderName: function() {
    var nameTemplate = _.template('
@<%= name %>
');
    var node = $(nameTemplate({ name: this.model.get('name') }));
    this.$el.append(node);
  },

  _renderDescription: function() {
    var descriptionTemplate = _.template(
      '
<%= description %>
');
    var node = $(descriptionTemplate({
      description: this.model.get('description')
    }));
    this.$el.append(node);
  },

  _updateURL: function() {
    window.location.replace(this.model.url() + '/input');
  },

  _renderAlert: function(button, msg) {
    var updateButton = this.$(button);
    updateButton.next('.alert-msg').remove();
    $.alertMsg(msg).insertAfter(updateButton);
  },

  events: {
    'dblclick #rule-name': function(e) {
      this._renderEditControls('name')(e)
    },
    'click button.name': function() {
      this._saveEdits('name')(this)
    },
    'dblclick #rule-description': function(e) {
      this._renderEditControls('description')(e)
    },
    'click button.description': function() {
      this._saveEdits('description')(this)
    },
  },

  _renderEditControls: function(modelField) {
    return function(e) {
      var fieldToEdit = $(e.currentTarget);
      var currentValue = fieldToEdit.text();
      if (modelField === 'name') {
        currentValue = currentValue.substring(1);
      }
      var inputField = $.textInput().addClass(modelField)
        .val(currentValue);
      var okButton = $.okButton().addClass(modelField);
      fieldToEdit.hide();
      inputField.insertAfter(fieldToEdit);
      okButton.insertAfter(inputField);
      inputField.focus();
    };
  },

  _saveEdits: function(modelField) {
    return function(view) {
      var inputField = view.$('input.' + modelField);
      if (!inputField.isEmpty() &&
          inputField.val() !== view.model.get(modelField)) {
        var updateFunction = 'update' + modelField.charAt(0).toUpperCase() +
          modelField.slice(1);
        view.model[updateFunction](inputField.val());
      } else {
        view.render();
      }
    };
  },

});

var Pair = Backbone.Model.extend({

  initialize: function(options) {
    if (options.feature.get('type') === 'complex') {
      var accept = '#' + options.feature.get('targets').join(', #');
      this.set('value', new AVM([], { accept: accept }));
    } else {
      this.set('value', 'underspecified');
    }
  },

});

var AVM = Backbone.Collection.extend({

  initialize: function(models, options) {
    this.accept = options.accept;
  },

}); // Each rule is associated
    // with a single AVM
    // representing its LHS

var PairView = Backbone.View.extend({

  className: 'pair',

  initialize: function(options) {
    this.parentView = options.parentView;
  },

  render: function() {
    var attribute = this.model.get('feature');
    this.$el.append(
      $.span('attribute').text(attribute.get('name')));
    if (attribute.get('type') === 'complex') {
      this._renderSubstructure();
    } else {
      this._renderValue();
    }
    return this;
  },

  _renderSubstructure: function() {
    var avmView = new AVMView({ collection: this.model.get('value') });
    var value = $.div('value');
    value.append(avmView.render().$el);
    this.$el.append(value);
    this.listenTo(
      avmView, 're-rendered',
      function() { this.parentView.trigger('update') });
  },

  _renderValue: function() {
    var selectMenu = $.selectMenu();
    var options = ['underspecified']
      .concat(this.model.get('feature').get('targets'));
    _.each(options, function(o) {
      selectMenu.append($.option(o));
    });
    selectMenu.val(this.model.get('value'));
    var value = $.span('value');
    value.append(selectMenu);
    this.$el.append(value);
  },

});

var AVMView = Backbone.View.extend({

  className: 'avm',

  initialize: function() {
    this.collection.on({
      'add': function(newPair) {
        this._renderPair(newPair);
        this.trigger('update');
      },
    }, this);
    this.on({
      'update': function() {
        this._adjustBracketHeight();
        this.trigger('re-rendered');
      },
    }, this);
  },

  _renderPair: function(pair) {
    var pairView = new PairView({ model: pair, parentView: this });
    pairView.render();
    pairView.$el
      .insertBefore(this.$el.children('.content').children('.placeholder'));
  },

  render: function() {
    this.$el.empty();
    this._renderBracket('left');
    this._renderContent();
    this._renderBracket('right');
    this._adjustBracketHeight();
    return this;
  },

  _renderBracket: function(type) {
    this.$el.append($.div('bracket bracket-' + type));
  },

  _renderContent: function() {
    var content = $.div('content');
    var view = this;
    var placeholder = $.placeholder('Drop feature here ...')
      .droppable({
        accept: view.collection.accept,
        drop: function(e, ui) {
          var item = $(ui.helper);
          var feature = new Feature({
            name: item.data('name'),
            type: item.data('type'),
            targets: item.dataToArray('targets'),
          });
          view.collection.add(new Pair({ feature: feature }));
        },
      });
    content.append(placeholder);
    this.$el.append(content);
  },

  _adjustBracketHeight: function() {
    var content = this.$el.children('.content');
    var height = content.children('.placeholder').height();
    if (!this.collection.isEmpty()) {
      height = content.height();
    }
    this.$el.children('.bracket').height(height);
  },

}); // This view should eventually
    // be incorporated into
    // RuleView as a subview

// Application

$(document).ready(function() {

  // "Features" sidebar

  var featureItems = $('.feature-item');

  var featureList = new FeatureList(
    _.map(featureItems, function(i) {
      var item = $(i);
      var name = item.data('name');
      return new Feature({
        id: name,
        name: name,
        type: item.data('type'),
        description: item.data('description'),
        targets: item.dataToArray('targets'),
      });
    }),
    { comparator: 'name' }
  );

  var featureListView = new FeatureListView({
    el: '#feature-list',
    collection: featureList,
  });
  featureListView.render();

  $('#feature-filter').on('keyup', function() {
    var currentInput = $(this).val().toLowerCase();
    featureListView.filterItems(currentInput);
  });

  // Input Builder

  var name = $('#rule-name').text();
  var description = $('#rule-description').text();
  var rule = new Rule({ id: name, name: name, description: description });

  var ruleView = new RuleView({
    model: rule,
    el: '#interaction-block',
  });
  ruleView.render();

  var avm = new AVM([], { accept: '.feature-item' });
  var avmView = new AVMView({ collection: avm });
  ruleView.$el.append(avmView.render().$el);

});
<commit_msg>WIP: Add functionality for removing attribute-value pairs from AVMs.<commit_after>// Features

var Feature = Backbone.Model.extend({});

var FeatureList = Backbone.Collection.extend({});

var FeatureItemView = Backbone.View.extend({

  className: 'feature-item draggable',

  attributes: function() {
    return {
      id: this.model.id,
      'data-name': this.model.get('name'),
      'data-description': this.model.get('description'),
      'data-type': this.model.get('type'),
      'data-targets': '[' + this.model.get('targets') + ']',
    }
  },

  initialize: function() {
    this.model.on({
      'hide': function() { this.$el.hide(); },
      'show': function() { this.$el.show(); },
    }, this);
  },

  render: function() {
    this.$el.text(this.model.get('name'));
    this.$el.draggable({
      helper: 'clone',
      revert: 'invalid',
    });
    return this;
  }

});

var FeatureListView = Backbone.View.extend({

  render: function() {
    this.$el.empty();
    this.collection.forEach(this._addFeatureItem, this);
    return this;
  },

  _addFeatureItem: function(featureItem) {
    var featureItemView = new FeatureItemView({ model: featureItem });
    this.$el.append(featureItemView.render().$el);
  },

  events: {
    'mouseenter .feature-item': '_highlight',
    'mouseleave .feature-item': '_unhighlight',
  },

  _highlight: function(e) {
    $(e.currentTarget).addClass('highlighted');
  },

  _unhighlight: function(e) {
    $(e.currentTarget).removeClass('highlighted');
  },

  filterItems: function(input) {
    this.collection.each(function(i) {
      if (!$.matches(i.get('name').toLowerCase(), input)) {
        i.trigger('hide');
      } else {
        i.trigger('show');
      }
    });
  },

});

// Rules

var Rule = Backbone.Model.extend({

  initialize: function() {
    this.urlRoot = '/rules';
  },

  _update: function(field, attrs, success) {
    this.save(attrs,
              { url: this.url() + '/' + field,
                wait: true,
                success: success,
                error: function(model, xhr, options) {
                  var response = $.parseJSON(xhr.responseText);
                  model.trigger('update-error:' + field, response.message);
                },
              });
  },

  updateName: function(newName) {
    var success = function(model, response, options) {
      model.id = newName;
    };
    this._update('name', { name: newName }, success);
  },

  updateDescription: function(newDescription) {
    var success = function(model, response, options) {};
    this._update('description', { description: newDescription }, success);
  },

});

var RuleView = Backbone.View.extend({

  initialize: function() {
    this.model.on({
      'change': this.render,
      'change:name': this._updateURL,
      'update-error:name': function(msg) {
        this._renderAlert('button.name', msg);
      },
      'update-error:description': function(msg) {
        this._renderAlert('button.description', msg);
      },
    }, this);
  },

  render: function() {
    this.$el.empty();
    this._renderName();
    this._renderDescription();
    return this;
  },

  _renderName: function() {
    var nameTemplate = _.template('
@<%= name %>
');
    var node = $(nameTemplate({ name: this.model.get('name') }));
    this.$el.append(node);
  },

  _renderDescription: function() {
    var descriptionTemplate = _.template(
      '
<%= description %>
');
    var node = $(descriptionTemplate({
      description: this.model.get('description')
    }));
    this.$el.append(node);
  },

  _updateURL: function() {
    window.location.replace(this.model.url() + '/input');
  },

  _renderAlert: function(button, msg) {
    var updateButton = this.$(button);
    updateButton.next('.alert-msg').remove();
    $.alertMsg(msg).insertAfter(updateButton);
  },

  events: {
    'dblclick #rule-name': function(e) {
      this._renderEditControls('name')(e)
    },
    'click button.name': function() {
      this._saveEdits('name')(this)
    },
    'dblclick #rule-description': function(e) {
      this._renderEditControls('description')(e)
    },
    'click button.description': function() {
      this._saveEdits('description')(this)
    },
  },

  _renderEditControls: function(modelField) {
    return function(e) {
      var fieldToEdit = $(e.currentTarget);
      var currentValue = fieldToEdit.text();
      if (modelField === 'name') {
        currentValue = currentValue.substring(1);
      }
      var inputField = $.textInput().addClass(modelField)
        .val(currentValue);
      var okButton = $.okButton().addClass(modelField);
      fieldToEdit.hide();
      inputField.insertAfter(fieldToEdit);
      okButton.insertAfter(inputField);
      inputField.focus();
    };
  },

  _saveEdits: function(modelField) {
    return function(view) {
      var inputField = view.$('input.' + modelField);
      if (!inputField.isEmpty() &&
          inputField.val() !== view.model.get(modelField)) {
        var updateFunction = 'update' + modelField.charAt(0).toUpperCase() +
          modelField.slice(1);
        view.model[updateFunction](inputField.val());
      } else {
        view.render();
      }
    };
  },

});

var Pair = Backbone.Model.extend({

  initialize: function(options) {
    if (options.feature.get('type') === 'complex') {
      var accept = '#' + options.feature.get('targets').join(', #');
      this.set('value', new AVM([], { accept: accept }));
    } else {
      this.set('value', 'underspecified');
    }
  },

});

var AVM = Backbone.Collection.extend({

  initialize: function(models, options) {
    this.accept = options.accept;
  },

}); // Each rule is associated
    // with a single AVM
    // representing its LHS

var PairView = Backbone.View.extend({

  className: 'pair',

  initialize: function(options) {
    this.parentView = options.parentView;
  },

  render: function() {
    var attribute = this.model.get('feature');
    this.$el.append(
      $.span('attribute').text(attribute.get('name')));
    this.$el.append(
      $.removeButton(attribute.get('name')).css('visibility', 'hidden'));
    if (attribute.get('type') === 'complex') {
      this._renderSubstructure();
    } else {
      this._renderValue();
    }
    return this;
  },

  _renderSubstructure: function() {
    var avmView = new AVMView({ collection: this.model.get('value') });
    var value = $.div('value');
    value.append(avmView.render().$el);
    this.$el.append(value);
    this.listenTo(
      avmView, 're-rendered',
      function() { this.parentView.trigger('update') });
  },

  _renderValue: function() {
    var selectMenu = $.selectMenu();
    var options = ['underspecified']
      .concat(this.model.get('feature').get('targets'));
    _.each(options, function(o) {
      selectMenu.append($.option(o));
    });
    selectMenu.val(this.model.get('value'));
    var value = $.span('value');
    value.append(selectMenu);
    this.$el.append(value);
  },

  events: {
    'mouseenter': '_showRemoveButton',
    'mouseleave': '_hideRemoveButton',
    'click .remove-button': function() {
      this.remove();
      this.parentView.trigger('remove:pair', this.model);
    },
  },

  _showRemoveButton: function() {
    this.$('.remove-button').css('visibility', 'visible');
  },

  _hideRemoveButton: function() {
    this.$('.remove-button').css('visibility', 'hidden');
  },

});

var AVMView = Backbone.View.extend({

  className: 'avm',

  initialize: function() {
    this.collection.on({
      'add': function(newPair) {
        this._renderPair(newPair);
        this.trigger('update');
      },
    }, this);
    this.on({
      'update': function() {
        this._adjustBracketHeight();
        this.trigger('re-rendered');
      },
      'remove:pair': function(pair) {
        this.collection.remove(pair);
        this.trigger('update');
      },
    }, this);
  },

  _renderPair: function(pair) {
    var pairView = new PairView({ model: pair, parentView: this });
    pairView.render();
    pairView.$el
      .insertBefore(this.$el.children('.content').children('.placeholder'));
  },

  render: function() {
    this.$el.empty();
    this._renderBracket('left');
    this._renderContent();
    this._renderBracket('right');
    this._adjustBracketHeight();
    return this;
  },

  _renderBracket: function(type) {
    this.$el.append($.div('bracket bracket-' + type));
  },

  _renderContent: function() {
    var content = $.div('content');
    var view = this;
    var placeholder = $.placeholder('Drop feature here ...')
      .droppable({
        accept: view.collection.accept,
        drop: function(e, ui) {
          var item = $(ui.helper);
          var feature = new Feature({
            name: item.data('name'),
            type: item.data('type'),
            targets: item.dataToArray('targets'),
          });
          view.collection.add(new Pair({ feature: feature }));
        },
      });
    content.append(placeholder);
    this.$el.append(content);
  },

  _adjustBracketHeight: function() {
    var content = this.$el.children('.content');
    var height = content.children('.placeholder').height();
    if (!this.collection.isEmpty()) {
      height = content.height();
    }
    this.$el.children('.bracket').height(height);
  },

}); // This view should eventually
    // be incorporated into
    // RuleView as a subview

// Application

$(document).ready(function() {

  // "Features" sidebar

  var featureItems = $('.feature-item');

  var featureList = new FeatureList(
    _.map(featureItems, function(i) {
      var item = $(i);
      var name = item.data('name');
      return new Feature({
        id: name,
        name: name,
        type: item.data('type'),
        description: item.data('description'),
        targets: item.dataToArray('targets'),
      });
    }),
    { comparator: 'name' }
  );

  var featureListView = new FeatureListView({
    el: '#feature-list',
    collection: featureList,
  });
  featureListView.render();

  $('#feature-filter').on('keyup', function() {
    var currentInput = $(this).val().toLowerCase();
    featureListView.filterItems(currentInput);
  });

  // Input Builder

  var name = $('#rule-name').text();
  var description = $('#rule-description').text();
  var rule = new Rule({ id: name, name: name, description: description });

  var ruleView = new RuleView({
    model: rule,
    el: '#interaction-block',
  });
  ruleView.render();

  var avm = new AVM([], { accept: '.feature-item' });
  var avmView = new AVMView({ collection: avm });
  ruleView.$el.append(avmView.render().$el);

});
<|endoftext|>


<commit_before>import { app, shell } from 'electron';
import archiver from 'archiver';
import fs from 'fs';
import os from 'os';
import path from 'path';

const packageJSON = require(path.resolve(__dirname, '../../../..', 'package.json'));

Emitter.on('generateDebugInfo', () => {
  const debugInfo =
  `Dump Details:
Timestamp: ${new Date()}

App Details:
Version: ${packageJSON.version}
Electron Version: ${packageJSON.dependencies.electron}
Development Build: ${fs.existsSync(path.resolve(__dirname, '../../../..', 'circle.yml')) ? 'true' : 'false'}

Operating System Details:
OS: ${os.platform()}
OS Version: ${os.release()}
OS Arch: ${os.arch()}
System Memory: ${os.totalmem()}
  `;
  const debugFolder = path.resolve(os.tmpdir(), 'gpmdp_debug_info');
  if (!fs.existsSync(debugFolder)) fs.mkdirSync(debugFolder);
  const zip = archiver('zip');
  const output = fs.createWriteStream(path.resolve(debugFolder, 'debug_info.zip'));
  const toRemove = [];
  const addStringToZip = (name, data) => {
    fs.writeFileSync(path.resolve(debugFolder, name), data);
    zip.file(path.resolve(debugFolder, name), {
      name,
    });
    toRemove.push(path.resolve(debugFolder, name));
  };
  const addFileToZip = (filePath) => {
    zip.file(filePath, {
      name: path.basename(filePath),
    });
  };
  output.on('close', () => {
    toRemove.forEach((removePath) => {
      fs.unlinkSync(removePath);
    });
    shell.showItemInFolder(path.resolve(debugFolder, 'debug_info.zip'));
  });

  zip.on('error', (err) => {
    Logger.error(err);
  });

  zip.pipe(output);

  addStringToZip('install.txt', debugInfo.replace(/\n/g, os.EOL));
  addStringToZip('settings.json', JSON.stringify(Object.assign({}, Settings.data, { lastFMKey: 'HIDDEN' }), null, 4));
  addFileToZip(path.resolve(app.getPath('userData'), 'gpmdp.log'));
  if (process.platform === 'win32') {
    addFileToZip(path.resolve(process.env['LOCALAPPDATA'], 'GPMDP_3', 'SquirrelSetup.log')); // eslint-disable-line
  }
  zip.finalize();
});
<commit_msg>Only delete files that exist<commit_after>import { app, shell } from 'electron';
import archiver from 'archiver';
import fs from 'fs';
import os from 'os';
import path from 'path';

const packageJSON = require(path.resolve(__dirname, '../../../..', 'package.json'));

Emitter.on('generateDebugInfo', () => {
  const debugInfo =
  `Dump Details:
Timestamp: ${new Date()}

App Details:
Version: ${packageJSON.version}
Electron Version: ${packageJSON.dependencies.electron}
Development Build: ${fs.existsSync(path.resolve(__dirname, '../../../..', 'circle.yml')) ? 'true' : 'false'}

Operating System Details:
OS: ${os.platform()}
OS Version: ${os.release()}
OS Arch: ${os.arch()}
System Memory: ${os.totalmem()}
  `;
  const debugFolder = path.resolve(os.tmpdir(), 'gpmdp_debug_info');
  if (!fs.existsSync(debugFolder)) fs.mkdirSync(debugFolder);
  const zip = archiver('zip');
  const output = fs.createWriteStream(path.resolve(debugFolder, 'debug_info.zip'));
  const toRemove = [];
  const addStringToZip = (name, data) => {
    fs.writeFileSync(path.resolve(debugFolder, name), data);
    zip.file(path.resolve(debugFolder, name), {
      name,
    });
    toRemove.push(path.resolve(debugFolder, name));
  };
  const addFileToZip = (filePath) => {
    zip.file(filePath, {
      name: path.basename(filePath),
    });
  };
  output.on('close', () => {
    toRemove.forEach((removePath) => {
      if (fs.existsSync(removePath)) fs.unlinkSync(removePath);
    });
    shell.showItemInFolder(path.resolve(debugFolder, 'debug_info.zip'));
  });

  zip.on('error', (err) => {
    Logger.error(err);
  });

  zip.pipe(output);

  addStringToZip('install.txt', debugInfo.replace(/\n/g, os.EOL));
  addStringToZip('settings.json', JSON.stringify(Object.assign({}, Settings.data, { lastFMKey: 'HIDDEN' }), null, 4));
  addFileToZip(path.resolve(app.getPath('userData'), 'gpmdp.log'));
  if (process.platform === 'win32') {
    addFileToZip(path.resolve(process.env['LOCALAPPDATA'], 'GPMDP_3', 'SquirrelSetup.log')); // eslint-disable-line
  }
  zip.finalize();
});
<|endoftext|>