<commit_before>(function() {
    'use strict';

    angular.module('app.employee').service('empPmsService');

    /** @ngInject */
    var employeePms = [{
        "data": [{
            "empID": "001",
            "date": "01-01-9999",
            "personalInfo": [{
                "nameEn": "Jakkrich",
                "LastNameEn": "Jongklangthawatchkij",
                "position": "Developer",
                "salary": "9999999",
                "address": "51/333",
                "moo": "9",
                "road": "lumlukka",
                "district": "lassawai",
                "amphur": "lumlukka",
                "province": "phathumthani",
                "postcode": "12150",
                "tel": "089-555-5555",
                "mobile": "089-555-5555",
                "living": "own home",
                "birthdate": "18/05/1992",
                "age": "24",
                "race": "Thai",
                "nationality": "Thai",
                "religion": "Bhudda",
                "idcard": "7895641236547",
                "idcardEx": "12/05/2016",
                "height": "180",
                "weight": "65",
                "military": "Not yet",
                "marital": "Single",
                "sex": "Male",
                "account": "521562451",
                "img": "assets/images/avatars/garry.jpg",
                "underlying": "No",
                "email": "toonelicious@gmail.com"
            }],
            "familyInfo": [{
                "nameFm": "Jakleaw",
                "LastNameFm": "Jongklangthawatchkij",
                "age": "50",
                "related": "Father",
                "Occupation": "Engineer",
                "tel": "098-775-8886"
            }, {
                "nameFm": "JakEyang",
                "LastNameFm": "Jongklangthawatchkij",
                "age": "50",
                "related": "Mother",
                "Occupation": "Engineery",
                "tel": "098-445-8886"
            }],
            "EducationInfo": [{
                "EduLevel": "Grade 1-6",
                "Institution": "ReanTumMai School",
                "DateEduFrom": "2000",
                "DateEduTo": "2006"
            }, {
                "EduLevel": "Bachelor Degree",
                "Institution": "NorthBangkok University",
                "DateEduFrom": "2011",
                "DateEduTo": "2015"
            }],
            "WorkInfo": [{
                "Company": "C@net",
                "Position": "Developer",
                "DateFrom": "2000",
                "DateTo": "2006",
                "JobDesc": " Design and Developer Analyst With tools of developer "
            }],
            "languageInfo": [{
                "langType": "ThaiLand",
                "speaking": "Good",
                "writing": "Fair",
                "reading": "Poor"
            }, {
                "langType": "English",
                "speaking": "Good",
                "writing": "Good",
                "reading": "Good"
            }, {
                "langType": "Japanish",
                "speaking": "Fair",
                "writing": "Fair",
                "reading": "Poor"
            }],
            "specialInfo": [{
                "typingTH": "25",
                "typingEN": "30",
                "computer": "Microsoft office, Photostop",
                "driving": "1234567890123",
                "officeMachine": "Printer",
                "hobby": "Pokemon GO",
                "sport": "Runing",
                "knowledge": "NO",
                "other": "NO"
            }],
            "otherInfo": [{
                "workCountry": "Yes",
                "firstName": "Jakleaw",
                "lastName": "Jongklangthawatchkij",
                "related": "Mother",
                "address": "51/333 lumlokka phathumthani 12150",
                "tel": "082-346-7897",
                "sourceInfo": "Internet",
                "contagious": "No",
                "firstNameWork": "Sirintra",
                "lastNameWork": "Wannakheaw",
                "introduce": "I Am Pretty I Am Pretty I Am Pretty I Am PrettyI Am Pretty I Am Pretty I Am Pretty I Am Pretty"
            }]
        }, {
            "empID": "002",
            "date": "01-01-2000",
            "personalInfo": [{
                "nameEn": "Sirintra",
                "LastNameEn": "Wannakheaw",
                "position": "Developer",
                "salary": "9999999999",
                "address": "499/195",
                "moo": "5",
                "road": "phaholyothin",
                "district": "khongtanon",
                "amphur": "saimai",
                "province": "bangkok",
                "postcode": "10220",
                "tel": "091-552-6974",
                "mobile": "091-552-6974",
                "living": "own home",
                "birthdate": "12/01/1996",
                "age": "18",
                "race": "Thai",
                "nationality": "Thai",
                "religion": "Bhudda",
                "idcard": "7895641236547",
                "idcardEx": "12/05/2016",
                "height": "173",
                "weight": "49",
                "military": "Not yet",
                "marital": "Single",
                "sex": "Famale",
                "account": "521562451",
                "img": "assets/images/avatars/alice.jpg",
                "underlying": "No",
                "email": "Sirintra@gmail.com"
            }],
            "familyInfo": [{
                "nameFm": "Sirinty",
                "LastNameFm": "Wannakheaw",
                "age": "45",
                "related": "Father",
                "Occupation": "Engineer",
                "tel": "099-999-9999"
            }, {
                "nameFm": "Sireaw",
                "LastNameFm": "Wannakheaw",
                "age": "47",
                "related": "Mother",
                "Occupation": "Engineery",
                "tel": "099-999-9999"
            }],
            "EducationInfo": [{
                "EduLevel": "Grade 1-6",
                "Institution": "Watbannnon School",
                "DateEduFrom": "2000",
                "DateEduTo": "2006"
            }, {
                "EduLevel": "Hight 1-6",
                "Institution": "Prachantarajbumrung School",
                "DateEduFrom": "2006",
                "DateEduTo": "2008"
            }, {
                "EduLevel": "Bachelor Degree",
                "Institution": "NorthBangkok University",
                "DateEduFrom": "2009",
                "DateEduTo": "2013"
            }],
            "WorkInfo": [{
                "Company": "C@net",
                "Position": "Developer",
                "DateFrom": "2015",
                "DateTo": "2016",
                "JobDesc": " Design and Developer Analyst With tools of developer "
            }, {
                "Company": "Saint's John University",
                "Position": "System Developer",
                "DateFrom": "2014",
                "DateTo": "2015",
                "JobDesc": " Design and Developer Analyst With tools of developer "
            }],
            "languageInfo": [{
                "langType": "ThaiLand",
                "speaking": "Good",
                "writing": "Fair",
                "reading": "Poor"
            }, {
                "langType": "English",
                "speaking": "Good",
                "writing": "Good",
                "reading": "Good"
            }, {
                "langType": "Japanish",
                "speaking": "Fair",
                "writing": "Fair",
                "reading": "Poor"
            }],
            "specialInfo": [{
                "typingTH": "30",
                "typingEN": "35",
                "computer": "Microsoft office, Photostop",
                "driving": "1234567890123",
                "officeMachine": "Printer",
                "hobby": "Pokemon GO",
                "sport": "Runing",
                "knowledge": "NO",
                "other": "NO"
            }],
            "otherInfo": [{
                "workCountry": "Yes",
                "firstName": "Sireaw",
                "lastName": "Wannakheaw",
                "related": "Mother",
                "address": "51/33 lumlokka phathumthani 12150",
                "tel": "082-346-7897",
                "sourceInfo": "Internet",
                "contagious": "No",
                "firstNameWork": "Jakkrich",
                "lastNameWork": "Jongklangthawatchkij",
                "introduce": "I Am Pretty I Am Pretty I Am Pretty I Am PrettyI Am Pretty I Am Pretty I Am Pretty I Am Pretty"
            }],
            "position": [{
                "name": "Director",
                "code": "01"
            }, {
                "name": "Human Resource",
                "code": "02"
            }, {
                "name": "Accounting",
                "code": "03"
            }, {
                "name": "Developer",
                "code": "04"
            }, {
                "name": "System Analyst",
                "code": "05"
            }]
        }],
    }];

    // this.getEmployeesPms = function() {
    //     return employeePms;
    // }

    // this.getEmployeePms = function(id) {
    //     var result = {};
    //     for (var i = 0; i < employeePms.length; i++) {
    //         if (employeePms[i].id == id) {
    //             result = employeePms[i];
    //             break;
    //         }
    //     }
    //     return result;
    // };

    // this.addEmployeePms = function(item) {
    //     employeePms.push(item);
    // };

    // this.getFileNameList = function() {
    //     return fileName;
    // };

    // this.getFileName = function(arr, id) {
    //     if (arr) {
    //         for (var i = 0; i < arr.length; i++) {
    //             if (arr[i].id == id) {
    //                 return arr[i];
    //             }
    //         }
    //         return [];
    //     }
    //     return [];

    // };

    // this.deleteEmployeePms = function(id) {
    //     for (var i = 0; i < employeePms.length; i++) {
    //         if (employeePms[i].id == id) {
    //             employeePms.splice(i, 1);
    //             break;
    //         }
    //     }
    //     return employeePms;
    // };

})();
<commit_msg>update Benefits<commit_after><|endoftext|>


<commit_before>/**
 * The CharCounter Utility
 *
 * @module aui-char-counter
 */

var L = A.Lang,
    isNumber = L.isNumber;

/**
 * A base class for CharCounter, providing:
 *
 * - Limit the number of characters allowed in an input box
 * - Display the number of characters left
 *
 * Check the [live demo](http://alloyui.com/examples/char-counter/).
 *
 * @class A.CharCounter
 * @extends Base
 * @param {Object} config Object literal specifying widget configuration
 *     properties.
 * @constructor
 * @include http://alloyui.com/examples/char-counter/basic-markup.html
 * @include http://alloyui.com/examples/char-counter/basic.js
 */
var CharCounter = A.Component.create({
    /**
     * Static property provides a string to identify the class.
     *
     * @property NAME
     * @type String
     * @static
     */
    NAME: 'char-counter',

    /**
     * Static property used to define the default attribute
     * configuration for the CharCounter.
     *
     * @property ATTRS
     * @type Object
     * @static
     */
    ATTRS: {
        /**
         * Node or Selector to display the information of the counter.
         *
         * @attribute counter
         * @default null
         * @type {Node | String}
         */
        counter: {
            setter: A.one
        },

        /**
         * Max number of characters the [input](A.CharCounter.html#attr_input)
         * can have.
         *
         * @attribute maxLength
         * @default Infinity
         * @type Number
         */
        maxLength: {
            lazyAdd: false,
            setter: function(v) {
                return this._setMaxLength(v);
            },
            value: Infinity,
            validator: isNumber
        },

        /**
         * Node or Selector for the input field. Required.
         *
         * @attribute input
         * @default null
         * @type {Node | String}
         */
        input: {
            setter: A.one
        }
    },

    /**
     * Static property used to define which component it extends.
     *
     * @property EXTENDS
     * @type Object
     * @static
     */
    EXTENDS: A.Base,

    prototype: {

        /**
         * Event handler for the input [aui-event](../modules/aui-event.html)
         * event.
         *
         * @property handler
         * @type EventHandle
         * @protected
         */
        handler: null,

        /**
         * Construction logic executed during CharCounter instantiation.
         * Lifecycle.
         *
         * @method initializer
         * @protected
         */
        initializer: function() {
            var instance = this;

            instance.bindUI();

            instance.checkLength();
        },

        /**
         * Bind the events on the CharCounter UI. Lifecycle.
         *
         * @method bindUI
         * @protected
         */
        bindUI: function() {
            var instance = this;
            var input = instance.get('input');

            instance.publish('maxLength');

            instance.after('maxLengthChange', instance.checkLength);

            if (input) {
                // use cross browser input-handler event
                instance.handler = input.on('input', A.bind(instance._onInputChange, instance));
            }
        },

        /**
         * Sync the CharCounter UI. Lifecycle.
         *
         * @method syncUI
         * @protected
         */
        syncUI: function() {
            var instance = this;
            var counter = instance.get('counter');

            if (counter) {
                var value = instance.get('input').val();

                counter.html(
                    instance.get('maxLength') - value.length
                );
            }
        },

        /**
         * Destructor lifecycle implementation for the `CharCounter` class.
         * Purges events attached to the node (and all child nodes).
         *
         * @method destroy
         * @protected
         */
        destroy: function() {
            var instance = this;

            if (instance.handler) {
                instance.handler.detach();
            }
        },

        /**
         * Check the current value of the
         * [input](A.CharCounter.html#attr_input), truncate the data if needed,
         * and re-sync the UI. Fired from
         * [_onInputChange](A.CharCounter.html#method__onInputChange).
         *
         * @method checkLength
         */
        checkLength: function() {
            var instance = this;
            var input = instance.get('input');
            var maxLength = instance.get('maxLength');

            if (!input) {
                return false; // NOTE: return
            }

            var value = input.val();
            var scrollTop = input.get('scrollTop');
            var scrollLeft = input.get('scrollLeft');

            if (value.length > maxLength) {
                input.val(
                    value.substring(0, maxLength)
                );
            }

            if (value.length === maxLength) {
                instance.fire('maxLength');
            }

            input.set('scrollTop', scrollTop);
            input.set('scrollLeft', scrollLeft);

            instance.syncUI();
        },

        /**
         * Fired on input value change.
         *
         * @method _onInputChange
         * @param {EventFacade} event
         * @protected
         */
        _onInputChange: function() {
            var instance = this;

            instance.checkLength();
        },

        /**
         * Setter for [maxLength](A.CharCounter.html#attr_maxLength).
         *
         * @method _setMaxLength
         * @param {Number} v Value of the new
         *     [maxLength](A.CharCounter.html#attr_maxLength).
         * @protected
         * @return {Number}
         */
        _setMaxLength: function(v) {
            var instance = this;
            var input = instance.get('input');

            if (input && (v < Infinity)) {
                input.set('maxLength', v);
            }

            return v;
        }
    }
});

A.CharCounter = CharCounter;
<commit_msg>AUI-1715 Normalize line return lengths.<commit_after>/**
 * The CharCounter Utility
 *
 * @module aui-char-counter
 */

var L = A.Lang,
    isNumber = L.isNumber;

/**
 * A base class for CharCounter, providing:
 *
 * - Limit the number of characters allowed in an input box
 * - Display the number of characters left
 *
 * Check the [live demo](http://alloyui.com/examples/char-counter/).
 *
 * @class A.CharCounter
 * @extends Base
 * @param {Object} config Object literal specifying widget configuration
 *     properties.
 * @constructor
 * @include http://alloyui.com/examples/char-counter/basic-markup.html
 * @include http://alloyui.com/examples/char-counter/basic.js
 */
var CharCounter = A.Component.create({
    /**
     * Static property provides a string to identify the class.
     *
     * @property NAME
     * @type String
     * @static
     */
    NAME: 'char-counter',

    /**
     * Static property used to define the default attribute
     * configuration for the CharCounter.
     *
     * @property ATTRS
     * @type Object
     * @static
     */
    ATTRS: {
        /**
         * Node or Selector to display the information of the counter.
         *
         * @attribute counter
         * @default null
         * @type {Node | String}
         */
        counter: {
            setter: A.one
        },

        /**
         * Max number of characters the [input](A.CharCounter.html#attr_input)
         * can have.
         *
         * @attribute maxLength
         * @default Infinity
         * @type Number
         */
        maxLength: {
            lazyAdd: false,
            setter: function(v) {
                return this._setMaxLength(v);
            },
            value: Infinity,
            validator: isNumber
        },

        /**
         * Node or Selector for the input field. Required.
         *
         * @attribute input
         * @default null
         * @type {Node | String}
         */
        input: {
            setter: A.one
        }
    },

    /**
     * Static property used to define which component it extends.
     *
     * @property EXTENDS
     * @type Object
     * @static
     */
    EXTENDS: A.Base,

    prototype: {

        /**
         * Event handler for the input [aui-event](../modules/aui-event.html)
         * event.
         *
         * @property handler
         * @type EventHandle
         * @protected
         */
        handler: null,

        /**
         * Construction logic executed during CharCounter instantiation.
         * Lifecycle.
         *
         * @method initializer
         * @protected
         */
        initializer: function() {
            var instance = this;

            instance.bindUI();

            instance.checkLength();
        },

        /**
         * Bind the events on the CharCounter UI. Lifecycle.
         *
         * @method bindUI
         * @protected
         */
        bindUI: function() {
            var instance = this;
            var input = instance.get('input');

            instance.publish('maxLength');

            instance.after('maxLengthChange', instance.checkLength);

            if (input) {
                // use cross browser input-handler event
                instance.handler = input.on('input', A.bind(instance._onInputChange, instance));
            }
        },

        /**
         * Sync the CharCounter UI. Lifecycle.
         *
         * @method syncUI
         * @protected
         */
        syncUI: function() {
            var instance = this;
            var counter = instance.get('counter');

            if (counter) {
                var value = instance.get('input').val();

                var counterValue = instance.get('maxLength') - instance._getNormalizedLength(value);

                counter.html(counterValue);
            }
        },

        /**
         * Destructor lifecycle implementation for the `CharCounter` class.
         * Purges events attached to the node (and all child nodes).
         *
         * @method destroy
         * @protected
         */
        destroy: function() {
            var instance = this;

            if (instance.handler) {
                instance.handler.detach();
            }
        },

        /**
         * Check the current value of the
         * [input](A.CharCounter.html#attr_input), truncate the data if needed,
         * and re-sync the UI. Fired from
         * [_onInputChange](A.CharCounter.html#method__onInputChange).
         *
         * @method checkLength
         */
        checkLength: function() {
            var instance = this;
            var input = instance.get('input');
            var maxLength = instance.get('maxLength');

            if (!input) {
                return false; // NOTE: return
            }

            var value = input.val();
            var scrollTop = input.get('scrollTop');
            var scrollLeft = input.get('scrollLeft');

            var normalizedLength = instance._getNormalizedLength(value);

            if (normalizedLength > maxLength) {
                var trimLength = maxLength - (normalizedLength - value.length);

                value = value.substring(0, trimLength);

                input.val(value);
            }

            if (normalizedLength >= maxLength) {
                instance.fire('maxLength');
            }

            input.set('scrollTop', scrollTop);
            input.set('scrollLeft', scrollLeft);

            instance.syncUI();
        },

        /**
         * Normalize reported length between browsers.
         *
         * @method _getNormalizedLength
         * @param {String} value.
         * @protected
         * @return {Number}
         */
        _getNormalizedLength: function(value) {
            var instance = this;

            var newLines = value.match(/(\r\n|\n|\r)/g);

            var newLinesCorrection = 0;

            if (newLines != null) {
                newLinesCorrection = newLines.length;
            }

            return value.length + newLinesCorrection;
        },

        /**
         * Fired on input value change.
         *
         * @method _onInputChange
         * @param {EventFacade} event
         * @protected
         */
        _onInputChange: function() {
            var instance = this;

            instance.checkLength();
        },

        /**
         * Setter for [maxLength](A.CharCounter.html#attr_maxLength).
         *
         * @method _setMaxLength
         * @param {Number} v Value of the new
         *     [maxLength](A.CharCounter.html#attr_maxLength).
         * @protected
         * @return {Number}
         */
        _setMaxLength: function(v) {
            var instance = this;
            var input = instance.get('input');

            if (input && (v < Infinity)) {
                input.set('maxLength', v);
            }

            return v;
        }
    }
});

A.CharCounter = CharCounter;
<|endoftext|>


<commit_before>import { TelegramHandler } from 'bottender';
import {
  searchVideos,
  hotVideos,
  newVideos,
  tutorial,
  setting,
  about,
  disclaimer,
  report,
  contactUs,
  unhandled,
  callbackQuery,
  // imageAnalytic,
  subscribe,
  ourshd,
} from '../actions';

const mainHandlerMiddleware = new TelegramHandler()
  // PPAV
  .onText(/(^PPAV$|^PPAV 熱門$|^PPAV hot$|^[#＃]PPAV)/i, hotVideos)
  // PPAV new
  .onText(/(^PPAV 最新$|^PPAV new$)/i, newVideos)
  // 奧視
  .onText(/(奧視|ours|[#＃](奧視|ours))$/, ourshd)
  // 搜尋 番號、標題、女優
  .onText(/[#＃]\s*\+*\s*(\S+)/, searchVideos)
  // 使用說明
  .onText(/(使用說明|Tutorial) 📖$/i, tutorial)
  // 設置
  .onText(/(設置|Setting) ⚙$/i, setting)
  // 圖片分析
  // .onPhoto(imageAnalytic)
  // 訂閱推播
  .onText(/^(gginin|nogg)\s*(\d*)/i, subscribe)
  // 關於 PPAV
  .onText(/(關於 PPAV|About PPAV) 👀$/i, about)
  // 免責聲明
  .onText(/(免責聲明|Disclaimer) 📜$/i, disclaimer)
  // 意見回饋
  .onText(/(意見回饋|Report) 🙏$/i, report)
  // 聯絡我們
  .onText(/(聯絡我們|Contact PPAV) 📩$/i, contactUs)
  // callback_query
  .onCallbackQuery(callbackQuery)
  // unhandled
  .onEvent(unhandled);

export default mainHandlerMiddleware;
<commit_msg>Fix ourshd regex<commit_after>import { TelegramHandler } from 'bottender';
import {
  searchVideos,
  hotVideos,
  newVideos,
  tutorial,
  setting,
  about,
  disclaimer,
  report,
  contactUs,
  unhandled,
  callbackQuery,
  // imageAnalytic,
  subscribe,
  ourshd,
} from '../actions';

const mainHandlerMiddleware = new TelegramHandler()
  // PPAV
  .onText(/(^PPAV$|^PPAV 熱門$|^PPAV hot$|^[#＃]PPAV)/i, hotVideos)
  // PPAV new
  .onText(/(^PPAV 最新$|^PPAV new$)/i, newVideos)
  // 奧視
  .onText(/(奧視|ours|[#＃]\s*(奧視|ours))$/, ourshd)
  // 搜尋 番號、標題、女優
  .onText(/[#＃]\s*\+*\s*(\S+)/, searchVideos)
  // 使用說明
  .onText(/(使用說明|Tutorial) 📖$/i, tutorial)
  // 設置
  .onText(/(設置|Setting) ⚙$/i, setting)
  // 圖片分析
  // .onPhoto(imageAnalytic)
  // 訂閱推播
  .onText(/^(gginin|nogg)\s*(\d*)/i, subscribe)
  // 關於 PPAV
  .onText(/(關於 PPAV|About PPAV) 👀$/i, about)
  // 免責聲明
  .onText(/(免責聲明|Disclaimer) 📜$/i, disclaimer)
  // 意見回饋
  .onText(/(意見回饋|Report) 🙏$/i, report)
  // 聯絡我們
  .onText(/(聯絡我們|Contact PPAV) 📩$/i, contactUs)
  // callback_query
  .onCallbackQuery(callbackQuery)
  // unhandled
  .onEvent(unhandled);

export default mainHandlerMiddleware;
<|endoftext|>


<commit_before>/* eslint-disable */
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import { transform } from 'babel-standalone'

// snippet for defining HTML: <div /&#62

// SET TO TRUE WHEN QA IS COMPLETE:
export const QA = false;

// -------------- define challenge title and challenge instructions --------------
export const challengeTitle = `<span class = 'default'>Challenge: </span>Create a Complex JSX Element`
export const challengeText = `Intro:
That was pretty simple. But JSX can include complex nested HTML as well. Now let's try to use JSX in this way.`

export const challengeInstructions = `Instructions:
Define a new constant JSX which renders a <div> which contains, in this order, an <h1> tag followed by
a <p> tag followed by an unordered list with three <li> items. You can include whatever text you want within
each element. When rendering multiple elements like this, you can wrap them all in parathenses but it's not strictly required. You should
note that it is required that multiple child elements all be wrapped in a single element. For instance, if you remove the
<div> that is wrapping the elements here, the JSX will no longer transpile. Keep this in mind later
because it will also apply when we are returning JSX elements in React components.`

// Maybe here ^^ would be a good place to mention that all nested JSX needs to have only one outer level?

// ---------------------------- define challenge seed code ----------------------------
export const seedCode = `// write your code here`

// ---------------------------- define challenge solution code ----------------------------
export const solutionCode =
`const JSX = (

Hello JSX!

Some info

An item

Another item

A third item

);`

// ---------------------------- define challenge tests ----------------------------

export const executeTests = (code) => {

	let es5, mockedComponent, jsx, passed = true;

	let testResults = [
		{
			test: 0,
			status: false,
			condition: 'Your JSX code was transpiled successfully.'
		},
		{
			test: 1,
			status: false,
			condition: 'The constant JSX returns an
 element.'
		},
		{
			test: 2,
			status: false,
			condition: 'The div contains an h1 tag as the second element.'
		},
		{
			test: 3,
			status: false,
			condition: 'The div contains an p tag as the second element.'
		},
		{
			test: 4,
			status: false,
			condition: 'The div contains a ul tag as the third element.'
		},
		{
			test: 5,
			status: false,
			condition: 'The ul contains three li elements.'
		}
	];

	const prepend = `(function() {`
	const apend = `; return JSX })()`
	const modifiedCode = prepend.concat(code).concat(apend);

	// test 0: try to transpile JSX, ES6 code to ES5 in browser
	try {
		es5 = transform(modifiedCode, { presets: [ 'es2015', 'react' ] }).code;
		testResults[0].status = true;
	} catch (err) {
		passed = false;
		testResults[0].status = false;
	}

	// shallow render the component with Enzyme
	try {
		jsx = eval(es5);
	} catch (err) {
		passed = false;
	}

	// test 1:
	try {
		assert.strictEqual(jsx.type, 'div', 'The constant JSX returns an
 element.');
		testResults[1].status = true;
	} catch (err) {
		passed = false;
		testResults[1].status = false;
	}

	// test 2:
	try {
		assert.strictEqual(jsx.props.children[0].type, 'h1', 'The div contains an h1 tag as the first element.');
		testResults[2].status = true;
	} catch (err) {
		passed = false;
		testResults[2].status = false;
	}

	// test 3:
	try {
		assert.strictEqual(jsx.props.children[1].type, 'p', 'The div contains an p tag as the second element.');
		testResults[3].status = true;
	} catch (err) {
		passed = false;
		testResults[3].status = false;
	}

	// test 4:
	try {
		assert.strictEqual(jsx.props.children[2].type, 'ul', 'The div contains an h1 tag as the third element.');
		testResults[4].status = true;
	} catch (err) {
		passed = false;
		testResults[4].status = false;
	}

	// test 5:
	try {
		assert.strictEqual(jsx.props.children[2].props.children.length, 3, 'The ul contains three li elements.');
		testResults[5].status = true;
	} catch (err) {
		passed = false;
		testResults[5].status = false;
	}

	return {
		passed,
		testResults,
	}

}

// ---------------------------- define live render function ----------------------------

export const liveRender = (code) => {

	try {
		const es5 = transform(code, { presets: [ 'es2015', 'react' ] }).code;
		const renderedComponent = React.createElement(eval(es5));
		return renderedComponent;
	} catch (err) {
		console.log('Live rendering failed', err);
	}

}<commit_msg>Update React_02.js<commit_after>/* eslint-disable */
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import { transform } from 'babel-standalone'

// snippet for defining HTML: <div /&#62

// SET TO TRUE WHEN QA IS COMPLETE:
export const QA = false;

// -------------- define challenge title and challenge instructions --------------
export const challengeTitle = `<span class = 'default'>Challenge: </span>Create a Complex JSX Element`
export const challengeText = `Intro:
That was pretty simple. But JSX can include complex nested HTML as well. Now let's try to use JSX in this way.`

export const challengeInstructions = `Instructions:
Define a new constant JSX that renders a <div> which contains the following elements in order:
a <h1>, a <p>, and an unordered list containing three <li> items. When rendering multiple elements like this, you can wrap them all in parathenses, but it's not strictly required. You should also note that all child elements must be wrapped within a single element. For instance, if you remove the
<div> that is wrapping all of the elements here, the JSX will no longer transpile. Keep this in mind for later,
because it will also apply when we are returning JSX elements in React components. Feel free to include whatever text you want within
each element.`

// Maybe here ^^ would be a good place to mention that all nested JSX needs to have only one outer level?

// ---------------------------- define challenge seed code ----------------------------
export const seedCode = `// write your code here`

// ---------------------------- define challenge solution code ----------------------------
export const solutionCode =
`const JSX = (

Hello JSX!

Some info

An item

Another item

A third item

);`

// ---------------------------- define challenge tests ----------------------------

export const executeTests = (code) => {

	let es5, mockedComponent, jsx, passed = true;

	let testResults = [
		{
			test: 0,
			status: false,
			condition: 'Your JSX code was transpiled successfully.'
		},
		{
			test: 1,
			status: false,
			condition: 'The constant JSX returns an
 element.'
		},
		{
			test: 2,
			status: false,
			condition: 'The div contains an h1 tag as the second element.'
		},
		{
			test: 3,
			status: false,
			condition: 'The div contains an p tag as the second element.'
		},
		{
			test: 4,
			status: false,
			condition: 'The div contains a ul tag as the third element.'
		},
		{
			test: 5,
			status: false,
			condition: 'The ul contains three li elements.'
		}
	];

	const prepend = `(function() {`
	const apend = `; return JSX })()`
	const modifiedCode = prepend.concat(code).concat(apend);

	// test 0: try to transpile JSX, ES6 code to ES5 in browser
	try {
		es5 = transform(modifiedCode, { presets: [ 'es2015', 'react' ] }).code;
		testResults[0].status = true;
	} catch (err) {
		passed = false;
		testResults[0].status = false;
	}

	// shallow render the component with Enzyme
	try {
		jsx = eval(es5);
	} catch (err) {
		passed = false;
	}

	// test 1:
	try {
		assert.strictEqual(jsx.type, 'div', 'The constant JSX returns an
 element.');
		testResults[1].status = true;
	} catch (err) {
		passed = false;
		testResults[1].status = false;
	}

	// test 2:
	try {
		assert.strictEqual(jsx.props.children[0].type, 'h1', 'The div contains an h1 tag as the first element.');
		testResults[2].status = true;
	} catch (err) {
		passed = false;
		testResults[2].status = false;
	}

	// test 3:
	try {
		assert.strictEqual(jsx.props.children[1].type, 'p', 'The div contains an p tag as the second element.');
		testResults[3].status = true;
	} catch (err) {
		passed = false;
		testResults[3].status = false;
	}

	// test 4:
	try {
		assert.strictEqual(jsx.props.children[2].type, 'ul', 'The div contains an h1 tag as the third element.');
		testResults[4].status = true;
	} catch (err) {
		passed = false;
		testResults[4].status = false;
	}

	// test 5:
	try {
		assert.strictEqual(jsx.props.children[2].props.children.length, 3, 'The ul contains three li elements.');
		testResults[5].status = true;
	} catch (err) {
		passed = false;
		testResults[5].status = false;
	}

	return {
		passed,
		testResults,
	}

}

// ---------------------------- define live render function ----------------------------

export const liveRender = (code) => {

	try {
		const es5 = transform(code, { presets: [ 'es2015', 'react' ] }).code;
		const renderedComponent = React.createElement(eval(es5));
		return renderedComponent;
	} catch (err) {
		console.log('Live rendering failed', err);
	}

}
<|endoftext|>


<commit_before>"use strict";

let {forEach, find, findIndex, defer, partial} = require("lodash");

let can = require("can"),
    $ = require("jquery"),
    element = require("../../lib/customElement");

let {conn} = require("../../lib/socketConn");

let {submitEntry} = require("../../models/chatlog");

let character = require("../../models/character"),
    user = require("../../models/user");

let inputs = [
  {name: "dialogue", template: require("./inputs/dialogue.mustache")},
  {name: "action", template: require("./inputs/action.mustache")},
  {name: "ooc", template: require("./inputs/ooc.mustache")},
  {name: "slug", template: require("./inputs/slug.mustache")},
  {name: "heading", template: require("./inputs/heading.mustache")}
];

/**
 * ChatInput Controller
 */
let ChatInput = element.define("chat-input", {
  style: require("./styles.styl"),
  template: require("./template.mustache"),
  events: {
    "form submit": sendMessage,
    "form keydown": keyPressed,
    "form [name=content] keydown": textAreaKeyPressed,
    "form [name=content] input": typingNotification,
    "form click": focusContent,
    "[name=type] change": typeSelectorChanged,
    "[name=actor] change": updateScopeActor,
    "[name=user] input": updateScopeUser,
    "{scope} type": typeChanged,
    "{scope} actor": updateActorDropdown,
    "{scope.characters} length": updateScopeActor,
    inserted: function(el) {
      focusContent(el);
      alignActionInput(el);
      conn.state.bind("change", function(ev, newstate) {
        if (newstate === "open" && el.props("user.id")) {
          defer(partial(updateScopeUser, el));
        }
      });
      $(window).resize();
    }
  },
  properties: {
    characters: { defun: character.list },
    actor: {},
    conn: { default: conn },
    user: {
      defun: function(props) {
        user.current().then(function(usr) {
          props.attr("user", usr);
        }).done();
        return {};
      }
    },
    log: { type: "lookup", required: true, observe: false },
    type: { type: "string", default: inputs[0].name },
    inputs: { default: inputs },
    defaults: {
      internal: true,
      defun: function() {
        return Object.create(null);
      }
    }
  },
  helpers: {
    renderInput: renderInput,
    isSelected: isSelected
  }
});

/*
 * Chat message handling
 */
function sendMessage(el, _target, event) {
  event.preventDefault();
  let formVals = can.deparam(el.find("form").serialize()),
      props = el.props();
  if (!formVals.content) { return; }
  formVals.actor = currentActor(el).id;
  submitEntry(props.log, props.type, formVals);
  props.attr("user").attr("typing", false);
  props.attr("user").save();
  el.find("form")[0].reset();
}

/*
 * Actor management
 */
function currentActor(el) {
  return el.props("actor");
}

function updateScopeActor(el) {
  let actor = el.find("[name=actor] :selected").data("actor"),
      props = el.props();
  can.batch.start();
  try {
    props.attr("actor", actor);
    props.attr("user").attr("character", actor.name);
    props.attr("user").save();
    alignActionInput(el);
    $(window).resize();
  } finally {
    can.batch.stop();
  }
}

function alignActionInput(el) {
  let actionForm = el.find("form.action");
  if (actionForm) {
    actionForm.find("[name=content]").css({
      "text-indent": actionForm.find("actor").width()
    });
  }
}

function updateActorDropdown(el) {
  el.find("[name=actor]").val(el.props("actor.id"));
}

/*
 * Cycle between input types
 */
const TAB = 9;
function keyPressed(el, _target, event) {
  if (event.keyCode === TAB) {
    event.preventDefault();
    cycleInputType(el, !event.shiftKey);
  }
}

function cycleInputType(el, goForward) {
  let typeIndex = findIndex(inputs, {name: el.props("type")});
  el.props(
    "type",
    inputs[(typeIndex + (goForward ? 1 : inputs.length - 1)) %
           inputs.length].name);
  focusContent(el);
}

function typeChanged(el) {
  let form = el.find("form");
  el.find("[name=type]").val(el.props("type"));
  el.props("defaults", can.deparam(form.serialize()));
  forEach(el.props("defaults"), function(v, k) {
    form.find("[name="+k+"]").val(v);
  });
  alignActionInput(el);
  typingNotification(el);
  $(window).resize();
}

function typeSelectorChanged(el, target) {
  el.props("type", target.val());
}

/*
 * Special textareas
 */
const RET = 13;
function textAreaKeyPressed(_el, _target, event) {
  /*jshint validthis:true*/
  if (event.keyCode === RET && !event.shiftKey) {
    sendMessage.apply(this, arguments);
  }
}

function focusContent(el) {
  el.find("form [name=content]").focus();
}

function typingNotification(el) {
  let user = el.props("user");
  if (el.find("form [name=content]").val().length) {
    user.attr("typing", true);
  } else {
    user.attr("typing", false);
  }
  user.save();
}

/*
 * Username management
 */
function updateScopeUser(el) {
  if (conn.state() === "open") {
    can.batch.start();
    try {
      let user = el.props("user");
      user.attr("name", el.find("[name=user]").val());
      user.save();
    } finally {
      can.batch.stop();
    }
  }
}

/*
 * Mustache helpers
 */
function renderInput(props, type, actor) {
  function render(el) {
    $(el).html(find(inputs, {name: type()}).template(props));
  }
  return function(el) {
    render(el);
    type.bind("change", function() {
      render(el);
    });
    actor.bind("change", function() {
      render(el);
    });
  };
}

function isSelected(props, opts) {
  if (props.attr("actor") === opts.context) {
    return opts.fn();
  } else {
    return opts.inverse();
  }
}

/*
 * Exports
 */
module.exports.install = function(tag) {
  element.install(ChatInput, tag);
};
<commit_msg>Store input defaults<commit_after>"use strict";

let {forEach, find, findIndex, defer, partial} = require("lodash");

let can = require("can"),
    $ = require("jquery"),
    element = require("../../lib/customElement");

let {conn} = require("../../lib/socketConn");

let {submitEntry} = require("../../models/chatlog");

let character = require("../../models/character"),
    user = require("../../models/user");

let inputs = [
  {name: "dialogue", template: require("./inputs/dialogue.mustache")},
  {name: "action", template: require("./inputs/action.mustache")},
  {name: "ooc", template: require("./inputs/ooc.mustache")},
  {name: "slug", template: require("./inputs/slug.mustache")},
  {name: "heading", template: require("./inputs/heading.mustache")}
];

/**
 * ChatInput Controller
 */
let ChatInput = element.define("chat-input", {
  style: require("./styles.styl"),
  template: require("./template.mustache"),
  events: {
    "form submit": sendMessage,
    "form keydown": keyPressed,
    "form [name=content] keydown": textAreaKeyPressed,
    "form [name=content] input": typingNotification,
    "form click": focusContent,
    "[name=type] change": typeSelectorChanged,
    "[name=actor] change": updateScopeActor,
    "[name=user] input": updateScopeUser,
    "{scope} type": typeChanged,
    "{scope} actor": updateActorDropdown,
    "{scope.characters} length": updateScopeActor,
    inserted: function(el) {
      focusContent(el);
      alignActionInput(el);
      conn.state.bind("change", function(ev, newstate) {
        if (newstate === "open" && el.props("user.id")) {
          defer(partial(updateScopeUser, el));
        }
      });
      $(window).resize();
    }
  },
  properties: {
    characters: { defun: character.list },
    actor: {},
    conn: { default: conn },
    user: {
      defun: function(props) {
        user.current().then(function(usr) {
          props.attr("user", usr);
        }).done();
        return {};
      }
    },
    log: { type: "lookup", required: true, observe: false },
    type: { type: "string", default: inputs[0].name },
    inputs: { default: inputs }
  },
  helpers: {
    renderInput: renderInput,
    isSelected: isSelected
  }
});

/*
 * Chat message handling
 */
function sendMessage(el, _target, event) {
  event.preventDefault();
  let formVals = can.deparam(el.find("form").serialize()),
      props = el.props();
  if (!formVals.content) { return; }
  formVals.actor = currentActor(el).id;
  submitEntry(props.log, props.type, formVals);
  props.attr("user").attr("typing", false);
  props.attr("user").save();
  el.find("form")[0].reset();
}

/*
 * Actor management
 */
function currentActor(el) {
  return el.props("actor");
}

function updateScopeActor(el) {
  let actor = el.find("[name=actor] :selected").data("actor"),
      props = el.props();
  can.batch.start();
  try {
    props.attr("actor", actor);
    props.attr("user").attr("character", actor.name);
    props.attr("user").save();
    alignActionInput(el);
    $(window).resize();
  } finally {
    can.batch.stop();
  }
}

function alignActionInput(el) {
  let actionForm = el.find("form.action");
  if (actionForm) {
    actionForm.find("[name=content]").css({
      "text-indent": actionForm.find("actor").width()
    });
  }
}

function updateActorDropdown(el) {
  el.find("[name=actor]").val(el.props("actor.id"));
}

/*
 * Cycle between input types
 */
const TAB = 9;
function keyPressed(el, _target, event) {
  if (event.keyCode === TAB) {
    event.preventDefault();
    cycleInputType(el, !event.shiftKey);
  }
}

function cycleInputType(el, goForward) {
  let inputs = el.props("inputs");
  let typeIndex = findIndex(inputs, {name: el.props("type")});
  inputs[typeIndex].attr("defaults", objectify(el.find("form")));
  el.props("type", inputs[(typeIndex + goForward*2 - 1) % inputs.length].name);
  focusContent(el);
}

function typeChanged(el) {
  let form = el.find("form");
  el.find("[name=type]").val(el.props("type"));
  let inputs = el.props("inputs");
  let typeIndex = findIndex(inputs, {name: el.props("type")});
  forEach(inputs[typeIndex].attr("defaults"), function(v, k) {
    form.find("[name="+k+"]").focus().val(v);
  });
  alignActionInput(el);
  typingNotification(el);
  $(window).resize();
}

function typeSelectorChanged(el, target) {
  el.props("type", target.val());
}

/*
 * Special textareas
 */
const RET = 13;
function textAreaKeyPressed(_el, _target, event) {
  /*jshint validthis:true*/
  if (event.keyCode === RET && !event.shiftKey) {
    sendMessage.apply(this, arguments);
  }
}

function focusContent(el) {
  el.find("form [name=content]").focus();
}

function typingNotification(el) {
  let user = el.props("user");
  if (el.find("form [name=content]").val().length) {
    user.attr("typing", true);
  } else {
    user.attr("typing", false);
  }
  user.save();
}

/*
 * Username management
 */
function updateScopeUser(el) {
  if (conn.state() === "open") {
    can.batch.start();
    try {
      let user = el.props("user");
      user.attr("name", el.find("[name=user]").val());
      user.save();
    } finally {
      can.batch.stop();
    }
  }
}

/*
 * Mustache helpers
 */
function renderInput(props, type, actor) {
  function render(el) {
    $(el).html(find(inputs, {name: type()}).template(props));
  }
  return function(el) {
    render(el);
    type.bind("change", function() {
      render(el);
    });
    actor.bind("change", function() {
      render(el);
    });
  };
}

function isSelected(props, opts) {
  if (props.attr("actor") === opts.context) {
    return opts.fn();
  } else {
    return opts.inverse();
  }
}

/*
 * Utils
 */
function objectify(form) {
  return can.deparam(form.serialize());
}

/*
 * Exports
 */
module.exports.install = function(tag) {
  element.install(ChatInput, tag);
};
<|endoftext|>


<commit_before>define('yandex-map-markers', [
    'domReady',
    'jquery',
    'underscore',
    'backbone',
    'yandex-map-api'
], function (domReady, $, _, Backbone, YandexMapApi) {
    'use strict';

    console.log('%cfile: yandex-map-markers.js', 'color: #C2ECFF');

    var self;
    var router;
    var Deferred = $.Deferred();
    var __dataLoaded = false;
    var map_options = {
        default_options: {
            center: [55.751574, 37.573856],
            zoom: 10,
            controls: []
        },

        markers_options: {
            iconImageHref: '../../images/marker.png'
        },

        controls_types: {
            zoomControl : { right: "8px", top: "125px" },
            fullscreenControl : { right: "8px", top: "15px" }
        }
    };

    var __balloonTemplate = function() {
        self.marker_balloon_layout = ymaps.templateLayoutFactory.createClass(
            '
' +
            '
$[properties.name]
' +
            '
$[properties.region], $[properties.city]
' +
            '
$[properties.address]
' +
            '
'
        );
        ymaps.layout.storage.add('map#marker_balloon_layout', self.marker_balloon_layout);
    };

    var __addMarkers = function() {
        self.objectManager.add(self.markers_data);
    };

    var __select_address = function(id) {
        var templateSurroundingCitiesItem = _.template($('#js__surrounding-cities-item-template').html()),
            contentSurroundingCities = '';

        var finded_item = __findObjectInObjectManager(id);
        var surrounding_cities = __findSurroundingCities(finded_item.region, finded_item.city);

        _.each(surrounding_cities, function(item){
            contentSurroundingCities += templateSurroundingCitiesItem(item);
        });

        var address_full;

        if(finded_item.city == 'г. Москва') {
            address_full = 'Россия, ' + finded_item.city;
        } else {
            address_full = 'Россия, ' + finded_item.region + ', ' + finded_item.city;
        }

        self.setCenterByAddress(address_full);

        $('.js__select-address').removeClass('active');
        $('.js__select-address[data-id= "'+ id + '"]').addClass('active');
        var city = finded_item.city;
        city = city.replace('г. ', '');
        $('.js__current_city').html(city);
        $('.js__surrounding_cities').html(contentSurroundingCities);
        return false;
    };

    var __findObjectInObjectManager = function(id) {
        var finded = self.objectManager.objects.getById(id);

        if(!finded) {
            return false;
        }

        var finded_item = {
            id: finded.properties.id,
            region: finded.properties.region,
            city: finded.properties.city
        };

        return finded_item;
    };

    var __findSurroundingCities = function(region, city) {
        var surrounding_cities = [];

        self.objectManager.objects.each(function(object) {
            if(object.properties.region == region && object.properties.city != city) {
                surrounding_cities.push(object.properties);
            }
        });

        surrounding_cities = _.uniq(surrounding_cities, function(el) {
            return el.city;
        });

        return surrounding_cities;
    };

    var __dataLoader = function __dataLoader(){
        $.ajax({
            url: '/json/data.json',
            data: {},
            success: function(data){
                console.log('%ctrace: Map -> ajax', 'color: #ccc');

                self.markers_data = data;
                ymaps.ready(function() {
                    self.initialize();
                    __dataLoaded = true;
                    Deferred.resolve();
                });
            },
            dataType: 'json'
        });

        return Deferred.promise();
    };

    function Map(map_id, options) {
        console.log('%ctrace: Map -> constructor', 'color: #ccc');

        self = this;
        self.map_id = map_id;
        self.options = options;

        new YandexMapApi();

        self.init_routes();

        if(!window.location.hash){
            __dataLoader();
        }
    }

    Map.prototype = {
        initialize: function() {
            console.log('%ctrace: Map -> initialize', 'color: #ccc');

            self.init_map();
            self.init_ballon_template();
            self.init_map_controls();
            self.init_my_controls();
            self.init_object_manager();
            self.add_markers();
            self.getVisibleMarkers();

            if(!window.location.hash){
                self.geolocation();
            }
        },

        //Инициализация карты
        init_map: function() {
            var init_options = $.extend({}, map_options.default_options, self.map_id);
            self.map = new ymaps.Map(
                self.map_id,
                init_options
            );

            self.map.behaviors.disable('scrollZoom');
        },

        //Инициализация ObjectManager
        init_object_manager: function() {
            self.objectManager = new ymaps.ObjectManager({
                clusterize: true,
                geoObjectPreset: 'islands#greenDotIcon',
                geoObjectBalloonContentLayout: self.marker_balloon_layout,
                geoObjectBalloonMaxWidth: 505,
                geoObjectBalloonMaxHeight: 215,
                geoObjectHideIconOnBalloonOpen: false,
                geoObjectIconLayout: 'default#image',
                geoObjectIconImageHref: map_options.markers_options.iconImageHref,
                geoObjectIconImageSize: [36, 36],
                geoObjectIconImageOffset: [-18, -18],
                clusterBalloonContentLayout: 'cluster#balloonCarouselContent',
                clusterBalloonItemContentLayout: self.marker_balloon_layout,
                clusterballoonWidth: 505,
                clusterBalloonHeight: 215,
                clustergroupByCoordinates: false,
                clusterDisableClickZoom: false,
                clusterHideIconOnBalloonOpen: false
            });

            self.map.geoObjects.add(self.objectManager);
        },

        //Инициализация элементов урпавления для карты
        init_map_controls: function() {
            for (var control in map_options.controls_types) {
                self.map.controls.add(control, {
                    float: 'none',
                    position: map_options.controls_types[control]
                });
            }
        },

        //Инициализация дополнительных элементов управления
        init_my_controls: function() {
            self.map.events.add(['boundschange','datachange','objecttypeschange'], function(e){
                self.getVisibleMarkers();
            });

            $('body').on('click', '.js__show-all-addresses', _.bind(self.showAllAddresses, self));
            $('body').on('click', '.js__show-main-addresses', _.bind(self.showMainAddresses, self));
            $('body').on('click', '.js__select-optics', _.bind(self.selectOptics, self));
        },

        //Инициализация шаблона для балунов
        init_ballon_template: function() {
            __balloonTemplate();
        },

        init_routes: function() {
            console.log('%ctrace: Map -> init_routes', 'color: #ccc');

            var Router = Backbone.Router.extend({
                routes: {
                    'city/:id': 'select_address'
                }
            });

            router = new Router();

            router.on('route:select_address', function (id) {
                if(__dataLoaded){
                    __select_address(id);
                    return true;
                }

                __dataLoader().done(function() {
                    __select_address(id);
                });
            });
        },

        //Добавление меток на карту
        add_markers: function() {
            __addMarkers();
        },

        //Получит видимые метки
        getVisibleMarkers: function() {
            var result = ymaps.geoQuery(self.objectManager.objects).searchInside(self.map);
            result.then(function () {
                self.showOptics(result);
            });
        },

        //Центрирование по адресу
        setCenterByAddress: function(address) {
            ymaps.geocode(address, {
                results: 1,
                kind: 'locality'
            }).then(function (res) {
                var firstGeoObject = res.geoObjects.get(0),
                    bounds = firstGeoObject.properties.get('boundedBy');

                self.map.setBounds(bounds, {
                    checkZoomRange: true
                });
            });
        },

        //Геолокация
        geolocation: function() {
            var geolocation = ymaps.geolocation;
            geolocation.get({
                provider: 'auto',
                autoReverseGeocode: true
            }).then(function (res) {
                var firstGeoObject = res.geoObjects.get(0),
                    bounds = firstGeoObject.properties.get('boundedBy');

                self.map.setBounds(bounds, {
                    checkZoomRange: true
                });
            });
        },

        //Показать оптику
        showOptics: function(optics) {
            var i = 1,
                templateOpticsItem = _.template($('#js__optics__item-template').html()),
                contentOptics = '';

            optics.each(function(optic) {
                var item = {
                    id: optic.properties.get('id'),
                    name: optic.properties.get('name'),
                    region: optic.properties.get('region'),
                    city: optic.properties.get('city'),
                    address: optic.properties.get('address'),
                    description: optic.properties.get('description'),
                    i: i
                };

                i++;
                contentOptics += templateOpticsItem(item);
            });

            $('.js__optics').html(contentOptics);
        },

        //Показать все адреса (города)
        showAllAddresses: function() {
            $('.js__all-addresses').css('display', 'block');
            $('.js__main-addresses').css('display', 'none');
            return false;
        },

        //Показать главные адреса (города)
        showMainAddresses: function() {
            $('.js__all-addresses').css('display', 'none');
            $('.js__main-addresses').css('display', 'block');
            return false;
        },

        //Выбрать адрес (город)
        selectAddress: function(e) {
            var $el = $(e.currentTarget),
                $id = $el.attr('data-id');

            __select_address($id);

            return false;
        },

        //Выбрать оптику
        selectOptics: function(e) {
            var $el = $(e.currentTarget),
                $id = $el.attr('data-id');

            var finded = self.objectManager.objects.getById($id);
            var coord = finded.geometry.coordinates;

            self.map.setCenter(coord, 17)
                .then(function () {
                    setTimeout(function() {
                        var objectState = self.objectManager.getObjectState($id);
                        // Проверяем, находится ли объект в видимой области карты.
                        if (objectState.isShown) {
                            // Если объект попадает в кластер, открываем балун кластера с нужным выбранным объектом.
                            if (objectState.isClustered) {
                                self.objectManager.clusters.state.set('activeObject', finded);
                                self.objectManager.clusters.balloon.open(objectState.cluster.id);
                            } else {
                                // Если объект не попал в кластер, открываем его собственный балун.
                                self.objectManager.objects.balloon.open($id);
                            }
                        }
                    }, 300);
                });

            return false;
        }
    };

    return Map;
});
<commit_msg>Update yandex-map-markers.js<commit_after>define('yandex-map-markers', [
    'domReady',
    'jquery',
    'underscore',
    'backbone',
    'yandex-map-api'
], function (domReady, $, _, Backbone, YandexMapApi) {
    'use strict';

    console.log('%cfile: yandex-map-markers.js', 'color: #C2ECFF');

    var self;
    var router;
    var Deferred = $.Deferred();
    var __dataLoaded = false;
    var map_options = {
        default_options: {
            center: [55.751574, 37.573856],
            zoom: 10,
            controls: []
        },

        markers_options: {
            iconImageHref: '../../images/marker.png'
        },

        controls_types: {
            zoomControl : { right: "8px", top: "125px" },
            fullscreenControl : { right: "8px", top: "15px" }
        }
    };

    var __balloonTemplate = function() {
        self.marker_balloon_layout = ymaps.templateLayoutFactory.createClass(
            '
' +
            '
$[properties.name]
' +
            '
$[properties.region], $[properties.city]
' +
            '
$[properties.address]
' +
            '
'
        );
        ymaps.layout.storage.add('map#marker_balloon_layout', self.marker_balloon_layout);
    };

    var __addMarkers = function() {
        self.objectManager.add(self.markers_data);
    };

    var __select_address = function(id) {
        var templateSurroundingCitiesItem = _.template($('#js__surrounding-cities-item-template').html()),
            contentSurroundingCities = '';

        var finded_item = __findObjectInObjectManager(id);
        var surrounding_cities = __findSurroundingCities(finded_item.region, finded_item.city);

        _.each(surrounding_cities, function(item){
            contentSurroundingCities += templateSurroundingCitiesItem(item);
        });

        var address_full;

        if(finded_item.city == 'г. Москва') {
            address_full = 'Россия, ' + finded_item.city;
        } else {
            address_full = 'Россия, ' + finded_item.region + ', ' + finded_item.city;
        }

        self.setCenterByAddress(address_full);

        $('.js__select-address').removeClass('active');
        $('.js__select-address[data-id= "'+ id + '"]').addClass('active');
        var city = finded_item.city;
        city = city.replace('г. ', '');
        $('.js__current_city').html(city);
        $('.js__surrounding_cities').html(contentSurroundingCities);
        return false;
    };

    var __findObjectInObjectManager = function(id) {
        var finded = self.objectManager.objects.getById(id);

        if(!finded) {
            return false;
        }

        var finded_item = {
            id: finded.properties.id,
            region: finded.properties.region,
            city: finded.properties.city
        };

        return finded_item;
    };

    var __findSurroundingCities = function(region, city) {
        var surrounding_cities = [];

        self.objectManager.objects.each(function(object) {
            if(object.properties.region == region && object.properties.city != city) {
                surrounding_cities.push(object.properties);
            }
        });

        surrounding_cities = _.uniq(surrounding_cities, function(el) {
            return el.city;
        });

        return surrounding_cities;
    };

    var __dataLoader = function __dataLoader(){
        $.ajax({
            url: '/json/data.json',
            data: {},
            success: function(data){
                console.log('%ctrace: Map -> ajax', 'color: #ccc');

                self.markers_data = data;
                ymaps.ready(function() {
                    self.initialize();
                    __dataLoaded = true;
                    Deferred.resolve();
                });
            },
            dataType: 'json'
        });

        return Deferred.promise();
    };

    function Map(map_id, options) {
        console.log('%ctrace: Map -> constructor', 'color: #ccc');

        self = this;
        self.map_id = map_id;
        self.options = options;

        if($('#' + self.map_id).length <= 0){
            console.warn('%ctrace: Map: not found dom elements', 'color: #ccc');
            return false;
        }

        new YandexMapApi();

        self.init_routes();

        if(!window.location.hash){
            __dataLoader();
        }
    }

    Map.prototype = {
        initialize: function() {
            console.log('%ctrace: Map -> initialize', 'color: #ccc');

            self.init_map();
            self.init_ballon_template();
            self.init_map_controls();
            self.init_my_controls();
            self.init_object_manager();
            self.add_markers();
            self.getVisibleMarkers();

            if(!window.location.hash){
                self.geolocation();
            }
        },

        //Инициализация карты
        init_map: function() {
            var init_options = $.extend({}, map_options.default_options, self.map_id);
            self.map = new ymaps.Map(
                self.map_id,
                init_options
            );

            self.map.behaviors.disable('scrollZoom');
        },

        //Инициализация ObjectManager
        init_object_manager: function() {
            self.objectManager = new ymaps.ObjectManager({
                clusterize: true,
                geoObjectPreset: 'islands#greenDotIcon',
                geoObjectBalloonContentLayout: self.marker_balloon_layout,
                geoObjectBalloonMaxWidth: 505,
                geoObjectBalloonMaxHeight: 215,
                geoObjectHideIconOnBalloonOpen: false,
                geoObjectIconLayout: 'default#image',
                geoObjectIconImageHref: map_options.markers_options.iconImageHref,
                geoObjectIconImageSize: [36, 36],
                geoObjectIconImageOffset: [-18, -18],
                clusterBalloonContentLayout: 'cluster#balloonCarouselContent',
                clusterBalloonItemContentLayout: self.marker_balloon_layout,
                clusterballoonWidth: 505,
                clusterBalloonHeight: 215,
                clustergroupByCoordinates: false,
                clusterDisableClickZoom: false,
                clusterHideIconOnBalloonOpen: false
            });

            self.map.geoObjects.add(self.objectManager);
        },

        //Инициализация элементов урпавления для карты
        init_map_controls: function() {
            for (var control in map_options.controls_types) {
                self.map.controls.add(control, {
                    float: 'none',
                    position: map_options.controls_types[control]
                });
            }
        },

        //Инициализация дополнительных элементов управления
        init_my_controls: function() {
            self.map.events.add(['boundschange','datachange','objecttypeschange'], function(e){
                self.getVisibleMarkers();
            });

            $('body').on('click', '.js__show-all-addresses', _.bind(self.showAllAddresses, self));
            $('body').on('click', '.js__show-main-addresses', _.bind(self.showMainAddresses, self));
            $('body').on('click', '.js__select-optics', _.bind(self.selectOptics, self));
        },

        //Инициализация шаблона для балунов
        init_ballon_template: function() {
            __balloonTemplate();
        },

        init_routes: function() {
            console.log('%ctrace: Map -> init_routes', 'color: #ccc');

            var Router = Backbone.Router.extend({
                routes: {
                    'city/:id': 'select_address'
                }
            });

            router = new Router();

            router.on('route:select_address', function (id) {
                if(__dataLoaded){
                    __select_address(id);
                    return true;
                }

                __dataLoader().done(function() {
                    __select_address(id);
                });
            });
        },

        //Добавление меток на карту
        add_markers: function() {
            __addMarkers();
        },

        //Получит видимые метки
        getVisibleMarkers: function() {
            var result = ymaps.geoQuery(self.objectManager.objects).searchInside(self.map);
            result.then(function () {
                self.showOptics(result);
            });
        },

        //Центрирование по адресу
        setCenterByAddress: function(address) {
            ymaps.geocode(address, {
                results: 1,
                kind: 'locality'
            }).then(function (res) {
                var firstGeoObject = res.geoObjects.get(0),
                    bounds = firstGeoObject.properties.get('boundedBy');

                self.map.setBounds(bounds, {
                    checkZoomRange: true
                });
            });
        },

        //Геолокация
        geolocation: function() {
            var geolocation = ymaps.geolocation;
            geolocation.get({
                provider: 'auto',
                autoReverseGeocode: true
            }).then(function (res) {
                var firstGeoObject = res.geoObjects.get(0),
                    bounds = firstGeoObject.properties.get('boundedBy');

                //console.log(self.objectManager.objects.getClosestTo(firstGeoObject));

                self.map.setBounds(bounds, {
                    checkZoomRange: true
                });
            });
        },

        //Показать оптику
        showOptics: function(optics) {
            var i = 1,
                templateOpticsItem = _.template($('#js__optics__item-template').html()),
                contentOptics = '';

            optics.each(function(optic) {
                var item = {
                    id: optic.properties.get('id'),
                    name: optic.properties.get('name'),
                    region: optic.properties.get('region'),
                    city: optic.properties.get('city'),
                    address: optic.properties.get('address'),
                    description: optic.properties.get('description'),
                    i: i
                };

                i++;
                contentOptics += templateOpticsItem(item);
            });

            $('.js__optics').html(contentOptics);
        },

        //Показать все адреса (города)
        showAllAddresses: function() {
            $('.js__all-addresses').css('display', 'block');
            $('.js__main-addresses').css('display', 'none');
            return false;
        },

        //Показать главные адреса (города)
        showMainAddresses: function() {
            $('.js__all-addresses').css('display', 'none');
            $('.js__main-addresses').css('display', 'block');
            return false;
        },

        //Выбрать адрес (город)
        selectAddress: function(e) {
            var $el = $(e.currentTarget),
                $id = $el.attr('data-id');

            __select_address($id);

            return false;
        },

        //Выбрать оптику
        selectOptics: function(e) {
            var $el = $(e.currentTarget),
                $id = $el.attr('data-id');

            var finded = self.objectManager.objects.getById($id);
            var coord = finded.geometry.coordinates;

            self.map.setCenter(coord, 17)
                .then(function () {
                    setTimeout(function() {
                        var objectState = self.objectManager.getObjectState($id);
                        // Проверяем, находится ли объект в видимой области карты.
                        if (objectState.isShown) {
                            // Если объект попадает в кластер, открываем балун кластера с нужным выбранным объектом.
                            if (objectState.isClustered) {
                                self.objectManager.clusters.state.set('activeObject', finded);
                                self.objectManager.clusters.balloon.open(objectState.cluster.id);
                            } else {
                                // Если объект не попал в кластер, открываем его собственный балун.
                                self.objectManager.objects.balloon.open($id);
                            }
                        }
                    }, 300);
                });

            return false;
        }
    };

    return Map;
});
<|endoftext|>


<commit_before>const _ = require('lodash');

const ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]

function findByName(gameState, name) {
    return _.find(gameState.players, player => player.name == name);
}

function findMe(gameState) {
    return findByName(gameState, 'Texas Nodem');
}

function isPair(cards) {
    return cards[0].rank == cards[1].rank;
}

function sameColor(cards) {
    return cards[0].suit == cards[1].suit;
}

function isNeighbors(cards) {
    const index0 = ranks.indexOf(cards[0].rank);
    const index1 = ranks.indexOf(cards[1].rank);
    return Math.abs(index0 - index1);
}

function isAcePair(cards) {
    return isPair(cards) && cards[0].rank == "A";
}

function onlyFigures(cards) {
    return _.every(cards, card => ranks.indexOf(card.rank) > 7);
}

function allCards(gameState) {
    const myCards = findMe(gameState).hole_cards;
    return gameState.community_cards.concat(myCards);
}

function cardSum(cards) {
    return _.sum(cards.map(card => ranks.indexOf(card.rank)));
}

function isFlush(cards) {
    let hash = {};
    cards.forEach(card => {
        if (hash[card.suit]) hash[card.suit]++;
        else hash[card.suit] = 1;
    });
    for (let suit in hash) {
        if (hash[suit] >= 5) return true;
    }
    return false;
}

function cardsContainsRank(cards, rank) {
    return cards.some(card => card.rank == rank);
}

function decide(gameState) {
    const me = findMe(gameState);
    const cards = me.hole_cards;
    const playerCount = gameState.players.length;

    const sneaky = findByName(gameState, 'Sneaky');
    const sneakyActive = sneaky.status == "active";
    const kamikazeVeryGoodForUs = cardsContainsRank(cards, "A") || cardsContainsRank(cards, "K");
    const kamikazeON = !sneakyActive || kamikazeVeryGoodForUs;

    if (kamikazeON && gameState.minimum_raise > 20 && gameState.minimum_raise < 250) {
        return {
            action: "allIn",
            strategy: "kamikaze"
        };
    }

    if (isFlush(allCards(gameState))) {
        return {
            action: "allIn",
            strategy: "flush"
        };
    }

    if (sameColor(cards) && onlyFigures(cards)) {
        return {
            action: "allIn",
            strategy: "concrete"
        };
    }

    if (isAcePair(cards)) {
        return {
            action: "allIn",
            strategy: "acePair"
        };
    }

    if (isPair(cards)) {
        const sum = cardSum(cards);
        if ((playerCount == 5 && sum > 10) ||
            (playerCount == 4 && sum > 10) ||
            (playerCount == 3 && sum > 0) ||
            (playerCount == 2 && sum > 0)) {
            return {
                action: "allIn",
                strategy: "pair"
            };
        }
    }

    if (sameColor(cards) && isNeighbors(cards) && playerCount <= 3) {
        return {
            action: "allIn",
            strategy: "expectStraightFlush"
        };
    }

    if (gameState.minimum_raise <= 20) {
        return {
            action: "call",
            strategy: "limp"
        };
    }

    return {
        action: "fold",
        strategy: "none"
    };
}

function makeBet(gameState, bet) {
    const decision = decide(gameState);

    console.log("round ", gameState.round, " : ", "action = ", decision.action, "strategy = ", decision.strategy);

    const allInAmount_v0 = 5000;
    //const allInAmount_v1 = gameState.current_buy_in - gameState.players[gameState.in_action].bet + gameState.minimum_raise;
    const allInAmount_v2 = gameState.current_buy_in * 2;

    switch (decision.action) {
        case "allIn":
            return bet(allInAmount_v0);
        case "call":
            return bet(gameState.current_buy_in);
        case "fold":
        default:
            return bet(0);
    }
}

module.exports = {
    makeBet,
    isFlush,
    allCards,
    onlyFigures,
    isAcePair,
    isNeighbors,
    sameColor,
    isPair,
    findMe,
    decide,
    findByName,
    cardSum
};<commit_msg>post fold<commit_after>const _ = require('lodash');

const ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]

function findByName(gameState, name) {
    return _.find(gameState.players, player => player.name == name);
}

function findMe(gameState) {
    return findByName(gameState, 'Texas Nodem');
}

function isPair(cards) {
    return cards[0].rank == cards[1].rank;
}

function hasNumRank(cards, num) {
    let hash = {};
    cards.forEach(card => {
        if (hash[card.rank]) hash[card.rank]++;
        else hash[card.rank] = 1;
    });
    for (let rank in hash) {
        if (hash[rank] == num) return rank;
    }
    return null;
}

function getNumRanks(cards, num) {
    let hash = {};
    cards.forEach(card => {
        if (hash[card.rank]) hash[card.rank]++;
        else hash[card.rank] = 1;
    });
    let groups = [];
    for (let rank in hash) {
        if (hash[rank] == num) {
            groups.push(rank);
        }
    }
    return groups;
}

function getPairs(cards) {
    return getNumRanks(cards, 2);
}

function hasPair(cards) {
    return hasNumRank(cards, 2);
}

function hasDrill(cards) {
    return hasNumRank(cards, 2);
}

function sameColor(cards) {
    return cards[0].suit == cards[1].suit;
}

function isNeighbors(cards) {
    const index0 = ranks.indexOf(cards[0].rank);
    const index1 = ranks.indexOf(cards[1].rank);
    return Math.abs(index0 - index1);
}

function isAcePair(cards) {
    return isPair(cards) && cards[0].rank == "A";
}

function onlyFigures(cards) {
    return _.every(cards, card => ranks.indexOf(card.rank) > 7);
}

function allCards(gameState) {
    const myCards = findMe(gameState).hole_cards;
    return gameState.community_cards.concat(myCards);
}

function cardSum(cards) {
    return _.sum(cards.map(card => ranks.indexOf(card.rank)));
}

function isFlush(cards) {
    let hash = {};
    cards.forEach(card => {
        if (hash[card.suit]) hash[card.suit]++;
        else hash[card.suit] = 1;
    });
    for (let suit in hash) {
        if (hash[suit] >= 5) return true;
    }
    return false;
}

function cardsContainsRank(cards, rank) {
    return cards.some(card => card.rank == rank);
}

function decide(gameState) {
    const me = findMe(gameState);
    const cards = me.hole_cards;
    const playerCount = gameState.players.length;

    const isPreFlop = gameState.community_cards.length == 0;

    if (isPreFlop) {
        // Pre flop

        const sneaky = findByName(gameState, 'Sneaky');
        const sneakyActive = sneaky.status == "active";
        const kamikazeVeryGoodForUs = cardsContainsRank(cards, "A") || cardsContainsRank(cards, "K");
        const kamikazeON = !sneakyActive || kamikazeVeryGoodForUs;

        if (kamikazeON && gameState.minimum_raise > 20 && gameState.minimum_raise < 250) {
            return {
                action: "allIn",
                strategy: "kamikaze"
            };
        }

        if (isFlush(allCards(gameState))) {
            return {
                action: "allIn",
                strategy: "flush"
            };
        }

        if (sameColor(cards) && onlyFigures(cards)) {
            return {
                action: "allIn",
                strategy: "concrete"
            };
        }

        if (isAcePair(cards)) {
            return {
                action: "allIn",
                strategy: "acePair"
            };
        }

        if (isPair(cards)) {
            const sum = cardSum(cards);
            if ((playerCount == 5 && sum > 10) ||
                (playerCount == 4 && sum > 10) ||
                (playerCount == 3 && sum > 0) ||
                (playerCount == 2 && sum > 0)) {
                return {
                    action: "allIn",
                    strategy: "pair"
                };
            }
        }

        if (sameColor(cards) && isNeighbors(cards) && playerCount <= 3) {
            return {
                action: "allIn",
                strategy: "expectStraightFlush"
            };
        }

        if (gameState.minimum_raise <= 20) {
            return {
                action: "call",
                strategy: "limp"
            };
        }
    } else {
        // Post flop

        if (isFlush(allCards(gameState))) {
            return {
                action: "allIn",
                strategy: "flush"
            };
        }

        if (isPair(cards)) {
            const sum = cardSum(cards);
            if ((playerCount == 5 && sum > 10) ||
                (playerCount == 4 && sum > 10) ||
                (playerCount == 3 && sum > 0) ||
                (playerCount == 2 && sum > 0)) {
                return {
                    action: "allIn",
                    strategy: "pair"
                };
            }
        }

        if (!hasPair(gameState.community_cards) && hasPair(allCards(gameState))) {
            return {
                action: "allIn",
                strategy: "communityPair"
            };
        }

        const pairs = getNumRanks(cards, 2);
        if (pairs.length == 2) {
            return {
                action: "allIn",
                strategy: "twoPairs"
            };
        }
    }

    return {
        action: "fold",
        strategy: "none"
    };
}

function makeBet(gameState, bet) {
    const decision = decide(gameState);

    console.log("round ", gameState.round, " : ", "action = ", decision.action, "strategy = ", decision.strategy);

    const allInAmount_v0 = 5000;
    //const allInAmount_v1 = gameState.current_buy_in - gameState.players[gameState.in_action].bet + gameState.minimum_raise;
    const allInAmount_v2 = gameState.current_buy_in * 2;

    switch (decision.action) {
        case "allIn":
            return bet(allInAmount_v2);
        case "call":
            return bet(gameState.current_buy_in);
        case "fold":
        default:
            return bet(0);
    }
}

module.exports = {
    makeBet,
    isFlush,
    allCards,
    onlyFigures,
    isAcePair,
    isNeighbors,
    sameColor,
    isPair,
    findMe,
    decide,
    findByName,
    cardSum
};<|endoftext|>


<commit_before>/*global chrome hashblot queue blotpw*/

function updateFromPhrase(phrase) {
  document.getElementById('blotpath').setAttribute('d',
    phrase ? hashblot.sha1qp(phrase) : '');
}

function calculateHashblot() {
  updateFromPhrase(blotpw.blotString({
    email: document.getElementById('email').value,
    domain: document.getElementById('domain').value,
    salt: document.getElementById('salt').value}));
}

var currentInfo;

function loadInfo() {
  chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
    var currentUrl = tabs[0].url;
    var hostname = blotpw.getHostnameFromUrl(changeInfo.url);
    if (hostname) {
      blotpw.getDomainAndInfo(hostname, function(err,info) {
        currentInfo = info;
        setStateFromCurrentInfo();
      });
    }
  });
}

function getResultsFromInfo(info) {
  return {
    domain: info.domain,
    email: info.record ? (info.record.email || '')
      : info.defaults && (info.defaults.email || ''),
    salt: info.record ? (info.record.salt || '')
      : info.defaults && (info.defaults.salt || ''),
    memo: info.record && (info.record.memo || '');
  };
}

function setStateFromCurrentInfo() {
  var info = getResultsFromInfo(currentInfo);
  document.getElementById('domain').value = info.domain;
  document.getElementById('domainname').textContent = info.domain;
  document.getElementById('email').value = info.email;
  document.getElementById('salt').value = info.salt;
  document.getElementById('memo').value = info.memo;
  calculateHashblot();
  setDomainProfileInfo();
  updateDisplayState();
  snapToPostActionState();
}

function setDomainProfileInfo() {
  //TODO: Populate info about this domain
}

function showOnlyChild(parentId, childId) {
  var children = document.getElementById(parentId).children;
  for (var i = 0; i < children.length; i++) {
    children[i].style.display = children[i].id == childId ? '' : 'none';
  }
}

function updateDisplayState() {
  var formDomain = document.getElementById('domain').value;
  var formEmail = document.getElementById('email').value;
  var formSalt = document.getElementById('salt').value;
  var formMemo = document.getElementById('memo').value;

  if (currentInfo.record) {

    if (formDomain == currentInfo.domain
      && formEmail == currentInfo.record.email
      && formSalt == currentInfo.record.salt) {

        if (formMemo == currentInfo.record.memo) {

          showOnlyChild('finishers', 'record-untouched');

        } else if (formMemo == '' && formEmail == currentInfo.defaults.email
          && formSalt == currentInfo.defaults.salt ) {

          // Technically, this could be a record of note if the domain
          // is not the domain that would be found by default for this URL.
          // However, I just do not feel like considering that case.
          showOnlyChild('finishers', 'record-blanked');

        } else {

          showOnlyChild('finishers', 'record-edited');
        }
    } else {

      showOnlyChild('finishers', 'record-changed');
    }
  } else {

    if (formEmail != currentInfo.defaults.email
      || formSalt != currentInfo.defaults.salt
      || formMemo) {

      showOnlyChild('finishers', 'norecord-edited');

    } else {

      showOnlyChild('finishers', 'norecord-untouched');
    }
  }
}

function saveRecord() {
  var newDomain = document.getElementById('domain').value;
  // TODO: alert if new record won't match the current domain
  var newRecord = {
    email: document.getElementById('email').value,
    salt: document.getElementById('salt').value,
    memo: document.getElementById('memo').value
  };
  var newItems = {};
  newItems['records.' + newDomain] = newRecord;
  chrome.storage.local.set(newItems,function(){
    // If the domain is changing
    if (currentInfo.domain != newDomain) {
      // Delete the record for the old domain and re-retrieve the new info
      deleteRecord();
    } else {
      currentInfo.record = newRecord;
      updateDisplayState();
    }
  });
}

function updateDisplayStateAndBlot() {
  calculateHashblot();
  updateDisplayState();
}

function updateDomain() {
  document.getElementById('domain-fixed').textContent =
    document.getElementById('domain-input').value;
  updateDisplayStateAndBlot();
}

function deleteRecord() {
  chrome.storage.local.remove(['records.' + currentInfo.domain], function(){
    // load info for whatever the relevant domain is now
    loadInfo();
  });
}

function editDomain() {
  showOnlyChild('domaininfo', 'domain-input');
  document.getElementById('domain-input').setSelectionRange(0,0);
  document.getElementById('domain-input').focus();
}

// This is a bit of a mouthful - basically, it's for all the things that
// should happen after an "action", namely "finalizing" the domain name
// (hiding the input and displaying the "edit" button).
function snapToPostActionState() {
  showOnlyChild('domaininfo', 'domain-fixed');
}

function revertState() {
  setStateFromCurrentInfo();
  snapToPostActionState();
}

function closePopup() {
  window.close();
}

function hookupListeners() {
  // Get Element By ID, Add Event Listener
  function gebidael(id,event,listener){
    document.getElementById(id).addEventListener(event, listener);
  }

  gebidael('domain-input', 'keyup', updateDomain);
  gebidael('email', 'keyup', updateDisplayStateAndBlot);
  gebidael('salt', 'keyup', updateDisplayStateAndBlot);
  gebidael('memo', 'keyup', updateDisplayState);

  gebidael('domainedit', 'click', editDomain);

  gebidael('ok-dismiss', 'click', closePopup);
  gebidael('save-new', 'click', saveRecord);
  gebidael('cancel-new', 'click', closePopup);
  gebidael('ok-record', 'click', closePopup);
  gebidael('delete-record', 'click', deleteRecord);
  gebidael('save-edit', 'click', saveRecord);
  gebidael('cancel-edit', 'click', revertState);
  gebidael('save-blank', 'click', deleteRecord);
  gebidael('cancel-blank', 'click', revertState);
  gebidael('save-change', 'click', saveRecord);
  gebidael('cancel-change', 'click', revertState);
}

hookupListeners();
loadInfo();
<commit_msg>JS isn't Lua, no semicolons in constructors<commit_after>/*global chrome hashblot queue blotpw*/

function updateFromPhrase(phrase) {
  document.getElementById('blotpath').setAttribute('d',
    phrase ? hashblot.sha1qp(phrase) : '');
}

function calculateHashblot() {
  updateFromPhrase(blotpw.blotString({
    email: document.getElementById('email').value,
    domain: document.getElementById('domain').value,
    salt: document.getElementById('salt').value}));
}

var currentInfo;

function loadInfo() {
  chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
    var currentUrl = tabs[0].url;
    var hostname = blotpw.getHostnameFromUrl(changeInfo.url);
    if (hostname) {
      blotpw.getDomainAndInfo(hostname, function(err,info) {
        currentInfo = info;
        setStateFromCurrentInfo();
      });
    }
  });
}

function getResultsFromInfo(info) {
  return {
    domain: info.domain,
    email: info.record ? (info.record.email || '')
      : info.defaults && (info.defaults.email || ''),
    salt: info.record ? (info.record.salt || '')
      : info.defaults && (info.defaults.salt || ''),
    memo: info.record && (info.record.memo || '')
  };
}

function setStateFromCurrentInfo() {
  var info = getResultsFromInfo(currentInfo);
  document.getElementById('domain').value = info.domain;
  document.getElementById('domainname').textContent = info.domain;
  document.getElementById('email').value = info.email;
  document.getElementById('salt').value = info.salt;
  document.getElementById('memo').value = info.memo;
  calculateHashblot();
  setDomainProfileInfo();
  updateDisplayState();
  snapToPostActionState();
}

function setDomainProfileInfo() {
  //TODO: Populate info about this domain
}

function showOnlyChild(parentId, childId) {
  var children = document.getElementById(parentId).children;
  for (var i = 0; i < children.length; i++) {
    children[i].style.display = children[i].id == childId ? '' : 'none';
  }
}

function updateDisplayState() {
  var formDomain = document.getElementById('domain').value;
  var formEmail = document.getElementById('email').value;
  var formSalt = document.getElementById('salt').value;
  var formMemo = document.getElementById('memo').value;

  if (currentInfo.record) {

    if (formDomain == currentInfo.domain
      && formEmail == currentInfo.record.email
      && formSalt == currentInfo.record.salt) {

        if (formMemo == currentInfo.record.memo) {

          showOnlyChild('finishers', 'record-untouched');

        } else if (formMemo == '' && formEmail == currentInfo.defaults.email
          && formSalt == currentInfo.defaults.salt ) {

          // Technically, this could be a record of note if the domain
          // is not the domain that would be found by default for this URL.
          // However, I just do not feel like considering that case.
          showOnlyChild('finishers', 'record-blanked');

        } else {

          showOnlyChild('finishers', 'record-edited');
        }
    } else {

      showOnlyChild('finishers', 'record-changed');
    }
  } else {

    if (formEmail != currentInfo.defaults.email
      || formSalt != currentInfo.defaults.salt
      || formMemo) {

      showOnlyChild('finishers', 'norecord-edited');

    } else {

      showOnlyChild('finishers', 'norecord-untouched');
    }
  }
}

function saveRecord() {
  var newDomain = document.getElementById('domain').value;
  // TODO: alert if new record won't match the current domain
  var newRecord = {
    email: document.getElementById('email').value,
    salt: document.getElementById('salt').value,
    memo: document.getElementById('memo').value
  };
  var newItems = {};
  newItems['records.' + newDomain] = newRecord;
  chrome.storage.local.set(newItems,function(){
    // If the domain is changing
    if (currentInfo.domain != newDomain) {
      // Delete the record for the old domain and re-retrieve the new info
      deleteRecord();
    } else {
      currentInfo.record = newRecord;
      updateDisplayState();
    }
  });
}

function updateDisplayStateAndBlot() {
  calculateHashblot();
  updateDisplayState();
}

function updateDomain() {
  document.getElementById('domain-fixed').textContent =
    document.getElementById('domain-input').value;
  updateDisplayStateAndBlot();
}

function deleteRecord() {
  chrome.storage.local.remove(['records.' + currentInfo.domain], function(){
    // load info for whatever the relevant domain is now
    loadInfo();
  });
}

function editDomain() {
  showOnlyChild('domaininfo', 'domain-input');
  document.getElementById('domain-input').setSelectionRange(0,0);
  document.getElementById('domain-input').focus();
}

// This is a bit of a mouthful - basically, it's for all the things that
// should happen after an "action", namely "finalizing" the domain name
// (hiding the input and displaying the "edit" button).
function snapToPostActionState() {
  showOnlyChild('domaininfo', 'domain-fixed');
}

function revertState() {
  setStateFromCurrentInfo();
  snapToPostActionState();
}

function closePopup() {
  window.close();
}

function hookupListeners() {
  // Get Element By ID, Add Event Listener
  function gebidael(id,event,listener){
    document.getElementById(id).addEventListener(event, listener);
  }

  gebidael('domain-input', 'keyup', updateDomain);
  gebidael('email', 'keyup', updateDisplayStateAndBlot);
  gebidael('salt', 'keyup', updateDisplayStateAndBlot);
  gebidael('memo', 'keyup', updateDisplayState);

  gebidael('domainedit', 'click', editDomain);

  gebidael('ok-dismiss', 'click', closePopup);
  gebidael('save-new', 'click', saveRecord);
  gebidael('cancel-new', 'click', closePopup);
  gebidael('ok-record', 'click', closePopup);
  gebidael('delete-record', 'click', deleteRecord);
  gebidael('save-edit', 'click', saveRecord);
  gebidael('cancel-edit', 'click', revertState);
  gebidael('save-blank', 'click', deleteRecord);
  gebidael('cancel-blank', 'click', revertState);
  gebidael('save-change', 'click', saveRecord);
  gebidael('cancel-change', 'click', revertState);
}

hookupListeners();
loadInfo();
<|endoftext|>