<commit_before>/**
 * Created by Jackey Li on 2015/10/11.
 */
(function (React, ReactDOM) {
    'use strict';

    var UserList = React.createClass({
        getInitialState: function () {
            return {
                userList: [
                    {
                        "id": 1,
                        "userName": "Jackey1",
                        "age": 26
                    },
                    {
                        "id": 2,
                        "userName": "Jackey2",
                        "age": 26
                    }
                ]
            };
        },
        render: function () {
            var users = this.state.userList.map(function (user) {
                return (

{user.userName} {user.age}

                );
            });
            return (

{users}

            );
        }
    });

    var AddUser = React.createClass({
        render: function () {
            return (

add User

            );
        }
    });

    var UserBox = React.createClass({
        render: function () {
            return (

User List

                    <UserList/>
                    <AddUser/>

            );
        }
    });

    ReactDOM.render(
        <UserBox/>,
        document.getElementById('container')
    );

})(React, ReactDOM);<commit_msg>todo demo<commit_after>/**
 * Created by Jackey Li on 2015/10/11.
 */
(function (React, ReactDOM) {
    'use strict';

    var UserModel = function () {
        this.userList = [
            {
                "id": 1,
                "userName": "Jackey1",
                "age": 26
            },
            {
                "id": 2,
                "userName": "Jackey2",
                "age": 26
            }
        ];
    };

    UserModel.prototype.addUser = function (user) {
        user.id = 3;//todo: get next id
        this.userList.push(user);
    };

    UserModel.prototype.getUserList = function () {
        return this.userList;
    };

    var userModel = new UserModel();

    var UserList = React.createClass({
        loadUserList: function () {
            this.setState({
                userList: userModel.getUserList()
            });
        },
        getInitialState: function () {
            return {
                userList: []//userModel.getUserList()
            };
        },
        componentDidMount: function () {
            this.loadUserList();
            setInterval(this.loadUserList, 1000);
        },
        render: function () {
            var users = this.state.userList.map(function (user) {
                return (

{user.userName} {user.age}

                );
            });
            return (

{users}

            );
        }
    });

    var AddUser = React.createClass({
        handleSubmit: function (e) {
            e.preventDefault();
            var user = {
                userName: this.refs.userName.value,
                age: this.refs.age.value
            };

            userModel.addUser(user);
        },
        render: function () {
            return (

add User

                    <form onSubmit={this.handleSubmit}>
                        <input type='text' placeholder='userName' ref='userName'/>
                        <input type='text' placeholder='age' ref='age'/>
                        <input type='submit' value='Add User'/>
                    </form>

            );
        }
    });

    var UserBox = React.createClass({
        render: function () {
            return (

User List

                    <UserList/>
                    <AddUser/>

            );
        }
    });

    ReactDOM.render(
        <UserBox/>,
        document.getElementById('container')
    );

})(React, ReactDOM);<|endoftext|>


<commit_before>const {fbInternalOnly} = require('internaldocs-fb-helpers');

module.exports = {
  docs: {
    'Quick Start': [
      'getting-started',
      'quickstart-api-basics',
      'quickstart-rich-styling',
    ],
    'Advanced Topics': [
      'advanced-topics-entities',
      'v0-10-api-migration',
      'advanced-topics-decorators',
      'advanced-topics-key-bindings',
      'advanced-topics-managing-focus',
      'advanced-topics-block-styling',
      'advanced-topics-custom-block-render-map',
      'advanced-topics-block-components',
      'advanced-topics-inline-styles',
      'advanced-topics-nested-lists',
      'advanced-topics-text-direction',
      'advanced-topics-editorstate-race-conditions',
      'advanced-topics-issues-and-pitfalls',
    ],
    'API Reference': [
      'api-reference-editor',
      'api-reference-editor-change-type',
      'api-reference-editor-state',
      'api-reference-content-state',
      'api-reference-content-block',
      'api-reference-character-metadata',
      'api-reference-entity',
      'api-reference-selection-state',
      'api-reference-composite-decorator',
      'api-reference-data-conversion',
      'api-reference-rich-utils',
      'api-reference-atomic-block-utils',
      'api-reference-key-binding-util',
      'api-reference-modifier',
    ],
  },
  'fb-internal': {
    'FB Internal': fbInternalOnly([
      'fb/draft-js-g-ks',
      'fb/github-code-sync',
      'fb/impact-of-draft-js',
      'fb/importing-p-rs',
      'fb/internal-manual-test-plan',
      'fb/internal-tools-teams-using-draft-js',
      'fb/migrating-from-draft-0-9-1-to-0-10-0',
      'fb/oncall',
      'fb/opening-a-pull-request',
      'fb/releasing-a-new-version',
      'fb/triaging-issues',
      'fb/index',
    ]),
  },
};
<commit_msg>Add copyright header to sidebars file<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

const {fbInternalOnly} = require('internaldocs-fb-helpers');

module.exports = {
  docs: {
    'Quick Start': [
      'getting-started',
      'quickstart-api-basics',
      'quickstart-rich-styling',
    ],
    'Advanced Topics': [
      'advanced-topics-entities',
      'v0-10-api-migration',
      'advanced-topics-decorators',
      'advanced-topics-key-bindings',
      'advanced-topics-managing-focus',
      'advanced-topics-block-styling',
      'advanced-topics-custom-block-render-map',
      'advanced-topics-block-components',
      'advanced-topics-inline-styles',
      'advanced-topics-nested-lists',
      'advanced-topics-text-direction',
      'advanced-topics-editorstate-race-conditions',
      'advanced-topics-issues-and-pitfalls',
    ],
    'API Reference': [
      'api-reference-editor',
      'api-reference-editor-change-type',
      'api-reference-editor-state',
      'api-reference-content-state',
      'api-reference-content-block',
      'api-reference-character-metadata',
      'api-reference-entity',
      'api-reference-selection-state',
      'api-reference-composite-decorator',
      'api-reference-data-conversion',
      'api-reference-rich-utils',
      'api-reference-atomic-block-utils',
      'api-reference-key-binding-util',
      'api-reference-modifier',
    ],
  },
  'fb-internal': {
    'FB Internal': fbInternalOnly([
      'fb/draft-js-g-ks',
      'fb/github-code-sync',
      'fb/impact-of-draft-js',
      'fb/importing-p-rs',
      'fb/internal-manual-test-plan',
      'fb/internal-tools-teams-using-draft-js',
      'fb/migrating-from-draft-0-9-1-to-0-10-0',
      'fb/oncall',
      'fb/opening-a-pull-request',
      'fb/releasing-a-new-version',
      'fb/triaging-issues',
      'fb/index',
    ]),
  },
};
<|endoftext|>


<commit_before>export default {
  liveShoppingWidgetWithExpectedProduct: "[data-test-id='liveShoppingWidget'] [data-test-id='link: /item/393132']",
  addToCartButton: "[data-test-id='addToCartButton']",
  cartButton: "[data-test-id='CartButton']",
  checkoutButton: "[data-test-id='link: /checkout']",
  expectedCheckoutTitle: "[data-test-id='title: Bestellung']",
  emptyCartPlaceHolderString: "[data-test-id='emptyCartPlaceHolderString']",
  contextMenu: "[data-test-id='contextMenu']",
  contextMenuButton: "[data-test-id='contextMenuButton'] span",
  checkoutSummary: "[data-test-id='CheckoutSummary: 10']",
  shippingAdressTitle: "[data-test-id='shipingAdressTitle']",
  addShippingAdressButton: "[data-test-id='link: /checkout/selectAddress?type=shipping&selected=']",
  billingAdressTitle: "[data-test-id='billingAdressTitle']",
  addBillingAdressButton: "[data-test-id='link: /checkout/selectAddress?type=billing&selected=']",
  shippingMethodsTitle: "[data-test-id='ShippingMethodsTitle']",
  DHLShippingMethod: "[data-test-id='DHL']",
  HermesShippingMethod: "[data-test-id='Hermes']",
  UPSShippingMethod: "[data-test-id='UPS next day delivery']",
  loginPageEmailInput: "[data-test-id='LoginPage'] input[id='email']",
  loginPagePasswordInput: "[data-test-id='LoginPage'] input[id='password']",
  paymentTitle: "[data-test-id='paymentTitle']",
  AuthorizeNetPaymentMethod: "[data-test-id='Authorize.Net']",
  CashOnDeliveryPaymentMethod: "[data-test-id='Cash on delivery']",
  PaypalPaymentMethod: "[data-test-id='Paypal']",
  myCartTitle: "[data-test-id='cartTitle']",
  expectedCartitemProductWithBasePrice1: "[data-test-id='cartItem: product with base price 1']",
  expectedCartItemQuanitity: "[data-test-id='quantity: 1']",
  expectedCartItemProductWithBasePrice1Price: "[data-test-id='minPrice: 0 price: 10 currency: EUR']",
  totalComponentSpan: "[data-test-id='total'] span",
  navigatorButton: "[data-test-id='Navigator'] [data-test-id='Button']",
  loginWelcomeText: "[data-test-id='NavDrawerLoginButton'] span",
  logOutButtonNavDrawer: "[data-test-id='logoutButtonNavDrawer']",
  basicDialogOkButton: "[data-test-id='basicDialog'] [data-test-id='Button']",
  couponFieldInput: "[data-test-id='couponField'] input",
  basicDialogText: "[data-test-id='basicDialog'] span",
  deleteCouponButton: "[data-test-id='deleteCoupon']",
  navigationDrawerLoginButton: "[data-test-id='NavDrawerLoginButton']",
  shippingTotalLabel: "[data-test-id='totalLabel: shipping']",
  shippingTotalAmountDHL: "[data-test-id='shipping amount: 5.2']",
  paymentTotalLabel: "[data-test-id='totalLabel: payment']",
  paymenttotalAmountPaypal: "[data-test-id='payment amount: 2.5']",
  adressFormFirstName: "[id='firstName']",
  adressFormLastName: "[id='lastName']",
  adressFormStreet: "[id='street']",
  adressFormCity: "[id='city']",
  adressFromCountry: "[id='countryCode']",
  adressFormCountryDEOption: "Germany",
  adressFormZip: "[id='zipCode']",
  adressFormSaveButton: "[data-test-id='AddAddressButton']",
}<commit_msg>Renamed `totalComponentSpan` to `totalsComponentSpan` and updated it's value to the updated one<commit_after>export default {
  liveShoppingWidgetWithExpectedProduct: "[data-test-id='liveShoppingWidget'] [data-test-id='link: /item/393132']",
  addToCartButton: "[data-test-id='addToCartButton']",
  cartButton: "[data-test-id='CartButton']",
  checkoutButton: "[data-test-id='link: /checkout']",
  expectedCheckoutTitle: "[data-test-id='title: Bestellung']",
  emptyCartPlaceHolderString: "[data-test-id='emptyCartPlaceHolderString']",
  contextMenu: "[data-test-id='contextMenu']",
  contextMenuButton: "[data-test-id='contextMenuButton'] span",
  checkoutSummary: "[data-test-id='CheckoutSummary: 10']",
  shippingAdressTitle: "[data-test-id='shipingAdressTitle']",
  addShippingAdressButton: "[data-test-id='link: /checkout/selectAddress?type=shipping&selected=']",
  billingAdressTitle: "[data-test-id='billingAdressTitle']",
  addBillingAdressButton: "[data-test-id='link: /checkout/selectAddress?type=billing&selected=']",
  shippingMethodsTitle: "[data-test-id='ShippingMethodsTitle']",
  DHLShippingMethod: "[data-test-id='DHL']",
  HermesShippingMethod: "[data-test-id='Hermes']",
  UPSShippingMethod: "[data-test-id='UPS next day delivery']",
  loginPageEmailInput: "[data-test-id='LoginPage'] input[id='email']",
  loginPagePasswordInput: "[data-test-id='LoginPage'] input[id='password']",
  paymentTitle: "[data-test-id='paymentTitle']",
  AuthorizeNetPaymentMethod: "[data-test-id='Authorize.Net']",
  CashOnDeliveryPaymentMethod: "[data-test-id='Cash on delivery']",
  PaypalPaymentMethod: "[data-test-id='Paypal']",
  myCartTitle: "[data-test-id='cartTitle']",
  expectedCartitemProductWithBasePrice1: "[data-test-id='cartItem: product with base price 1']",
  expectedCartItemQuanitity: "[data-test-id='quantity: 1']",
  expectedCartItemProductWithBasePrice1Price: "[data-test-id='minPrice: 0 price: 10 currency: EUR']",
  totalsComponentSpan: "[data-test-id='totalsComponent'] span",
  navigatorButton: "[data-test-id='Navigator'] [data-test-id='Button']",
  loginWelcomeText: "[data-test-id='NavDrawerLoginButton'] span",
  logOutButtonNavDrawer: "[data-test-id='logoutButtonNavDrawer']",
  basicDialogOkButton: "[data-test-id='basicDialog'] [data-test-id='Button']",
  couponFieldInput: "[data-test-id='couponField'] input",
  basicDialogText: "[data-test-id='basicDialog'] span",
  deleteCouponButton: "[data-test-id='deleteCoupon']",
  navigationDrawerLoginButton: "[data-test-id='NavDrawerLoginButton']",
  shippingTotalLabel: "[data-test-id='totalLabel: shipping']",
  shippingTotalAmountDHL: "[data-test-id='shipping amount: 5.2']",
  paymentTotalLabel: "[data-test-id='totalLabel: payment']",
  paymenttotalAmountPaypal: "[data-test-id='payment amount: 2.5']",
  adressFormFirstName: "[id='firstName']",
  adressFormLastName: "[id='lastName']",
  adressFormStreet: "[id='street']",
  adressFormCity: "[id='city']",
  adressFromCountry: "[id='countryCode']",
  adressFormCountryDEOption: "Germany",
  adressFormZip: "[id='zipCode']",
  adressFormSaveButton: "[data-test-id='AddAddressButton']",
}
<|endoftext|>


<commit_before>/*
Gradebook from Names and Scores
I worked on this challenge [by myself, with:]
This challenge took me [#] hours.
You will work with the following two variables.  The first, students, holds the names of four students.
The second, scores, holds groups of test scores.  The relative positions of elements within the two
variables match (i.e., 'Joseph' is the first element in students; his scores are the first value in scores.).
Do not alter the students and scores code.
*/

var students = ["Joseph", "Susan", "William", "Elizabeth"]

var scores = [ [80, 70, 70, 100],
               [85, 80, 90, 90],
               [75, 70, 80, 75],
               [100, 90, 95, 85] ]

// __________________________________________
// Write your code below.

for (var i = 0; i < students.length; i ++) {
  scores.forEach(function(testScores, i){
    gradebook[students[i]]['testScores'] = testScores
  })
}

gradebook.addScore = function(name, scorearguments){
  gradebook[name].testScores.push(scorearguments)
}

gradebook.getAverage = function(name){
  return average(gradebook[name].testScores)
};

function average(array){
  return array.reduce((a, b) => a + b)/array.length;
}

console.log(gradebook)

// __________________________________________
// Refactored Solution

  scores.forEach(function(testScores, i){
    gradebook[students[i]]['testScores'] = testScores
  })

gradebook.addScore = function(name, scorearguments){
  gradebook[name].testScores.push(scorearguments)
}

gradebook.getAverage = function(name){
  return average(gradebook[name].testScores)
};

function average(array){
  return array.reduce((a, b) => a + b)/array.length;
}`

// __________________________________________
// Reflect

// __________________________________________
// Test Code:  Do not alter code below this line.

function assert(test, message, test_number) {
  if (!test) {
    console.log(test_number + "false");
    throw "ERROR: " + message;
  }
  console.log(test_number + "true");
  return true;
}

assert(
  (gradebook instanceof Object),
  "The value of gradebook should be an Object.\n",
  "1. "
)

assert(
  (gradebook["Elizabeth"] instanceof Object),
  "gradebook's Elizabeth property should be an object.",
  "2. "
)

assert(
  (gradebook.William.testScores === scores[2]),
  "William's testScores should equal the third element in scores.",
  "3. "
)

assert(
  (gradebook.addScore instanceof Function),
  "The value of gradebook's addScore property should be a Function.",
  "4. "
)

gradebook.addScore("Susan", 80)

assert(
  (gradebook.Susan.testScores.length === 5
   && gradebook.Susan.testScores[4] === 80),
  "Susan's testScores should have a new score of 80 added to the end.",
  "5. "
)

assert(
  (gradebook.getAverage instanceof Function),
  "The value of gradebook's getAverage property should be a Function.",
  "6. "
)

assert(
  (average instanceof Function),
  "The value of average should be a Function.\n",
  "7. "
)
assert(
  average([1, 2, 3]) === 2,
  "average should return the average of the elements in the array argument.\n",
  "8. "
)

assert(
  (gradebook.getAverage("Joseph") === 80),
  "gradebook's getAverage should return 80 if passed 'Joseph'.",
  "9. "
)<commit_msg>add reflection to file<commit_after>/*
Gradebook from Names and Scores
I worked on this challenge [by myself, with:]
This challenge took me [#] hours.
You will work with the following two variables.  The first, students, holds the names of four students.
The second, scores, holds groups of test scores.  The relative positions of elements within the two
variables match (i.e., 'Joseph' is the first element in students; his scores are the first value in scores.).
Do not alter the students and scores code.
*/

var students = ["Joseph", "Susan", "William", "Elizabeth"]

var scores = [ [80, 70, 70, 100],
               [85, 80, 90, 90],
               [75, 70, 80, 75],
               [100, 90, 95, 85] ]

// __________________________________________
// Write your code below.

// var gradebook = {};
// students.forEach(function(students) {
//                  gradebook[students] = {}
//                 })

// for (var i = 0; i < students.length; i ++) {
//   scores.forEach(function(testScores, i){
//     gradebook[students[i]]['testScores'] = testScores
//   })
// }

// gradebook.addScore = function(name, scorearguments){
//   gradebook[name].testScores.push(scorearguments)
// }

// gradebook.getAverage = function(name){
//   return average(gradebook[name].testScores)
// };

// function average(array){
//   return array.reduce((a, b) => a + b)/array.length;
// }

// console.log(gradebook)

// __________________________________________
// Refactored Solution

var gradebook = {};
students.forEach(function(students) {
               gradebook[students] = {}
              })

scores.forEach(function(testScores, i){
  gradebook[students[i]]['testScores'] = testScores
})

gradebook.addScore = function(name, scorearguments){
  gradebook[name].testScores.push(scorearguments)
}

gradebook.getAverage = function(name){
  return average(gradebook[name].testScores)
};

function average(array){
  return array.reduce((a, b) => a + b)/array.length;
}

// __________________________________________
// Reflect

//  What did you learn about adding functions to objects? - I learned when adding functions to objects and iterating through an array, it's important to not put quotes around the variable that changes. Otherwise the variable won't change. Also when assigning variables to names, you can't use dot notation to add that variable; only bracket notation will work.
// How did you iterate over nested arrays in JavaScript? - I iterated over nested arrays using.forEach, using both an index and a variable. It allowed us to work how .each_with_index works in Ruby.
//  Were there any new methods you were able to incorporate? If so, what were they and how did they work? - No new methods in this challenge, but a good refresher on how .forEach works and how important it is in JS methods to take in arguments.

// __________________________________________
// Test Code:  Do not alter code below this line.

function assert(test, message, test_number) {
  if (!test) {
    console.log(test_number + "false");
    throw "ERROR: " + message;
  }
  console.log(test_number + "true");
  return true;
}

assert(
  (gradebook instanceof Object),
  "The value of gradebook should be an Object.\n",
  "1. "
)

assert(
  (gradebook["Elizabeth"] instanceof Object),
  "gradebook's Elizabeth property should be an object.",
  "2. "
)

assert(
  (gradebook.William.testScores === scores[2]),
  "William's testScores should equal the third element in scores.",
  "3. "
)

assert(
  (gradebook.addScore instanceof Function),
  "The value of gradebook's addScore property should be a Function.",
  "4. "
)

gradebook.addScore("Susan", 80)

assert(
  (gradebook.Susan.testScores.length === 5
   && gradebook.Susan.testScores[4] === 80),
  "Susan's testScores should have a new score of 80 added to the end.",
  "5. "
)

assert(
  (gradebook.getAverage instanceof Function),
  "The value of gradebook's getAverage property should be a Function.",
  "6. "
)

assert(
  (average instanceof Function),
  "The value of average should be a Function.\n",
  "7. "
)
assert(
  average([1, 2, 3]) === 2,
  "average should return the average of the elements in the array argument.\n",
  "8. "
)

assert(
  (gradebook.getAverage("Joseph") === 80),
  "gradebook's getAverage should return 80 if passed 'Joseph'.",
  "9. "
)<|endoftext|>


<commit_before>"use strict";
import Component from 'can/component/';
import Map from 'can/map/';
import List from 'can/list/';
import 'can/map/define/';
import './modify-ajax2.less!';
import template from './modify-ajax2.stache!';
import Fixture from 'can/util/fixture/';

export const ViewModel = Map.extend({
	myArray: new List()
});

Fixture("GET /api/simple-ajax", function() {
	return {
		myArray: [1,2,3,4,5]
	}
});

export default Component.extend({
  tag: 'modify-ajax-2',
  viewModel: ViewModel,
	init() {
		this.getArray();
	},
  template,
	getArray() {
		can.ajax({
			'url': '/api/simple-ajax',
			success: function(response) {
				this.viewModel.myArray.attr(response.myArray);
			}.bind(this)
		});
	}
});
<commit_msg>actually modified<commit_after>"use strict";
import Component from 'can/component/';
import Map from 'can/map/';
import List from 'can/list/';
import 'can/map/define/';
import './modify-ajax2.less!';
import template from './modify-ajax2.stache!';
import Fixture from 'can/util/fixture/';

export const ViewModel = Map.extend({
	myArray: new List()
});

Fixture("GET /api/simple-ajax", function() {
	return {
		myArray: [1,2,3,4,5]
	}
});

export default Component.extend({
  tag: 'modify-ajax-2',
  viewModel: ViewModel,
	init() {
		this.getArray();
	},
  template,
	getArray() {
		can.ajax({
			'url': '/api/simple-ajax',
			success: function(response) {
				response.myArray.push("modified");
				this.viewModel.myArray.attr(response.myArray);
			}.bind(this)
		});
	}
});
<|endoftext|>


<commit_before>if (typeof sibilino === "undefined") {
    sibilino = {}; // Initialize sibilino as empty object if not existing
}
sibilino.openlayers = (function ($) {
    var pub = {
        isActive: true,
        init: function () {
            // Nothing to init
        },
        // Storage for additional options that may be set by other javascript scripts
        // Usage: sibilino.openlayers.mapOptions["identifier"] = { layers: [new ol.layer.Vector(...)] }
        // Then, configure the OpenLayers widget with $jsOptionIdentifier = "identifier".
        mapOptions: {},
        createMap: function (options, id, jsOptionIdentifier) {
            if (typeof id === "undefined") {
                id = options['target'];
            }
            if (typeof jsOptionIdentifier !== "undefined") {
                $.extend(true, options, mapOptions[jsOptionIdentifier]); // Deep merge
            }
            maps[id] = new ol.Map(options);
        },
        maps: {},
        getMapById: function (id) {
            return maps[id];
        }
    };
    // ... private functions and properties go here ...
    return pub;
})(jQuery);

jQuery(document).ready(function () {
    yii.initModule(sibilino);
});
<commit_msg>mapOptions are stored by mapId<commit_after>if (typeof sibilino === "undefined") {
    sibilino = {}; // Initialize sibilino as empty object if not existing
}
sibilino.openlayers = (function ($) {
    var pub = {
        isActive: true,
        init: function () {
            // Nothing to init
        },
        // Storage for additional options that may be set by other javascript scripts
        // Usage: sibilino.openlayers.mapOptions[mapId] = { layers: [new ol.layer.Vector(...)] }
        // Then, configure the OpenLayers widget with "id" equal to mapId
        mapOptions: {},
        // Create the map with the options coming from PHP as the "options" argument.
        // Options will be merged with mapOptions[id], if existing.
        createMap: function (options, id) {
            if (typeof id === "undefined") {
                id = options['target'];
            }
            if (mapOptions[id]) {
                $.extend(true, options, mapOptions[id]); // Deep merge of mapOptions into options
            }
            maps[id] = new ol.Map(options);
        },
        maps: {},
        getMapById: function (id) {
            return maps[id];
        }
    };

    return pub;
})(jQuery);

jQuery(document).ready(function () {
    yii.initModule(sibilino);
});
<|endoftext|>


<commit_before>/* @flow */
import queryString from 'query-string'
import { decodeToken } from 'jsontokens'
import protocolCheck from 'custom-protocol-detection-blockstack'
import { makeAuthRequest, verifyAuthResponse } from './index'
import { BLOCKSTACK_HANDLER, isLaterVersion, hexStringToECPair } from '../utils'
import { getAddressFromDID, makeECPrivateKey } from '../index'
import { LoginFailedError } from '../errors'
import { decryptPrivateKey } from './authMessages'
import {
  BLOCKSTACK_APP_PRIVATE_KEY_LABEL,
  BLOCKSTACK_STORAGE_LABEL,
  BLOCKSTACK_DEFAULT_GAIA_HUB_URL,
  DEFAULT_BLOCKSTACK_HOST,
  DEFAULT_SCOPE
} from './authConstants'

import { BLOCKSTACK_GAIA_HUB_LABEL } from '../storage'

import { extractProfile } from '../profiles'

import { Logger } from '../logger'

const DEFAULT_PROFILE = {
  '@type': 'Person',
  '@context': 'http://schema.org'
}

/**
 * Generates a ECDSA keypair and stores the hex value of the private key in
 * local storage.
 * @return {String} the hex encoded private key
 * @private
 */
export function generateAndStoreTransitKey() {
  const transitKey = makeECPrivateKey()
  localStorage.setItem(BLOCKSTACK_APP_PRIVATE_KEY_LABEL, transitKey)
  return transitKey
}

/**
 * Fetches the hex value of the transit private key from local storage.
 * @return {String} the hex encoded private key
 * @private
 */
export function getTransitKey() : string {
  const transitKey = localStorage.getItem(BLOCKSTACK_APP_PRIVATE_KEY_LABEL)
  return ((transitKey: any): string)
}

/**
 * Check if a user is currently signed in.
 * @return {Boolean} `true` if the user is signed in, `false` if not.
 */
export function isUserSignedIn() {
  return !!window.localStorage.getItem(BLOCKSTACK_STORAGE_LABEL)
}

/**
 * Redirects the user to the Blockstack browser to approve the sign in request
 * given.
 *
 * The user is redirected to the `blockstackIDHost` if the `blockstack:`
 * protocol handler is not detected. Please note that the protocol handler detection
 * does not work on all browsers.
 * @param  {String} authRequest - the authentication request generated by `makeAuthRequest`
 * @param  {String} blockstackIDHost - the URL to redirect the user to if the blockstack
 *                                     protocol handler is not detected
 * @return {void}
 */
export function redirectToSignInWithAuthRequest(authRequest: string = makeAuthRequest(),
                                                blockstackIDHost: string =
                                                DEFAULT_BLOCKSTACK_HOST) {
  const protocolURI = `${BLOCKSTACK_HANDLER}:${authRequest}`
  const httpsURI = `${blockstackIDHost}?authRequest=${authRequest}`
  function successCallback() {
    Logger.info('protocol handler detected')
    // protocolCheck should open the link for us
  }

  function failCallback() {
    Logger.warn('protocol handler not detected')
    window.location = httpsURI
  }

  function unsupportedBrowserCallback() {
    if (/iPad|iPhone|iPod/.test(navigator.platform)) {
      // iOS doesn’t do protocols (yet)
      Logger.warn('platform does not support custom protocols, sending to https')
      window.location = httpsURI
    }
    else {
      // Safari is unsupported by protocolCheck
      Logger.warn('can not detect custom protocols on this browser')
      window.location = protocolURI
    }
  }

  protocolCheck(protocolURI, failCallback, successCallback, unsupportedBrowserCallback)
}

/**
 * Generates an authentication request and redirects the user to the Blockstack
 * browser to approve the sign in request.
 *
 * Please note that this requires that the web browser properly handles the
 * `blockstack:` URL protocol handler.
 *
 * Most applications should use this
 * method for sign in unless they require more fine grained control over how the
 * authentication request is generated. If your app falls into this category,
 * use `generateAndStoreTransitKey`, `makeAuthRequest`,
 * and `redirectToSignInWithAuthRequest` to build your own sign in process.
 *
 * @param {String} [redirectURI=`${window.location.origin}/`]
 * The location to which the identity provider will redirect the user after
 * the user approves sign in.
 * @param  {String} [manifestURI=`${window.location.origin}/manifest.json`]
 * Location of the manifest file.
 * @param  {Array} [scopes=DEFAULT_SCOPE] Defaults to requesting write access to
 * this app's data store.
 * An array of strings indicating which permissions this app is requesting.
 * @return {void}
 */
export function redirectToSignIn(redirectURI: string = `${window.location.origin}/`,
                                 manifestURI: string = `${window.location.origin}/manifest.json`,
                                 scopes: Array<string> = DEFAULT_SCOPE) {
  const authRequest = makeAuthRequest(
    generateAndStoreTransitKey(), redirectURI, manifestURI, scopes
  )
  redirectToSignInWithAuthRequest(authRequest)
}

/**
 * Retrieve the authentication token from the URL query
 * @return {String} the authentication token if it exists otherwise `null`
 */
export function getAuthResponseToken(): string {
  const queryDict = queryString.parse(location.search)
  return queryDict.authResponse ? queryDict.authResponse : ''
}

/**
 * Check if there is a authentication request that hasn't been handled.
 * @return {Boolean} `true` if there is a pending sign in, otherwise `false`
 */
export function isSignInPending() {
  return !!getAuthResponseToken()
}

/**
 * Try to process any pending sign in request by returning a `Promise` that resolves
 * to the user data object if the sign in succeeds.
 *
 * @param {String} nameLookupURL - the endpoint against which to verify public
 * keys match claimed username
 * @param {String} authResponseToken - the signed authentication response token
 * @param {String} transitKey - the transit private key that corresponds to the transit public key
 * that was provided in the authentication request
 * @return {Promise} that resolves to the user data object if successful and rejects
 * if handling the sign in request fails or there was no pending sign in request.
 */
export function handlePendingSignIn(nameLookupURL: string = 'https://core.blockstack.org/v1/names/',
                                    authResponseToken: string = getAuthResponseToken(),
                                    transitKey: string = getTransitKey()) {
  return verifyAuthResponse(authResponseToken, nameLookupURL)
    .then((isValid) => {
      if (!isValid) {
        throw new LoginFailedError('Invalid authentication response.')
      }
      const tokenPayload = decodeToken(authResponseToken).payload
      // TODO: real version handling
      let appPrivateKey = tokenPayload.private_key
      let coreSessionToken = tokenPayload.core_token
      if (isLaterVersion(tokenPayload.version, '1.1.0')) {
        if (transitKey !== undefined && transitKey != null) {
          if (tokenPayload.private_key !== undefined && tokenPayload.private_key !== null) {
            try {
              appPrivateKey = decryptPrivateKey(transitKey, tokenPayload.private_key)
            } catch (e) {
              Logger.warn('Failed decryption of appPrivateKey, will try to use as given')
              try {
                hexStringToECPair(tokenPayload.private_key)
              } catch (ecPairError) {
                throw new LoginFailedError('Failed decrypting appPrivateKey. Usually means'
                                         + ' that the transit key has changed during login.')
              }
            }
          }
          if (coreSessionToken !== undefined && coreSessionToken !== null) {
            try {
              coreSessionToken = decryptPrivateKey(transitKey, coreSessionToken)
            } catch (e) {
              Logger.info('Failed decryption of coreSessionToken, will try to use as given')
            }
          }
        } else {
          throw new LoginFailedError('Authenticating with protocol > 1.1.0 requires transit'
                                   + ' key, and none found.')
        }
      }
      let hubUrl = BLOCKSTACK_DEFAULT_GAIA_HUB_URL
      if (isLaterVersion(tokenPayload.version, '1.2.0')
        && tokenPayload.hubUrl !== null && tokenPayload.hubUrl !== undefined) {
        hubUrl = tokenPayload.hubUrl
      }

      const userData = {
        username: tokenPayload.username,
        profile: tokenPayload.profile,
        decentralizedID: tokenPayload.iss,
        identityAddress: getAddressFromDID(tokenPayload.iss),
        appPrivateKey,
        coreSessionToken,
        authResponseToken,
        hubUrl
      }
      const profileURL = tokenPayload.profile_url
      if ((userData.profile === null
         || userData.profile === undefined)
        && profileURL !== undefined && profileURL !== null) {
        return fetch(profileURL)
          .then((response) => {
            if (!response.ok) { // return blank profile if we fail to fetch
              userData.profile = Object.assign({}, DEFAULT_PROFILE)
              window.localStorage.setItem(
                BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)
              )
              return userData
            } else {
              return response.text()
                .then(responseText => JSON.parse(responseText))
                .then(wrappedProfile => extractProfile(wrappedProfile[0].token))
                .then((profile) => {
                  userData.profile = profile
                  window.localStorage.setItem(
                    BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)
                  )
                  return userData
                })
            }
          })
      } else {
        userData.profile = tokenPayload.profile
        window.localStorage.setItem(
          BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)
        )
        return userData
      }
    })
}

/**
 * Retrieves the user data object. The user's profile is stored in the key `profile`.
 * @return {Object} User data object.
 */
export function loadUserData() {
  return JSON.parse(window.localStorage.getItem(BLOCKSTACK_STORAGE_LABEL))
}

/**
 * Sign the user out and optionally redirect to given location.
 * @param  {String} [redirectURL=null] Location to redirect user to after sign out.
 * @return {void}
 */
export function signUserOut(redirectURL: ?string = null) {
  window.localStorage.removeItem(BLOCKSTACK_STORAGE_LABEL)
  window.localStorage.removeItem(BLOCKSTACK_GAIA_HUB_LABEL)

  if (redirectURL !== null) {
    window.location = redirectURL
  }
}
<commit_msg>Fix lint issue<commit_after>/* @flow */
import queryString from 'query-string'
import { decodeToken } from 'jsontokens'
import protocolCheck from 'custom-protocol-detection-blockstack'
import { makeAuthRequest, verifyAuthResponse } from './index'
import { BLOCKSTACK_HANDLER, isLaterVersion, hexStringToECPair } from '../utils'
import { getAddressFromDID, makeECPrivateKey } from '../index'
import { LoginFailedError } from '../errors'
import { decryptPrivateKey } from './authMessages'
import {
  BLOCKSTACK_APP_PRIVATE_KEY_LABEL,
  BLOCKSTACK_STORAGE_LABEL,
  BLOCKSTACK_DEFAULT_GAIA_HUB_URL,
  DEFAULT_BLOCKSTACK_HOST,
  DEFAULT_SCOPE
} from './authConstants'

import { BLOCKSTACK_GAIA_HUB_LABEL } from '../storage'

import { extractProfile } from '../profiles'

import { Logger } from '../logger'

const DEFAULT_PROFILE = {
  '@type': 'Person',
  '@context': 'http://schema.org'
}

/**
 * Generates a ECDSA keypair and stores the hex value of the private key in
 * local storage.
 * @return {String} the hex encoded private key
 * @private
 */
export function generateAndStoreTransitKey() {
  const transitKey = makeECPrivateKey()
  localStorage.setItem(BLOCKSTACK_APP_PRIVATE_KEY_LABEL, transitKey)
  return transitKey
}

/**
 * Fetches the hex value of the transit private key from local storage.
 * @return {String} the hex encoded private key
 * @private
 */
export function getTransitKey() : string {
  const transitKey = localStorage.getItem(BLOCKSTACK_APP_PRIVATE_KEY_LABEL)
  return ((transitKey: any): string)
}

/**
 * Check if a user is currently signed in.
 * @return {Boolean} `true` if the user is signed in, `false` if not.
 */
export function isUserSignedIn() {
  return !!window.localStorage.getItem(BLOCKSTACK_STORAGE_LABEL)
}

/**
 * Redirects the user to the Blockstack browser to approve the sign in request
 * given.
 *
 * The user is redirected to the `blockstackIDHost` if the `blockstack:`
 * protocol handler is not detected. Please note that the protocol handler detection
 * does not work on all browsers.
 * @param  {String} authRequest - the authentication request generated by `makeAuthRequest`
 * @param  {String} blockstackIDHost - the URL to redirect the user to if the blockstack
 *                                     protocol handler is not detected
 * @return {void}
 */
export function redirectToSignInWithAuthRequest(authRequest: string = makeAuthRequest(),
                                                blockstackIDHost: string =
                                                DEFAULT_BLOCKSTACK_HOST) {
  const protocolURI = `${BLOCKSTACK_HANDLER}:${authRequest}`
  const httpsURI = `${blockstackIDHost}?authRequest=${authRequest}`
  function successCallback() {
    Logger.info('protocol handler detected')
    // protocolCheck should open the link for us
  }

  function failCallback() {
    Logger.warn('protocol handler not detected')
    window.location = httpsURI
  }

  function unsupportedBrowserCallback() {
    if (/iPad|iPhone|iPod/.test(navigator.platform)) {
      // iOS doesn’t do protocols (yet)
      Logger.warn('platform does not support custom protocols, sending to https')
      window.location = httpsURI
    } else {
      // Safari is unsupported by protocolCheck
      Logger.warn('can not detect custom protocols on this browser')
      window.location = protocolURI
    }
  }

  protocolCheck(protocolURI, failCallback, successCallback, unsupportedBrowserCallback)
}

/**
 * Generates an authentication request and redirects the user to the Blockstack
 * browser to approve the sign in request.
 *
 * Please note that this requires that the web browser properly handles the
 * `blockstack:` URL protocol handler.
 *
 * Most applications should use this
 * method for sign in unless they require more fine grained control over how the
 * authentication request is generated. If your app falls into this category,
 * use `generateAndStoreTransitKey`, `makeAuthRequest`,
 * and `redirectToSignInWithAuthRequest` to build your own sign in process.
 *
 * @param {String} [redirectURI=`${window.location.origin}/`]
 * The location to which the identity provider will redirect the user after
 * the user approves sign in.
 * @param  {String} [manifestURI=`${window.location.origin}/manifest.json`]
 * Location of the manifest file.
 * @param  {Array} [scopes=DEFAULT_SCOPE] Defaults to requesting write access to
 * this app's data store.
 * An array of strings indicating which permissions this app is requesting.
 * @return {void}
 */
export function redirectToSignIn(redirectURI: string = `${window.location.origin}/`,
                                 manifestURI: string = `${window.location.origin}/manifest.json`,
                                 scopes: Array<string> = DEFAULT_SCOPE) {
  const authRequest = makeAuthRequest(
    generateAndStoreTransitKey(), redirectURI, manifestURI, scopes
  )
  redirectToSignInWithAuthRequest(authRequest)
}

/**
 * Retrieve the authentication token from the URL query
 * @return {String} the authentication token if it exists otherwise `null`
 */
export function getAuthResponseToken(): string {
  const queryDict = queryString.parse(location.search)
  return queryDict.authResponse ? queryDict.authResponse : ''
}

/**
 * Check if there is a authentication request that hasn't been handled.
 * @return {Boolean} `true` if there is a pending sign in, otherwise `false`
 */
export function isSignInPending() {
  return !!getAuthResponseToken()
}

/**
 * Try to process any pending sign in request by returning a `Promise` that resolves
 * to the user data object if the sign in succeeds.
 *
 * @param {String} nameLookupURL - the endpoint against which to verify public
 * keys match claimed username
 * @param {String} authResponseToken - the signed authentication response token
 * @param {String} transitKey - the transit private key that corresponds to the transit public key
 * that was provided in the authentication request
 * @return {Promise} that resolves to the user data object if successful and rejects
 * if handling the sign in request fails or there was no pending sign in request.
 */
export function handlePendingSignIn(nameLookupURL: string = 'https://core.blockstack.org/v1/names/',
                                    authResponseToken: string = getAuthResponseToken(),
                                    transitKey: string = getTransitKey()) {
  return verifyAuthResponse(authResponseToken, nameLookupURL)
    .then((isValid) => {
      if (!isValid) {
        throw new LoginFailedError('Invalid authentication response.')
      }
      const tokenPayload = decodeToken(authResponseToken).payload
      // TODO: real version handling
      let appPrivateKey = tokenPayload.private_key
      let coreSessionToken = tokenPayload.core_token
      if (isLaterVersion(tokenPayload.version, '1.1.0')) {
        if (transitKey !== undefined && transitKey != null) {
          if (tokenPayload.private_key !== undefined && tokenPayload.private_key !== null) {
            try {
              appPrivateKey = decryptPrivateKey(transitKey, tokenPayload.private_key)
            } catch (e) {
              Logger.warn('Failed decryption of appPrivateKey, will try to use as given')
              try {
                hexStringToECPair(tokenPayload.private_key)
              } catch (ecPairError) {
                throw new LoginFailedError('Failed decrypting appPrivateKey. Usually means'
                                         + ' that the transit key has changed during login.')
              }
            }
          }
          if (coreSessionToken !== undefined && coreSessionToken !== null) {
            try {
              coreSessionToken = decryptPrivateKey(transitKey, coreSessionToken)
            } catch (e) {
              Logger.info('Failed decryption of coreSessionToken, will try to use as given')
            }
          }
        } else {
          throw new LoginFailedError('Authenticating with protocol > 1.1.0 requires transit'
                                   + ' key, and none found.')
        }
      }
      let hubUrl = BLOCKSTACK_DEFAULT_GAIA_HUB_URL
      if (isLaterVersion(tokenPayload.version, '1.2.0')
        && tokenPayload.hubUrl !== null && tokenPayload.hubUrl !== undefined) {
        hubUrl = tokenPayload.hubUrl
      }

      const userData = {
        username: tokenPayload.username,
        profile: tokenPayload.profile,
        decentralizedID: tokenPayload.iss,
        identityAddress: getAddressFromDID(tokenPayload.iss),
        appPrivateKey,
        coreSessionToken,
        authResponseToken,
        hubUrl
      }
      const profileURL = tokenPayload.profile_url
      if ((userData.profile === null
         || userData.profile === undefined)
        && profileURL !== undefined && profileURL !== null) {
        return fetch(profileURL)
          .then((response) => {
            if (!response.ok) { // return blank profile if we fail to fetch
              userData.profile = Object.assign({}, DEFAULT_PROFILE)
              window.localStorage.setItem(
                BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)
              )
              return userData
            } else {
              return response.text()
                .then(responseText => JSON.parse(responseText))
                .then(wrappedProfile => extractProfile(wrappedProfile[0].token))
                .then((profile) => {
                  userData.profile = profile
                  window.localStorage.setItem(
                    BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)
                  )
                  return userData
                })
            }
          })
      } else {
        userData.profile = tokenPayload.profile
        window.localStorage.setItem(
          BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)
        )
        return userData
      }
    })
}

/**
 * Retrieves the user data object. The user's profile is stored in the key `profile`.
 * @return {Object} User data object.
 */
export function loadUserData() {
  return JSON.parse(window.localStorage.getItem(BLOCKSTACK_STORAGE_LABEL))
}

/**
 * Sign the user out and optionally redirect to given location.
 * @param  {String} [redirectURL=null] Location to redirect user to after sign out.
 * @return {void}
 */
export function signUserOut(redirectURL: ?string = null) {
  window.localStorage.removeItem(BLOCKSTACK_STORAGE_LABEL)
  window.localStorage.removeItem(BLOCKSTACK_GAIA_HUB_LABEL)

  if (redirectURL !== null) {
    window.location = redirectURL
  }
}
<|endoftext|>


<commit_before>load("../../../lib/DataView.js/DataView.js");

/**
 * Load SWF Dependencies
 */
var SWF = {};
load("../../swf/util.js");
load("../../swf/types.js");
load("../../swf/structs.js");
load("../../swf/tags.js");
load("../../swf/inflate.js");
load("../../swf/stream.js");
load("../../swf/templates.js");
load("../../swf/generator.js");
load("../../swf/parser.js");
load("../../swf/bitmap.js");
load("../../swf/button.js");
load("../../swf/font.js");
load("../../swf/image.js");
load("../../swf/label.js");
load("../../swf/shape.js");
load("../../swf/text.js");

/**
 * Load AVM2 Dependencies
 */

load("../util.js");
load("../options.js");

var stdout = new IndentingWriter();

var ArgumentParser = options.ArgumentParser;
var Option = options.Option;
var OptionSet = options.OptionSet;

var argumentParser = new ArgumentParser();

var systemOptions = new OptionSet("System Options");
var shellOptions = systemOptions.register(new OptionSet("AVM2 Shell Options"));
var disassemble = shellOptions.register(new Option("d", "disassemble", "boolean", false, "disassemble"));
var traceLevel = shellOptions.register(new Option("t", "traceLevel", "number", 0, "trace level"));
var traceGraphViz = shellOptions.register(new Option("v", "traceGraphViz" , "boolean", false, "trace GraphViz output"));
var execute = shellOptions.register(new Option("x", "execute", "boolean", false, "execute"));
var alwaysInterpret = shellOptions.register(new Option("i", "alwaysInterpret", "boolean", false, "always interpret"));
var compileSys = shellOptions.register(new Option("csys", "compileSystemDomain", "boolean", false, "compile system domain"));
var loadPlayerGlobal = shellOptions.register(new Option("p", "loadPlayerGlobal", "boolean", false, "load player global"));
var help = shellOptions.register(new Option("h", "help", "boolean", false, "prints help"));
var traceMetrics = shellOptions.register(new Option("tm", "traceMetrics", "boolean", false, "prints collected metrics"));

load("../constants.js");
load("../opcodes.js");
load("../parser.js");
load("../disassembler.js");
load("../analyze.js");
load("../metrics.js");

var Timer = metrics.Timer;

load("../compiler/lljs/src/estransform.js");
load("../compiler/lljs/src/escodegen.js");
load("../compiler/verifier.js");
load("../compiler/compiler.js");

load("../domain.js");
load("../runtime.js");
load("../viz.js");
load("../interpreter.js");
load("../native.js");

load("../vm.js");

argumentParser.addBoundOptionSet(systemOptions);

function printUsage() {
  stdout.writeLn("avm.js " + argumentParser.getUsage());
}

argumentParser.addArgument("h", "help", "boolean", {parse: function (x) {
  printUsage();
}});

argumentParser.addArgument("to", "traceOptions", "boolean", {parse: function (x) {
  systemOptions.trace(stdout);
}});

var file = argumentParser.addArgument("file", "file", "string", {
  positional: true
});

var argv;

try {
  argv = argumentParser.parse(arguments);
} catch (x) {
  stdout.writeLn(x.message);
  quit();
}

function grabABC(abcname) {
    return snarf("../generated/" + abcname + "/" + abcname + ".abc", "binary");
}

function installAvmPlus(vm) {
  var domain = vm.systemDomain;
  domain.installNative("getArgv", function() {
    return argv;
  });

  domain.executeAbc(new AbcFile(grabABC("avmplus"), "avmplus.abc"));
}

var vm;
if (execute.value) {
  var sysMode = alwaysInterpret.value ? ALWAYS_INTERPRET : (compileSys.value ? null : ALWAYS_INTERPRET);
  var appMode = alwaysInterpret.value ? ALWAYS_INTERPRET : null;
  vm = new AVM2(grabABC("builtin"), sysMode, appMode);
  installAvmPlus(vm);
  if (loadPlayerGlobal.value) {
    vm.loadPlayerGlobal(snarf("../generated/playerGlobal.swf", "binary"));
  } else {
    vm.systemDomain.executeAbc(new AbcFile(grabABC("shell"), "shell.abc"));
  }
}

if (file.value.endsWith(".swf")) {
  SWF.parse(snarf(file.value, "binary"), {
    oncomplete: function(result) {
      var tags = result.tags;
      for (var i = 0, n = tags.length; i < n; i++) {
        var tag = tags[i];
        if (tag.type === "abc") {
          processAbc(new AbcFile(tag.data, file.value + " [Tag ID: " + i + "]"));
        } else if (tag.type === "symbols") {
          for (var j = tag.references.length - 1; j >= 0; j--) {
            if (tag.references[j].id === 0) {
              vm.applicationDomain.getProperty(
                Multiname.fromSimpleName(tag.references[j].name),
                true, true
              );
              break;
            }
          }
        }
      }
    }
  });
} else {
  assert(file.value.endsWith(".abc"));
  processAbc(new AbcFile(snarf(file.value, "binary"), file.value));
}

function processAbc(abc) {
  var methodBodies = abc.methodBodies;

  if (disassemble.value) {
    abc.trace(stdout);
  }

  if (traceGraphViz.value) {
    stdout.enter("digraph {");
    var graph = 0;
    var opts = { massage: true };
    abc.methods.forEach(function (method) {
      method.analysis = new Analysis(method, opts);
      method.analysis.analyzeControlFlow();
      method.analysis.restructureControlFlow();
      if (method.analysis) {
        method.analysis.traceCFG(stdout, method, "G" + graph + "_");
        graph += 1;
      }
    });
    stdout.leave("}");
  }

  if (execute.value) {
    assert(vm);
    try {
      vm.applicationDomain.executeAbc(abc);
    } catch(e) {
      print(e);
      print("");
      print(e.stack);
    }

    if (traceLevel.value > 4) {
      /* Spew analysis information if not quiet. */
      stdout.enter("analyses {");
      abc.methods.forEach(function (method) {
        if (method.analysis) {
          method.analysis.trace(stdout);
        }
      });
      stdout.leave("}");
    }
  }
}

if (traceMetrics.value) {
  metrics.Timer.trace(stdout);
}
<commit_msg>Measure startup performance of AVM2.<commit_after>load("../../../lib/DataView.js/DataView.js");

/**
 * Load AVM2 Dependencies
 */

load("../util.js");
load("../options.js");

var stdout = new IndentingWriter();

var ArgumentParser = options.ArgumentParser;
var Option = options.Option;
var OptionSet = options.OptionSet;

var argumentParser = new ArgumentParser();

var systemOptions = new OptionSet("System Options");
var shellOptions = systemOptions.register(new OptionSet("AVM2 Shell Options"));
var disassemble = shellOptions.register(new Option("d", "disassemble", "boolean", false, "disassemble"));
var traceLevel = shellOptions.register(new Option("t", "traceLevel", "number", 0, "trace level"));
var traceGraphViz = shellOptions.register(new Option("v", "traceGraphViz" , "boolean", false, "trace GraphViz output"));
var execute = shellOptions.register(new Option("x", "execute", "boolean", false, "execute"));
var alwaysInterpret = shellOptions.register(new Option("i", "alwaysInterpret", "boolean", false, "always interpret"));
var compileSys = shellOptions.register(new Option("csys", "compileSystemDomain", "boolean", false, "compile system domain"));
var loadPlayerGlobal = shellOptions.register(new Option("p", "loadPlayerGlobal", "boolean", false, "load player global"));
var help = shellOptions.register(new Option("h", "help", "boolean", false, "prints help"));
var traceMetrics = shellOptions.register(new Option("tm", "traceMetrics", "boolean", false, "prints collected metrics"));

load("../metrics.js");
var Timer = metrics.Timer;
var Counter = new metrics.Counter();

Timer.start("Loading VM");

load("../constants.js");
load("../opcodes.js");
load("../parser.js");
load("../disassembler.js");
load("../analyze.js");

/**
 * Load SWF Dependencies
 */
var SWF = {};
Timer.start("Loading SWF Dependencies");
load("../../swf/util.js");
load("../../swf/types.js");
load("../../swf/structs.js");
load("../../swf/tags.js");
load("../../swf/inflate.js");
load("../../swf/stream.js");
load("../../swf/templates.js");
load("../../swf/generator.js");
Timer.start("Loading SWF Parser");
load("../../swf/parser.js");
Timer.stop();
load("../../swf/bitmap.js");
load("../../swf/button.js");
load("../../swf/font.js");
load("../../swf/image.js");
load("../../swf/label.js");
load("../../swf/shape.js");
load("../../swf/text.js");
Timer.stop();

Timer.start("Loading Compiler");
load("../compiler/lljs/src/estransform.js");
load("../compiler/lljs/src/escodegen.js");
load("../compiler/verifier.js");
load("../compiler/compiler.js");
Timer.stop();

load("../domain.js");
load("../runtime.js");
load("../viz.js");
load("../interpreter.js");
load("../native.js");
load("../vm.js");
Timer.stop();

argumentParser.addBoundOptionSet(systemOptions);

function printUsage() {
  stdout.writeLn("avm.js " + argumentParser.getUsage());
}

argumentParser.addArgument("h", "help", "boolean", {parse: function (x) {
  printUsage();
}});

argumentParser.addArgument("to", "traceOptions", "boolean", {parse: function (x) {
  systemOptions.trace(stdout);
}});

var file = argumentParser.addArgument("file", "file", "string", {
  positional: true
});

var argv;

try {
  argv = argumentParser.parse(arguments);
} catch (x) {
  stdout.writeLn(x.message);
  quit();
}

Counter.setEnabled(traceMetrics.value);

function grabABC(abcname) {
    return snarf("../generated/" + abcname + "/" + abcname + ".abc", "binary");
}

function installAvmPlus(vm) {
  var domain = vm.systemDomain;
  domain.installNative("getArgv", function() {
    return argv;
  });

  domain.executeAbc(new AbcFile(grabABC("avmplus"), "avmplus.abc"));
}

var vm;
if (execute.value) {
  var sysMode = alwaysInterpret.value ? ALWAYS_INTERPRET : (compileSys.value ? null : ALWAYS_INTERPRET);
  var appMode = alwaysInterpret.value ? ALWAYS_INTERPRET : null;
  vm = new AVM2(grabABC("builtin"), sysMode, appMode);
  installAvmPlus(vm);
  if (loadPlayerGlobal.value) {
    vm.loadPlayerGlobal(snarf("../generated/playerGlobal.swf", "binary"));
  } else {
    vm.systemDomain.executeAbc(new AbcFile(grabABC("shell"), "shell.abc"));
  }
}

if (file.value.endsWith(".swf")) {
  SWF.parse(snarf(file.value, "binary"), {
    oncomplete: function(result) {
      var tags = result.tags;
      for (var i = 0, n = tags.length; i < n; i++) {
        var tag = tags[i];
        if (tag.type === "abc") {
          processAbc(new AbcFile(tag.data, file.value + " [Tag ID: " + i + "]"));
        } else if (tag.type === "symbols") {
          for (var j = tag.references.length - 1; j >= 0; j--) {
            if (tag.references[j].id === 0) {
              vm.applicationDomain.getProperty(
                Multiname.fromSimpleName(tag.references[j].name),
                true, true
              );
              break;
            }
          }
        }
      }
    }
  });
} else {
  assert(file.value.endsWith(".abc"));
  processAbc(new AbcFile(snarf(file.value, "binary"), file.value));
}

function processAbc(abc) {
  var methodBodies = abc.methodBodies;

  if (disassemble.value) {
    abc.trace(stdout);
  }

  if (traceGraphViz.value) {
    stdout.enter("digraph {");
    var graph = 0;
    var opts = { massage: true };
    abc.methods.forEach(function (method) {
      method.analysis = new Analysis(method, opts);
      method.analysis.analyzeControlFlow();
      method.analysis.restructureControlFlow();
      if (method.analysis) {
        method.analysis.traceCFG(stdout, method, "G" + graph + "_");
        graph += 1;
      }
    });
    stdout.leave("}");
  }

  if (execute.value) {
    assert(vm);
    try {
      vm.applicationDomain.executeAbc(abc);
    } catch(e) {
      print(e);
      print("");
      print(e.stack);
    }

    if (traceLevel.value > 4) {
      /* Spew analysis information if not quiet. */
      stdout.enter("analyses {");
      abc.methods.forEach(function (method) {
        if (method.analysis) {
          method.analysis.trace(stdout);
        }
      });
      stdout.leave("}");
    }
  }
}

if (traceMetrics.value) {
  metrics.Timer.trace(stdout);
  Counter.trace(stdout);
}
<|endoftext|>


<commit_before>Axis2D.World = function World(cellSize) {
  Axis2D.typeCheck(cellSize, 'cellSize', 'Number');

  this._cellSize = Math.abs(cellSize) || 64;

  this._grid = {};

  this._colliders = [];

  // dynamic colliders are colliders that have moved or be resized
  this._dynamicColliders = [];

  this._collidersHitPerUpdate = [];

  this._debugDraw = undefined;
};

Axis2D.World.prototype = {
  createCollider: function(x, y, width, height) {
    return new Axis2D.Collider(this, x, y, width, height);
  },
  removeCollider: function(collider) {
    Axis2D.typeCheck(collider, 'collider', Axis2D.Collider);
    var collidersIndex = this._colliders.indexOf(collider),
        dynamicCollidersIndex = this._dynamicColliders.indexOf(collider);

    this._clearColliderFromGrid(collider);

    if(collidersIndex !== -1) {
      this._colliders.splice(collidersIndex, 1);
    }
    if(dynamicCollidersIndex !== -1) {
      this._dynamicColliders.splice(dynamicCollidersIndex, 1);
    }
  },
  createDebugDraw: function() {
    var d = new Axis2D.DebugDraw(this);
    this._debugDraw = d;
    return d;
  },
  debugDraw: function() {
    if(this._debugDraw) {
      this._debugDraw._draw();
    }
  },
  update: function() {
    // first pass - sweep dynamic colliders into other colliders + add hits
    this._dynamicColliders.forEach(function(collider){
      var otherColliders = [];

      // find potential hit colliders in the grid
      collider._positionInGridKeys.forEach(function(key){
        this._grid[key].forEach(function(oc){
          if(collider !== oc && otherColliders.indexOf(oc) === -1) {
            otherColliders.push(oc);
          }
        }, this);
      }, this);

      this._sweepCollisionsFixes(collider, otherColliders);
      this._placeColliderInGrid(collider);

      // cleanups
      collider._isDynamic = false;
      collider._delta.x = 0;
      collider._delta.y = 0;
    }, this);
    this._dynamicColliders = [];

    // second pass - return hits for static and dynamic
    this._collidersHitPerUpdate.forEach(function(c){
      c._calculateTouches();
      if(c._collisionCallback) {
        c._collisionCallback(c.getHits(), c.getTouches());
      }
    }, this);
    this._collidersHitPerUpdate = [];
  },
  _sweepCollisionsFixes: function(collider, otherColliders) {
    var cType = collider.getCollisionType(),
        sweep = this._sweepInto(collider, otherColliders);

    if(sweep.hit && cType !== 'sensor') {
      this._sweepMoveFowardBackCheck(sweep, collider, otherColliders);

      collider._lastHitPosition.x = sweep.hit.pos.x;
      collider._lastHitPosition.y = sweep.hit.pos.y;

      if(cType === 'slide' || cType === 'bounce') {

        if(cType === 'slide') {
          // undo the normal hit delta before slide
          if(sweep.hit.normal.x) {
            collider._delta.x = 0;
          }
          if(sweep.hit.normal.y) {
            collider._delta.y = 0;
          }
        }

        if(cType === 'bounce') {
          // mirror the delta before next sweep
          if(sweep.hit.normal.x) {
            collider._delta.x = -collider._delta.x;
          }
          if(sweep.hit.normal.y) {
            collider._delta.y = -collider._delta.y;
          }
        }

        // sweep again (slide or bounce)
        sweep = this._sweepInto(collider, otherColliders);

        if(sweep.hit) {
          this._sweepMoveFowardBackCheck(sweep, collider, otherColliders);
        }
        else {
          collider._AABB.pos.x += collider._delta.x;
          collider._AABB.pos.y += collider._delta.y;
        }
      }
    }
    // hit nothing or is a sensor
    else {
      collider._AABB.pos.x += collider._delta.x;
      collider._AABB.pos.y += collider._delta.y;
    }
  },
  _addToCollidersHitPerUpdate: function(colliderA, colliderB) {
    var hitA,
        hitB = colliderA._AABB.intersectAABB(colliderB._AABB);

    if(hitB) {
      hitA = colliderB._AABB.intersectAABB(colliderA._AABB);

      // overwrite for collision callbacks to access collider
      hitA.collider = colliderB;
      hitB.collider = colliderA;

      colliderA._addHit(hitA);
      colliderB._addHit(hitB);

      if(this._collidersHitPerUpdate.indexOf(colliderA) === -1) {
        this._collidersHitPerUpdate.push(colliderA);
      }

      if(this._collidersHitPerUpdate.indexOf(colliderB) === -1) {
        this._collidersHitPerUpdate.push(colliderB);
      }
    }
  },
  _sweepMoveFowardBackCheck: function(sweep, collider, otherColliders) {
    var pushFrac = 1.001;
    // set first hit position before sweeping again
    // add a little to the delta for collision callbacks
    collider._AABB.pos.x += sweep.hit.delta.x - sweep.hit.normal.x;
    collider._AABB.pos.y += sweep.hit.delta.y - sweep.hit.normal.y;

    otherColliders.forEach(function(otherCollider){
      this._addToCollidersHitPerUpdate(collider, otherCollider);
    }, this);

    // fix position (push away from collider with fraction)
    collider._AABB.pos.x += sweep.hit.normal.x * pushFrac;
    collider._AABB.pos.y += sweep.hit.normal.y * pushFrac;
  },
  _sweepSensorMoveForwardBackCheck: function(sweep, colliderA, colliderB) {
    colliderA._AABB.pos.x += sweep.hit.delta.x - sweep.hit.normal.x;
    colliderA._AABB.pos.y += sweep.hit.delta.y - sweep.hit.normal.y;

    this._addToCollidersHitPerUpdate(colliderA, colliderB);

    // move back for next move
    colliderA._AABB.pos.x -= sweep.hit.delta.x - sweep.hit.normal.x;
    colliderA._AABB.pos.y -= sweep.hit.delta.y - sweep.hit.normal.y;
  },
  _sweepInto: function(collider, otherColliders) {
    var cAABB = collider._AABB,
        cDelta = collider._delta,
        nearest = new intersect.Sweep();

    nearest.time = 1;
    nearest.pos.x = cAABB.pos.x + cDelta.x;
    nearest.pos.y = cAABB.pos.y + cDelta.y;

    otherColliders.forEach(function(oc) {
      var sweep = oc._AABB.sweepAABB(cAABB, cDelta),
          colFilterFound = oc._groupFilter.indexOf(collider._groupName) !== -1,
          ocFilterFound = collider._groupFilter.indexOf(oc._groupName) !== -1;

      if(sweep.hit) {
        // sensor/filter check
        if(collider.getCollisionType() === 'sensor' || colFilterFound) {
          this._sweepSensorMoveForwardBackCheck(sweep, collider, oc);
        }
        else if (sweep.time < nearest.time) {
          // other sensor/filter check
          if(oc.getCollisionType() === 'sensor' || ocFilterFound) {
            this._sweepSensorMoveForwardBackCheck(sweep, collider, oc);
          }
          else {
            sweep.hit.collider = oc;
            nearest = sweep;
          }
        }
      }
    }, this);

    return nearest;
  },
  _placeColliderInGrid: function(collider) {
    // delete from last main grid position
    this._clearColliderFromGrid(collider);

    this._getColliderKeysInGrid(collider).forEach(function(key){
      if(!this._grid[key]) {
        this._grid[key] = [];
      }

      if(this._grid[key].indexOf(collider) === -1) {
        collider._positionInGridKeys.push(key);
        this._grid[key].push(collider);
      }
    }, this);
  },
  _clearColliderFromGrid: function(collider) {
    var gKey;
    // delete from main grid
    collider._positionInGridKeys.forEach(function(cgKey){
      gKey = this._grid[cgKey];

      gKey.splice(gKey.indexOf(collider), 1);

      if(!gKey.length) {
        delete this._grid[cgKey];
      }
    }, this);
    // reset collider grid keys after deleting from main grid
    collider._positionInGridKeys = [];
  },
  _getColliderKeysInGrid: function(collider) {
    var keyX, keyY,
        x = 0,
        y = 0,
        posX = collider._AABB.pos.x - collider._AABB.half.x,
        posY = collider._AABB.pos.y - collider._AABB.half.y,
        cWidth = collider._AABB.half.x * 2,
        cHeight = collider._AABB.half.y * 2,
        cellSize = this._cellSize,
        keys = [];

    for(;;) {
      keyX = Math.floor((x+posX) / cellSize);
      keyY = Math.floor((y+posY) / cellSize);

      keys.push('x'+keyX+'y'+keyY);

      if(x === cWidth && y === cHeight) {
        break;
      }

      if(x === cWidth) {
        x = 0;
        y += cellSize;
        if(y > cHeight) {
          y = cHeight;
        }
      }
      else {
        x += cellSize;
      }

      if(x > cWidth) {
        x = cWidth;
      }
    }
    return keys;
  }
};
<commit_msg>API function rename + refactoring functions<commit_after>Axis2D.World = function World(cellSize) {
  Axis2D.typeCheck(cellSize, 'cellSize', 'Number');

  this._cellSize = Math.abs(cellSize) || 64;

  this._grid = {};

  this._colliders = [];

  // dynamic colliders are colliders that have moved or be resized
  this._dynamicColliders = [];

  this._collidersHitPerUpdate = [];

  this._responses = [];

  this._debugDraw = undefined;

  this._pushFrac = 1.001;
};

Axis2D.World.prototype = {
  createCollider: function(x, y, width, height) {
    return new Axis2D.Collider(this, x, y, width, height);
  },
  removeCollider: function(collider) {
    Axis2D.typeCheck(collider, 'collider', Axis2D.Collider);
    var collidersIndex = this._colliders.indexOf(collider),
        dynamicCollidersIndex = this._dynamicColliders.indexOf(collider);

    this._clearColliderFromGrid(collider);

    if(collidersIndex !== -1) {
      this._colliders.splice(collidersIndex, 1);
    }
    if(dynamicCollidersIndex !== -1) {
      this._dynamicColliders.splice(dynamicCollidersIndex, 1);
    }
  },
  createDebugDraw: function() {
    var d = new Axis2D.DebugDraw(this);
    this._debugDraw = d;
    return d;
  },
  update: function() {
    // first pass - sweep dynamic colliders into other colliders + add hits
    this._dynamicColliders.forEach(function(collider){
      var otherColliders = [];

      // find potential hit colliders in the grid
      collider._positionInGridKeys.forEach(function(key){
        this._grid[key].forEach(function(oc){
          if(collider !== oc && otherColliders.indexOf(oc) === -1) {
            otherColliders.push(oc);
          }
        }, this);
      }, this);

      this._firstSweepDeltaResponse(collider, otherColliders);

      // fix position
      collider._AABB.pos.x += collider._delta.x;
      collider._AABB.pos.y += collider._delta.y;

      this._placeColliderInGrid(collider);

      // cleanups
      collider._isDynamic = false;
      collider._delta.x = 0;
      collider._delta.y = 0;
    }, this);
    this._dynamicColliders = [];

    // second pass - return hits for static and dynamic
    this._collidersHitPerUpdate.forEach(function(c){
      c._calculateTouches();
      if(c._collisionCallback) {
        c._collisionCallback(c.getHits(), c.getTouches());
      }
    }, this);
    this._collidersHitPerUpdate = [];
  },
  debugDraw: function() {
    if(this._debugDraw) {
      this._debugDraw._draw();
    }
  },
  createCollisionType: function(name, response, secondSweep) {
    Axis2D.typeCheck(name, 'name', 'String');
    Axis2D.typeCheck(response, 'response', 'Function');

  },
  _secondSweepDeltaResponse: function(collider, otherColliders) {
    // sweep again (slide, bounce .. etc)
    sweep = this._sweepInto(collider, otherColliders);

    if(sweep.hit) {
      this._moveForwardAddHits(sweep, collider, otherColliders);

      // move back for next move
      collider._delta.x = sweep.hit.normal.x * this._pushFrac;
      collider._delta.y = sweep.hit.normal.y * this._pushFrac;
    }
  },
  _firstSweepDeltaResponse: function(collider, otherColliders) {
    var cType = collider.getCollisionType(),
        sweep = this._sweepInto(collider, otherColliders);

    if(sweep.hit && cType !== 'sensor') {
      this._moveForwardAddHits(sweep, collider, otherColliders);

      // fix position (push away from collider with fraction)
      collider._AABB.pos.x += sweep.hit.normal.x * this._pushFrac;
      collider._AABB.pos.y += sweep.hit.normal.y * this._pushFrac;

      // first hit, set last position for scripting
      collider._lastHitPosition.x = collider._AABB.pos.x;
      collider._lastHitPosition.y = collider._AABB.pos.y;

      if(cType === 'touch') {
        collider._delta.x = 0;
        collider._delta.y = 0;
      }

      if(cType === 'slide') {
        // undo the normal hit delta before slide
        if(sweep.hit.normal.x) {
          collider._delta.x = 0;
        }
        if(sweep.hit.normal.y) {
          collider._delta.y = 0;
        }

        this._secondSweepDeltaResponse(collider, otherColliders);
      }

      if(cType === 'bounce') {
        // mirror the delta before next sweep
        if(sweep.hit.normal.x) {
          collider._delta.x = -collider._delta.x;
        }
        if(sweep.hit.normal.y) {
          collider._delta.y = -collider._delta.y;
        }

        this._secondSweepDeltaResponse(collider, otherColliders);
      }
    }
  },
  _addToCollidersHitPerUpdate: function(colliderA, colliderB) {
    var hitA,
        hitB = colliderA._AABB.intersectAABB(colliderB._AABB);

    if(hitB) {
      hitA = colliderB._AABB.intersectAABB(colliderA._AABB);

      // overwrite for collision callbacks to access collider
      hitA.collider = colliderB;
      hitB.collider = colliderA;

      colliderA._addHit(hitA);
      colliderB._addHit(hitB);

      if(this._collidersHitPerUpdate.indexOf(colliderA) === -1) {
        this._collidersHitPerUpdate.push(colliderA);
      }

      if(this._collidersHitPerUpdate.indexOf(colliderB) === -1) {
        this._collidersHitPerUpdate.push(colliderB);
      }
    }
  },
  _moveForwardAddHits: function(sweep, collider, otherColliders) {
    // set first hit position before sweeping again
    // add a little to the delta for collision callbacks
    collider._AABB.pos.x += sweep.hit.delta.x - sweep.hit.normal.x;
    collider._AABB.pos.y += sweep.hit.delta.y - sweep.hit.normal.y;

    otherColliders.forEach(function(otherCollider){
      this._addToCollidersHitPerUpdate(collider, otherCollider);
    }, this);
  },
  _sweepInto: function(collider, otherColliders) {
    var cAABB = collider._AABB,
        cDelta = collider._delta,
        nearest = new intersect.Sweep();

    nearest.time = 1;
    nearest.pos.x = cAABB.pos.x + cDelta.x;
    nearest.pos.y = cAABB.pos.y + cDelta.y;

    otherColliders.forEach(function(oc) {
      var sweep = oc._AABB.sweepAABB(cAABB, cDelta),
          crf = collider._responseFilters,
          ocrf = oc._responseFilters,
          cf = ocrf.indexOf(collider._responseName) !== -1,
          ocf = crf.indexOf(oc._responseName) !== -1;

      if(sweep.hit) {
        // sensor/filter check
        if(collider.getCollisionType() === 'sensor' || cf) {
          this._moveForwardAddHits(sweep, collider, [oc]);

          // move back for next move
          collider._AABB.pos.x -= sweep.hit.delta.x - sweep.hit.normal.x;
          collider._AABB.pos.y -= sweep.hit.delta.y - sweep.hit.normal.y;
        }
        else if (sweep.time < nearest.time) {
          // other sensor/filter check
          if(oc.getCollisionType() === 'sensor' || ocf) {
            this._moveForwardAddHits(sweep, collider, [oc]);

            // move back for next move
            collider._AABB.pos.x -= sweep.hit.delta.x - sweep.hit.normal.x;
            collider._AABB.pos.y -= sweep.hit.delta.y - sweep.hit.normal.y;
          }
          else {
            sweep.hit.collider = oc;
            nearest = sweep;
          }
        }
      }
    }, this);

    return nearest;
  },
  _placeColliderInGrid: function(collider) {
    // delete from last main grid position
    this._clearColliderFromGrid(collider);

    this._getColliderKeysInGrid(collider).forEach(function(key){
      if(!this._grid[key]) {
        this._grid[key] = [];
      }

      if(this._grid[key].indexOf(collider) === -1) {
        collider._positionInGridKeys.push(key);
        this._grid[key].push(collider);
      }
    }, this);
  },
  _clearColliderFromGrid: function(collider) {
    var gKey;
    // delete from main grid
    collider._positionInGridKeys.forEach(function(cgKey){
      gKey = this._grid[cgKey];

      gKey.splice(gKey.indexOf(collider), 1);

      if(!gKey.length) {
        delete this._grid[cgKey];
      }
    }, this);
    // reset collider grid keys after deleting from main grid
    collider._positionInGridKeys = [];
  },
  _getColliderKeysInGrid: function(collider) {
    var keyX, keyY,
        x = 0,
        y = 0,
        posX = collider._AABB.pos.x - collider._AABB.half.x,
        posY = collider._AABB.pos.y - collider._AABB.half.y,
        cWidth = collider._AABB.half.x * 2,
        cHeight = collider._AABB.half.y * 2,
        cellSize = this._cellSize,
        keys = [];

    for(;;) {
      keyX = Math.floor((x+posX) / cellSize);
      keyY = Math.floor((y+posY) / cellSize);

      keys.push('x'+keyX+'y'+keyY);

      if(x === cWidth && y === cHeight) {
        break;
      }

      if(x === cWidth) {
        x = 0;
        y += cellSize;
        if(y > cHeight) {
          y = cHeight;
        }
      }
      else {
        x += cellSize;
      }

      if(x > cWidth) {
        x = cWidth;
      }
    }
    return keys;
  }
};
<|endoftext|>


<commit_before>(function($, obviel, module) {

    obviel.iface('autocomplete_field', 'textline_field');

    module.AutocompleteWidget = function(settings) {
        settings = settings || {};
        var d = {
            iface: 'autocomplete_field'
        };
        $.extend(d, settings);
        module.TextLineWidget.call(this, d);
    };

    module.AutocompleteWidget.prototype = new module.TextLineWidget;

    module.AutocompleteWidget.prototype.render = function() {
        var self = this;
        var obj = self.obj;
        var el = self.el;
        module.TextLineWidget.prototype.render.call(this);

        var autocomplete_options = obj.autocomplete_options || {};
        var input_el = $('#obviel-field-' + obj.prefixed_name, el);
        var clone_el = input_el.clone();
        self.clone_el = clone_el;
        clone_el.attr('id', null);
        clone_el.attr('name', 'obviel-field-cloned-' + obj.prefixed_name);
        input_el.hide();
        input_el.after(clone_el);

        var autocomplete_data = obj.data;
        // obj.data can point to a list of mappings with keys 'label'
        // and 'value', and even though autocomplete() supports a similar
        // structure, we deal with these objects in a different way (mostly
        // because the autocomplete() places the value in the input on
        // autocomplete, which is rather ugly)
        var label_to_value = null;
        var value_to_label = null;
        var labels = null;
        var source = null;
        if (typeof autocomplete_data == 'string' || autocomplete_data.url) {
            // obj.data is a string, meaning we get an url and possibly
            // a value to fill into the input, we provide a source
            // function for the autocomplete widget that retrieves
            // {'value': ..., 'label': ...} structures from the server
            // and then processes them for the rest of the mechanism
            var url = autocomplete_data.url || autocomplete_data;
            source = function(data, autocomplete_callback) {
                $.ajax({
                    type: 'GET',
                    url: url,
                    data: {
                        identifier: data.identifier,
                        term: data.term,
                        limit: autocomplete_options.limit || 10
                    },
                    dataType: 'json',
                    success: function(data) {
                        label_to_value = {};
                        value_to_label = {};
                        labels = [];

                        self.label_to_value = self.label_to_value || {};
                        self.value_to_label = self.label_to_value || {};

                        $.each(data, function(index, item) {
                            self.label_to_value[item.label] = item.value;
                            self.value_to_label[item.value] = item.label;
                            labels.push(item.label);
                        });
                        autocomplete_callback(labels);
                    },
                    error: function(xhr, status, error) {
                        // XXX must always call autocomplete callback
                        // even when there's an error?
                    }
                });
            };
        } else if (autocomplete_data.length && typeof autocomplete_data[0] != 'string') {
            label_to_value = {};
            value_to_label = {};
            labels = [];
            $.each(autocomplete_data, function(index, item) {
                label_to_value[item.label] = item.value;
                value_to_label[item.value] = item.label;
                labels.push(item.label);
            });
            self.label_to_value = label_to_value;
            self.value_to_label = value_to_label;
            source = labels;
        };

        self.source = source;

        autocomplete_options.source = source;
        var ignore_blur = false;
        autocomplete_options.open = function(ev) {
            ignore_blur = true;
        };
        autocomplete_options.close = function(ev) {
            ignore_blur = false;
            // it is possible that the close was not preceded by a
            // selection but an immediate blur (by pressing tab).
            // since we ignored the blur, we want to trigger
            // a change explicitly so we can report errors
            input_el.val(clone_el.val());
            var change_ev = $.Event('change');
            change_ev.target = input_el;
            input_el.trigger(change_ev);
        };
        clone_el.autocomplete(autocomplete_options);
        // when the user blurs away from the field, we want to validate the
        // field unless we blurred away because we opened the autocomplete
        // popup
        clone_el.blur(function(ev) {
            if (ignore_blur) {
                return;
            }
            input_el.val(clone_el.val());
            var change_ev = $.Event('change');
            change_ev.target = input_el;
            input_el.trigger(change_ev);
        });
    };

    module.AutocompleteWidget.prototype.validate = function(value) {
        var result = module.TextLineWidget.prototype.validate.call(this);
        if (result !== undefined) {
            return result;
        }
        return undefined;
    };

    module.AutocompleteWidget.prototype.convert = function(value) {
        var result = module.TextLineWidget.prototype.convert.call(
            this, value);
        if (result.error !== undefined) {
            return result;
        }
        if (result.value === null) {
            return result;
        }
        value = this.label_to_value[result.value];
        if (value === undefined) {
            return {error: 'unknown value'};
        }
        return {value: value};
    };

    module.AutocompleteWidget.prototype.convert_back = function(value,
                                                                source, target) {
        var self = this;
        var result = module.TextLineWidget.prototype.convert_back.call(
            this, value);
        target = $(target);
        if (result === null) {
            target.val('');
            this.clone_el.val('');
        }

        var set_value = function() {
            value = self.value_to_label[result];
            if (value === undefined) {
                target.val(''); // XXX should never happen?
                self.clone_el.val('');
            }
            // set the value in the input
            target.val(value);
            // set the value in the cloned element too
            self.clone_el.val(value);
        };

        if (!$.isFunction(this.source)) {
            set_value();
        } else if (this.value_to_label === undefined ||
                    this.value_to_label[value] === undefined) {
            this.source.call(this, {identifier: value}, set_value);
        } else {
            set_value();
        }
    };

    obviel.view(new module.AutocompleteWidget);

}(jQuery, obviel, obviel.forms));
<commit_msg>Fix bug tripped up in some browsers; cannot set attribute to null.<commit_after>(function($, obviel, module) {

    obviel.iface('autocomplete_field', 'textline_field');

    module.AutocompleteWidget = function(settings) {
        settings = settings || {};
        var d = {
            iface: 'autocomplete_field'
        };
        $.extend(d, settings);
        module.TextLineWidget.call(this, d);
    };

    module.AutocompleteWidget.prototype = new module.TextLineWidget;

    module.AutocompleteWidget.prototype.render = function() {
        var self = this;
        var obj = self.obj;
        var el = self.el;
        module.TextLineWidget.prototype.render.call(this);

        var autocomplete_options = obj.autocomplete_options || {};
        var input_el = $('#obviel-field-' + obj.prefixed_name, el);
        var clone_el = input_el.clone();
        self.clone_el = clone_el;
        clone_el.removeAttr('id');
        clone_el.attr('name', 'obviel-field-cloned-' + obj.prefixed_name);
        input_el.hide();
        input_el.after(clone_el);

        var autocomplete_data = obj.data;
        // obj.data can point to a list of mappings with keys 'label'
        // and 'value', and even though autocomplete() supports a similar
        // structure, we deal with these objects in a different way (mostly
        // because the autocomplete() places the value in the input on
        // autocomplete, which is rather ugly)
        var label_to_value = null;
        var value_to_label = null;
        var labels = null;
        var source = null;
        if (typeof autocomplete_data == 'string' || autocomplete_data.url) {
            // obj.data is a string, meaning we get an url and possibly
            // a value to fill into the input, we provide a source
            // function for the autocomplete widget that retrieves
            // {'value': ..., 'label': ...} structures from the server
            // and then processes them for the rest of the mechanism
            var url = autocomplete_data.url || autocomplete_data;
            source = function(data, autocomplete_callback) {
                $.ajax({
                    type: 'GET',
                    url: url,
                    data: {
                        identifier: data.identifier,
                        term: data.term,
                        limit: autocomplete_options.limit || 10
                    },
                    dataType: 'json',
                    success: function(data) {
                        label_to_value = {};
                        value_to_label = {};
                        labels = [];

                        self.label_to_value = self.label_to_value || {};
                        self.value_to_label = self.label_to_value || {};

                        $.each(data, function(index, item) {
                            self.label_to_value[item.label] = item.value;
                            self.value_to_label[item.value] = item.label;
                            labels.push(item.label);
                        });
                        autocomplete_callback(labels);
                    },
                    error: function(xhr, status, error) {
                        // XXX must always call autocomplete callback
                        // even when there's an error?
                    }
                });
            };
        } else if (autocomplete_data.length && typeof autocomplete_data[0] != 'string') {
            label_to_value = {};
            value_to_label = {};
            labels = [];
            $.each(autocomplete_data, function(index, item) {
                label_to_value[item.label] = item.value;
                value_to_label[item.value] = item.label;
                labels.push(item.label);
            });
            self.label_to_value = label_to_value;
            self.value_to_label = value_to_label;
            source = labels;
        };

        self.source = source;

        autocomplete_options.source = source;
        var ignore_blur = false;
        autocomplete_options.open = function(ev) {
            ignore_blur = true;
        };
        autocomplete_options.close = function(ev) {
            ignore_blur = false;
            // it is possible that the close was not preceded by a
            // selection but an immediate blur (by pressing tab).
            // since we ignored the blur, we want to trigger
            // a change explicitly so we can report errors
            input_el.val(clone_el.val());
            var change_ev = $.Event('change');
            change_ev.target = input_el;
            input_el.trigger(change_ev);
        };
        clone_el.autocomplete(autocomplete_options);
        // when the user blurs away from the field, we want to validate the
        // field unless we blurred away because we opened the autocomplete
        // popup
        clone_el.blur(function(ev) {
            if (ignore_blur) {
                return;
            }
            input_el.val(clone_el.val());
            var change_ev = $.Event('change');
            change_ev.target = input_el;
            input_el.trigger(change_ev);
        });
    };

    module.AutocompleteWidget.prototype.validate = function(value) {
        var result = module.TextLineWidget.prototype.validate.call(this);
        if (result !== undefined) {
            return result;
        }
        return undefined;
    };

    module.AutocompleteWidget.prototype.convert = function(value) {
        var result = module.TextLineWidget.prototype.convert.call(
            this, value);
        if (result.error !== undefined) {
            return result;
        }
        if (result.value === null) {
            return result;
        }
        value = this.label_to_value[result.value];
        if (value === undefined) {
            return {error: 'unknown value'};
        }
        return {value: value};
    };

    module.AutocompleteWidget.prototype.convert_back = function(value,
                                                                source, target) {
        var self = this;
        var result = module.TextLineWidget.prototype.convert_back.call(
            this, value);
        target = $(target);
        if (result === null) {
            target.val('');
            this.clone_el.val('');
        }

        var set_value = function() {
            value = self.value_to_label[result];
            if (value === undefined) {
                target.val(''); // XXX should never happen?
                self.clone_el.val('');
            }
            // set the value in the input
            target.val(value);
            // set the value in the cloned element too
            self.clone_el.val(value);
        };

        if (!$.isFunction(this.source)) {
            set_value();
        } else if (this.value_to_label === undefined ||
                    this.value_to_label[value] === undefined) {
            this.source.call(this, {identifier: value}, set_value);
        } else {
            set_value();
        }
    };

    obviel.view(new module.AutocompleteWidget);

}(jQuery, obviel, obviel.forms));
<|endoftext|>