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

import {Timer} from './timer';

describes.realWin('Timer', {}, env => {
  let windowMock;
  let timer;

  beforeEach(() => {
    const WindowApi = function() {};
    WindowApi.prototype.setTimeout = function(unusedCallback, unusedDelay) {};
    WindowApi.prototype.clearTimeout = function(unusedTimerId) {};
    WindowApi.prototype.document = {};
    const windowApi = new WindowApi();
    windowMock = sandbox.mock(windowApi);

    timer = new Timer(windowApi);
  });

  afterEach(() => {
    windowMock.verify();
    sandbox.restore();
  });

  it('delay', () => {
    const handler = () => {};
    windowMock
      .expects('setTimeout')
      .returns(1)
      .once();
    windowMock.expects('clearTimeout').never();
    timer.delay(handler, 111);
  });

  it('delay 0 real window', done => {
    timer = new Timer(self);
    timer.delay(done, 0);
  });

  it('delay 1 real window', done => {
    timer = new Timer(self);
    timer.delay(done, 1);
  });

  it('delay default', done => {
    windowMock.expects('setTimeout').never();
    windowMock.expects('clearTimeout').never();
    timer.delay(done);
  });

  it('cancel', () => {
    windowMock
      .expects('clearTimeout')
      .withExactArgs(1)
      .once();
    timer.cancel(1);
  });

  it('cancel default', done => {
    windowMock.expects('setTimeout').never();
    windowMock.expects('clearTimeout').never();
    const id = timer.delay(() => {
      throw new Error('should have been cancelled');
    });
    timer.cancel(id);

    // This makes sure the error has time to throw while this test
    // is still running.
    timer.delay(done);
  });

  it('promise', () => {
    windowMock
      .expects('setTimeout')
      .withExactArgs(
        sandbox.match(value => {
          value();
          return true;
        }),
        111
      )
      .returns(1)
      .once();

    let c = 0;
    return timer.promise(111).then(result => {
      c++;
      expect(c).to.equal(1);
      expect(result).to.be.undefined;
    });
  });

  it('timeoutPromise - no race', () => {
    windowMock
      .expects('setTimeout')
      .withExactArgs(
        sandbox.match(value => {
          value();
          return true;
        }),
        111
      )
      .returns(1)
      .once();

    let c = 0;
    return timer
      .timeoutPromise(111)
      .then(result => {
        c++;
        assert.fail('must never be here: ' + result);
      })
      .catch(reason => {
        c++;
        expect(c).to.equal(1);
        expect(reason.message).to.contain('timeout');
      });
  });

  it('timeoutPromise - race no timeout', () => {
    windowMock
      .expects('setTimeout')
      .withExactArgs(
        sandbox.match(value => {
          value();
          return true;
        }),
        111
      )
      .returns(1)
      .once();

    let c = 0;
    return timer.timeoutPromise(111, Promise.resolve('A')).then(result => {
      c++;
      expect(c).to.equal(1);
      expect(result).to.equal('A');
    });
  });

  it('timeoutPromise - race with timeout', () => {
    windowMock
      .expects('setTimeout')
      .withExactArgs(
        sandbox.match(value => {
          // Immediate timeout
          value();
          return true;
        }),
        111
      )
      .returns(1)
      .once();

    let c = 0;
    return timer
      .timeoutPromise(111, new Promise(() => {}))
      .then(result => {
        c++;
        assert.fail('must never be here: ' + result);
      })
      .catch(reason => {
        c++;
        expect(c).to.equal(1);
        expect(reason.message).to.contain('timeout');
      });
  });

  it('poll - resolves only when condition is true', () => {
    const realTimer = new Timer(env.win);
    let predicate = false;
    setTimeout(() => {
      predicate = true;
    }, 15);
    return realTimer
      .poll(10, () => {
        return predicate;
      })
      .then(() => {
        expect(predicate).to.be.true;
      });
  });

  it('poll - clears out interval when complete', () => {
    const realTimer = new Timer(env.win);
    const clearIntervalStub = sandbox.stub();
    env.win.clearInterval = clearIntervalStub;
    return realTimer
      .poll(111, () => {
        return true;
      })
      .then(() => {
        expect(clearIntervalStub).to.have.been.calledOnce;
      });
  });
});
<commit_msg>Fixes arg verification<commit_after>/**
 * Copyright 2018 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {Timer} from './timer';

describes.realWin('Timer', {}, env => {
  let windowMock;
  let timer;

  beforeEach(() => {
    const WindowApi = function() {};
    WindowApi.prototype.setTimeout = function(unusedCallback, unusedDelay) {};
    WindowApi.prototype.clearTimeout = function(unusedTimerId) {};
    WindowApi.prototype.document = {};
    const windowApi = new WindowApi();
    windowMock = sandbox.mock(windowApi);

    timer = new Timer(windowApi);
  });

  afterEach(() => {
    windowMock.verify();
    sandbox.restore();
  });

  it('delay', () => {
    const handler = () => {};
    windowMock
      .expects('setTimeout')
      .returns(1)
      .once();
    windowMock.expects('clearTimeout').never();
    timer.delay(handler, 111);
  });

  it('delay 0 real window', done => {
    timer = new Timer(self);
    timer.delay(done, 0);
  });

  it('delay 1 real window', done => {
    timer = new Timer(self);
    timer.delay(done, 1);
  });

  it('delay default', done => {
    windowMock.expects('setTimeout').never();
    windowMock.expects('clearTimeout').never();
    timer.delay(done);
  });

  it('cancel', () => {
    windowMock
      .expects('clearTimeout')
      .withExactArgs(1)
      .once();
    timer.cancel(1);
  });

  it('cancel default', done => {
    windowMock.expects('setTimeout').never();
    windowMock.expects('clearTimeout').never();
    const id = timer.delay(() => {
      throw new Error('should have been cancelled');
    });
    timer.cancel(id);

    // This makes sure the error has time to throw while this test
    // is still running.
    timer.delay(done);
  });

  it('promise', () => {
    windowMock
      .expects('setTimeout')
      .withExactArgs(
        sandbox.match(value => {
          value();
          return true;
        }),
        111
      )
      .returns(1)
      .once();

    let c = 0;
    return timer.promise(111).then(result => {
      c++;
      expect(c).to.equal(1);
      expect(result).to.be.undefined;
    });
  });

  it('timeoutPromise - no race', () => {
    windowMock
      .expects('setTimeout')
      .withExactArgs(
        sandbox.match(value => {
          value();
          return true;
        }),
        111
      )
      .returns(1)
      .once();

    let c = 0;
    return timer
      .timeoutPromise(111)
      .then(result => {
        c++;
        assert.fail('must never be here: ' + result);
      })
      .catch(reason => {
        c++;
        expect(c).to.equal(1);
        expect(reason.message).to.contain('timeout');
      });
  });

  it('timeoutPromise - race no timeout', () => {
    windowMock
      .expects('setTimeout')
      .withExactArgs(sandbox.match(fn => typeof fn === 'function'), 111)
      .returns(1)
      .once();

    let c = 0;
    return timer.timeoutPromise(111, Promise.resolve('A')).then(result => {
      c++;
      expect(c).to.equal(1);
      expect(result).to.equal('A');
    });
  });

  it('timeoutPromise - race with timeout', () => {
    windowMock
      .expects('setTimeout')
      .withExactArgs(
        sandbox.match(value => {
          // Immediate timeout
          value();
          return true;
        }),
        111
      )
      .returns(1)
      .once();

    let c = 0;
    return timer
      .timeoutPromise(111, new Promise(() => {}))
      .then(result => {
        c++;
        assert.fail('must never be here: ' + result);
      })
      .catch(reason => {
        c++;
        expect(c).to.equal(1);
        expect(reason.message).to.contain('timeout');
      });
  });

  it('poll - resolves only when condition is true', () => {
    const realTimer = new Timer(env.win);
    let predicate = false;
    setTimeout(() => {
      predicate = true;
    }, 15);
    return realTimer
      .poll(10, () => {
        return predicate;
      })
      .then(() => {
        expect(predicate).to.be.true;
      });
  });

  it('poll - clears out interval when complete', () => {
    const realTimer = new Timer(env.win);
    const clearIntervalStub = sandbox.stub();
    env.win.clearInterval = clearIntervalStub;
    return realTimer
      .poll(111, () => {
        return true;
      })
      .then(() => {
        expect(clearIntervalStub).to.have.been.calledOnce;
      });
  });
});
<|endoftext|>


<commit_before>import { countDecimalPlaces } from './utils'

describe('countDecimalPlaces', () => {
  [
    { value: '1.123', expected: 3 },
    { value: '1.12', expected: 2 },
    { value: '1.', expected: 0 },
    { value: '1', expected: 0 },
    { value: '.123', expected: 3 },
    { value: .123, expected: 3 },
    { value: '1e-3', expected: 3 },
    { value: '1e-2', expected: 2 },
    { value: '1.2e-2', expected: 3 },
    { value: '1.e-2', expected: 2 },
    { value: '1.23123e2', expected: 3 },
    { value: '123.123e+2', expected: 1 },
    { value: 123.123e+2, expected: 1 },
    { value: '.2e-2', expected: 3 },
    { value: '1', expected: 0 },
    { value: '123', expected: 0 },
    { value: '0', expected: 0 },
    { value: '-123', expected: 0 },
    { value: 'abc', expected: 0 },
    { value: 'e', expected: 0 },
    { value: '', expected: 0 },
    { value: null, expected: 0 },
    { value: false, expected: 0 },
    { value: undefined, expected: 0 }
  ].forEach(testCase => {
    it(`Should count decimals for ${testCase.value}`, () => {
      expect(countDecimalPlaces(testCase.value)).toBe(testCase.expected)
    })
  })
})
<commit_msg>Add validateMinCap tests<commit_after>import { countDecimalPlaces, validateMinCap } from './utils'

describe('countDecimalPlaces', () => {
  [
    { value: '1.123', expected: 3 },
    { value: '1.12', expected: 2 },
    { value: '1.', expected: 0 },
    { value: '1', expected: 0 },
    { value: '.123', expected: 3 },
    { value: .123, expected: 3 },
    { value: '1e-3', expected: 3 },
    { value: '1e-2', expected: 2 },
    { value: '1.2e-2', expected: 3 },
    { value: '1.e-2', expected: 2 },
    { value: '1.23123e2', expected: 3 },
    { value: '123.123e+2', expected: 1 },
    { value: 123.123e+2, expected: 1 },
    { value: '.2e-2', expected: 3 },
    { value: '1', expected: 0 },
    { value: '123', expected: 0 },
    { value: '0', expected: 0 },
    { value: '-123', expected: 0 },
    { value: 'abc', expected: 0 },
    { value: 'e', expected: 0 },
    { value: '', expected: 0 },
    { value: null, expected: 0 },
    { value: false, expected: 0 },
    { value: undefined, expected: 0 }
  ].forEach(testCase => {
    it(`Should count decimals for ${testCase.value}`, () => {
      expect(countDecimalPlaces(testCase.value)).toBe(testCase.expected)
    })
  })
})

describe('validateMinCap', () => {
  [
    { value: '', expected: true },
    { value: '0', expected: true },
    { value: '00', expected: true },
    { value: '1', expected: true },
    { value: '001', expected: true },
    { value: '150', expected: true },
    { value: '999', expected: true },
    { value: '-10', expected: false },
    { value: .123, expected: false },
    { value: '1.12', expected: false },
    { value: '1.', expected: false },
    { value: '1e10', expected: false },
    { value: '+1', expected: false },
    { value: null, expected: false },
    { value: false, expected: false },
    { value: undefined, expected: false }
  ].forEach(testCase => {
    const action = testCase.expected ? 'pass' : 'fail'

    it(`Should ${action} for '${testCase.value}'`, () => {
      expect(validateMinCap(testCase.value)).toBe(testCase.expected)
    })
  })
})
<|endoftext|>


<commit_before><commit_msg>Temporarily comment out failing test.<commit_after><|endoftext|>


<commit_before>/* eslint no-param-reassign: "off" */
import { Framework7 } from 'framework7/dist/js/framework7.modular';
import Utils from './utils';
import VueRouter from './vue-router';

export default {
  install(Vue) {
    // Event Hub
    const eventHub = new Vue();

    // Flags
    let f7Ready = false;
    let f7Instance;

    // Define protos
    const $theme = { ios: false, md: false };
    Vue.prototype.$f7 = undefined;
    Vue.prototype.Dom7 = Framework7.$;
    Vue.prototype.$$ = Framework7.$;
    Vue.prototype.$device = (Framework7.Device || Framework7.device);
    Vue.prototype.$theme = $theme;

    // Init F7
    function initFramework7(rootEl, params = {}, routes) {
      const f7Params = Utils.extend({}, params, { root: rootEl });
      if (routes && routes.length && !f7Params.routes) f7Params.routes = routes;

      f7Instance = new Framework7(f7Params);
      Vue.prototype.$f7 = f7Instance;
      $theme.ios = f7Instance.theme === 'ios';
      $theme.md = f7Instance.theme === 'md';
      f7Ready = true;
      eventHub.$emit('f7init', f7Instance);
    }

    // Extend Router
    Framework7.Router.use(VueRouter);

    // Mixin
    Vue.mixin({
      beforeCreate() {
        const self = this;
        if (self === self.$root) {
          const { theme } = (self.$options.framework7 || {});
          if (theme === 'md') $theme.md = true;
          if (theme === 'ios') $theme.ios = true;
          if (!theme || theme === 'auto') {
            $theme.ios = !!(Framework7.Device || Framework7.device).ios;
            $theme.md = !(Framework7.Device || Framework7.device).ios;
          }
        }

        let $route;
        let $router;
        let parent = self;
        while (parent && !$router && !$route) {
          if (parent.$route) $route = parent.$route;
          if (parent.$router) $router = parent.$router;
          else if (parent.f7View) {
            $router = parent.f7View.router;
          }
          parent = parent.$parent;
        }

        self.$route = $route;
        self.$router = $router;
      },
      mounted() {
        const self = this;
        if (self === self.$root) {
          initFramework7(self.$root.$el, self.$options.framework7, self.$options.routes);
        }
        if (!self.onF7Init) return;
        if (f7Ready) self.onF7Init(f7Instance);
        else {
          eventHub.$on('f7init', (f7) => {
            self.onF7Init(f7);
          });
        }
      },
    });
  },
};
<commit_msg>Use local F7<commit_after>/* eslint no-param-reassign: "off" */
import Utils from './utils';
import VueRouter from './vue-router';

export default {
  install(Vue, Framework7) {
    // Event Hub
    const eventHub = new Vue();

    // Flags
    let f7Ready = false;
    let f7Instance;

    // Define protos
    const $theme = { ios: false, md: false };
    Vue.prototype.$f7 = undefined;
    Vue.prototype.Dom7 = Framework7.$;
    Vue.prototype.$$ = Framework7.$;
    Vue.prototype.$device = (Framework7.Device || Framework7.device);
    Vue.prototype.$theme = $theme;

    // Init F7
    function initFramework7(rootEl, params = {}, routes) {
      const f7Params = Utils.extend({}, params, { root: rootEl });
      if (routes && routes.length && !f7Params.routes) f7Params.routes = routes;

      f7Instance = new Framework7(f7Params);
      Vue.prototype.$f7 = f7Instance;
      $theme.ios = f7Instance.theme === 'ios';
      $theme.md = f7Instance.theme === 'md';
      f7Ready = true;
      eventHub.$emit('f7init', f7Instance);
    }

    // Extend Router
    Framework7.Router.use(VueRouter);

    // Mixin
    Vue.mixin({
      beforeCreate() {
        const self = this;
        if (self === self.$root) {
          const { theme } = (self.$options.framework7 || {});
          if (theme === 'md') $theme.md = true;
          if (theme === 'ios') $theme.ios = true;
          if (!theme || theme === 'auto') {
            $theme.ios = !!(Framework7.Device || Framework7.device).ios;
            $theme.md = !(Framework7.Device || Framework7.device).ios;
          }
        }

        let $route;
        let $router;
        let parent = self;
        while (parent && !$router && !$route) {
          if (parent.$route) $route = parent.$route;
          if (parent.$router) $router = parent.$router;
          else if (parent.f7View) {
            $router = parent.f7View.router;
          }
          parent = parent.$parent;
        }

        self.$route = $route;
        self.$router = $router;
      },
      mounted() {
        const self = this;
        if (self === self.$root) {
          initFramework7(self.$root.$el, self.$options.framework7, self.$options.routes);
        }
        if (!self.onF7Init) return;
        if (f7Ready) self.onF7Init(f7Instance);
        else {
          eventHub.$on('f7init', (f7) => {
            self.onF7Init(f7);
          });
        }
      },
    });
  },
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Restful Design (restfuldesign.com), all rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*global require console module Buffer */

module.exports = (function(root) {
    "use strict";

    var https = require('https');
    var HTTP_ERROR = require('http').STATUS_CODES;

    var crypto = require('crypto');
    var querystring = require('querystring');

    function PandaError(code, error, message, data) {
        if (Error.captureStackTrace)
            Error.captureStackTrace(this, this.constructor);

        this.name = 'panda_error';

        if (error == undefined && isNaN(code)) {
            error = code;
        }

        if (typeof error === 'object') {
            this.code = error.code;
            this.error = error.error;
            this.message = error.message;
            this.data = error.data;
        } else {
            this.code = code;
            this.error = error;
            this.message = message;
            this.data = data;
        }
    }

    PandaError.prototype = Object.create(Error.prototype);
    PandaError.prototype.constructor = PandaError;

    var ERROR = {
        CONFIG_OPTIONS: {
            code: 1,
            error: 'config error',
            message: 'missing constructor options'
        },
        CONFIG_SHOP: {
            code: 2,
            error: 'config error',
            message: 'missing or invalid shop name'
        },
        CONFIG_OAUTH: {
            code: 3,
            error: 'config error',
            message: 'missing or invalid oauth settings'
        },
        OAUTH_SIGNATURE: {
            code: 4,
            error: 'oauth error',
            message: 'signature mismatch'
        },
        STREAM: {
            code: 5,
            error: 'stream error',
            message: 'missing or invalid stream'
        }
    };

    function PandaAPI(options) {

        if (!(this instanceof PandaAPI))
            return new PandaAPI(options);

        if (options == undefined)
            throw new PandaError(ERROR.CONFIG_OPTIONS);

        if (typeof options.shop !== 'string')
            throw new PandaError(ERROR.CONFIG_SHOP);

        if (options.oauth == undefined)
            throw new PandaError(ERROR.CONFIG_OAUTH);

        this.oauth = options.oauth;
        this.debug = !!options.debug;
        this.shop = options.shop;
        this.name = this.shop.split('.')[0];
        this.port = options.port || 443;

        this.httpsAgent = new https.Agent({
            keepAlive: options.keepAlive || true,
            maxSockets: options.maxSockets || 4
        });

        // used for debugging
        this.logger = this.debug ? (options.logger || console.log) : function() {};
    }

    PandaAPI.prototype = {
        "getAuthURL": function() {
            var url = 'https://' + this.shop;

            url += '/admin/oauth/authorize?';
            url += 'client_id=' + this.oauth.api_key;
            url += '&scope=' + this.oauth.scope;
            url += '&response_type=code';
            url += '&redirect_uri=' + this.oauth.redirect_uri;

            return url;
        },
        "setAccessToken": function(token) {
            this.oauth.access_token = token;
        },
        "urlQuerystring": function(url, query) {
            var uri;

            if (query == undefined) return querystring.parse(url);

            var q = (url.indexOf('?') >= 0 ? '&' : '?');

            if (typeof query === 'string') uri = q + query;
            else uri = q + querystring.stringify(query);

            url += uri;

            return url;
        },
        "validateSignature": function(params) {
            var signature = params['signature'],
                secret = this.oauth.shared_secret,
                parameters = [],
                digest,
                message;

            for (var key in params) {
                if (key != 'signature') {
                    parameters.push(key + '=' + params[key]);
                }
            }

            message = secret + parameters.sort().join('');

            digest = crypto.createHash('md5').update(message).digest('hex');

            return (digest === signature);
        },
        "exchangeToken": function(params, callback) {
            var data, error;

            if (!this.validateSignature(params)) {
                error = new PandaError(ERROR.OAUTH_SIGNATURE);

                if (callback) callback(error);
                else throw error;

                return;
            }

            data = {
                client_id: this.oauth.api_key,
                client_secret: this.oauth.private_key,
                code: params['code'],
                grant_type: 'authorization_code'
            };

            this.post('/admin/oauth/token.json', data, function(err, ret) {

                if (err) {
                    callback(err, ret);
                } else {
                    self.setAccessToken(ret['access_token']);
                    callback(undefined, ret);
                }
            });
        },
        request: function(method, path, data, options, callback) {
            var request = https.request,
                encoding = 'utf8',
                self = this,
                transmit,
                o = {};

            this.logger("%s", method, path);

            if (typeof options === 'function') {
                callback = options;
                options = undefined;
            }

            if (typeof data === 'function') {
                callback = data;
                data = undefined;
            }

            o.method = method || 'get';
            o.agent = this.httpsAgent;
            o.hostname = this.shop;
            o.port = this.port;
            o.path = path;
            o.headers = {};

            o.headers['accept'] = 'application/json';

            if (options.readable) {
                o.headers['transfer-encoding'] = 'chunked';
            } else {
                o.headers['content-type'] = 'application/json';
                o.headers['content-length'] = data ? Buffer.byteLength(data) : 0;

                if (data && typeof data !== 'string')
                    data = JSON.stringify(data); // note: throws on error
            }

            if (this.oauth.access_token) {
                o.headers['api-access-token'] = this.oauth.access_token;
            }

            if (typeof options === 'object') {
                if (options.timeout != undefined) {
                    request.socket.setTimeout(options.timeout);
                }

                if (options.headers != undefined) {
                    for (var key in options.headers)
                        o.headers[key] = options.headers[key];
                }

                if (options.encoding) encoding = options.encoding;
            }

            if (options.writable) {
                if (!data && !data.on && typeof data.on !== 'function')
                    throw new PandaError(ERROR.STREAM);

                transmit = request(o).on('response', function(message) {
                    var statusCode = message.statusCode,
                        statusMessage = message.statusMessage;

                    if (statusCode && statusCode < 400) {
                        message.pipe(data);
                    } else {
                        data.emit('error', new PandaError({
                            code: statusCode,
                            error: HTTP_ERROR[statusCode],
                            message: statusMessage,
                            data: message
                        }));
                    }

                });
            } else {
                transmit = request(o, function(response) {
                    var buffer = [];

                    response.on('data', function(chunk) {
                        buffer.push(chunk);
                    }).on('end', function() {
                        var headers = response.headers || {},
                            result = result = buffer.join(''),
                            statusCode = response.statusCode,
                            contentType = headers['content-type'];

                        self.logger("statusCode %d, content-type", statusCode, contentType);

                        if (contentType.indexOf('json') >= 0) {
                            result = JSON.parse(result);
                        }

                        self.logger("response: %j", JSON.stringify(result, null, 1));

                        if (statusCode && statusCode < 400) {
                            callback(undefined, result);
                        } else {
                            callback(new PandaError({
                                code: statusCode,
                                error: HTTP_ERROR[statusCode],
                                message: JSON.stringify(result),
                                data: result
                            }), response);
                        }

                    }).on('error', function(error) {
                        callback(error, response);
                    });
                });
            }

            transmit.on('error', function(error) {
                if (options.readable || options.writable) {
                    if (!data && !data.on && typeof data.on !== 'function')
                        throw new PandaError(ERROR.STREAM);

                    data.emit('error', error);
                }

                if (callback) callback(error);
                else throw new PandaError(error);
            });

            if (options.readable) {
                if (!data && !data.pipe && typeof data.pipe !== 'function')
                    throw new PandaError(ERROR.STREAM);

                data.pipe(transmit);
            } else {
                transmit.end(data, encoding);
            }

        }
    };

    // request methods
    ['head', 'get', 'put', 'post', 'delete', 'patch', 'trace', 'connect', 'options']
        .forEach(function(method) {
            PandaAPI.prototype[method] = function(url, data, opt, res) {
                return this.request(method, url, data, opt, res);
            };
        });

    return PandaAPI;

})(this);
<commit_msg>refactored logger output<commit_after>/*
 * Copyright (c) 2015 Restful Design (restfuldesign.com), all rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*global require console module Buffer */

module.exports = (function(root) {
    "use strict";

    var https = require('https');
    var HTTP_ERROR = require('http').STATUS_CODES;

    var crypto = require('crypto');
    var querystring = require('querystring');

    function PandaError(code, error, message, data) {
        if (Error.captureStackTrace)
            Error.captureStackTrace(this, this.constructor);

        this.name = 'panda_error';

        if (error == undefined && isNaN(code)) {
            error = code;
        }

        if (typeof error === 'object') {
            this.code = error.code;
            this.error = error.error;
            this.message = error.message;
            this.data = error.data;
        } else {
            this.code = code;
            this.error = error;
            this.message = message;
            this.data = data;
        }
    }

    PandaError.prototype = Object.create(Error.prototype);
    PandaError.prototype.constructor = PandaError;

    var ERROR = {
        CONFIG_OPTIONS: {
            code: 1,
            error: 'config error',
            message: 'missing constructor options'
        },
        CONFIG_SHOP: {
            code: 2,
            error: 'config error',
            message: 'missing or invalid shop name'
        },
        CONFIG_OAUTH: {
            code: 3,
            error: 'config error',
            message: 'missing or invalid oauth settings'
        },
        OAUTH_SIGNATURE: {
            code: 4,
            error: 'oauth error',
            message: 'signature mismatch'
        },
        STREAM: {
            code: 5,
            error: 'stream error',
            message: 'missing or invalid stream'
        }
    };

    function PandaAPI(options) {

        if (!(this instanceof PandaAPI))
            return new PandaAPI(options);

        if (options == undefined)
            throw new PandaError(ERROR.CONFIG_OPTIONS);

        if (typeof options.shop !== 'string')
            throw new PandaError(ERROR.CONFIG_SHOP);

        if (options.oauth == undefined)
            throw new PandaError(ERROR.CONFIG_OAUTH);

        this.oauth = options.oauth;
        this.debug = !!options.debug;
        this.shop = options.shop;
        this.name = this.shop.split('.')[0];
        this.port = options.port || 443;

        this.httpsAgent = new https.Agent({
            keepAlive: options.keepAlive || true,
            maxSockets: options.maxSockets || 4
        });

        // used for debugging
        this.logger = this.debug ? (options.logger || console.log) : function() {};
    }

    PandaAPI.prototype = {
        "getAuthURL": function() {
            var url = 'https://' + this.shop;

            url += '/admin/oauth/authorize?';
            url += 'client_id=' + this.oauth.api_key;
            url += '&scope=' + this.oauth.scope;
            url += '&response_type=code';
            url += '&redirect_uri=' + this.oauth.redirect_uri;

            return url;
        },
        "setAccessToken": function(token) {
            this.oauth.access_token = token;
        },
        "urlQuerystring": function(url, query) {
            var uri;

            if (query == undefined) return querystring.parse(url);

            var q = (url.indexOf('?') >= 0 ? '&' : '?');

            if (typeof query === 'string') uri = q + query;
            else uri = q + querystring.stringify(query);

            url += uri;

            return url;
        },
        "validateSignature": function(params) {
            var signature = params['signature'],
                secret = this.oauth.shared_secret,
                parameters = [],
                digest,
                message;

            for (var key in params) {
                if (key != 'signature') {
                    parameters.push(key + '=' + params[key]);
                }
            }

            message = secret + parameters.sort().join('');

            digest = crypto.createHash('md5').update(message).digest('hex');

            return (digest === signature);
        },
        "exchangeToken": function(params, callback) {
            var data, error;

            if (!this.validateSignature(params)) {
                error = new PandaError(ERROR.OAUTH_SIGNATURE);

                if (callback) callback(error);
                else throw error;

                return;
            }

            data = {
                client_id: this.oauth.api_key,
                client_secret: this.oauth.private_key,
                code: params['code'],
                grant_type: 'authorization_code'
            };

            this.post('/admin/oauth/token.json', data, function(err, ret) {

                if (err) {
                    callback(err, ret);
                } else {
                    self.setAccessToken(ret['access_token']);
                    callback(undefined, ret);
                }
            });
        },
        request: function(method, path, data, options, callback) {
            var request = https.request,
                encoding = 'utf8',
                self = this,
                transmit,
                o = {};

            this.logger("request: %s", method, path);

            if (typeof options === 'function') {
                callback = options;
                options = undefined;
            }

            if (typeof data === 'function') {
                callback = data;
                data = undefined;
            }

            o.method = method || 'get';
            o.agent = this.httpsAgent;
            o.hostname = this.shop;
            o.port = this.port;
            o.path = path;
            o.headers = {};

            o.headers['accept'] = 'application/json';

            if (options.readable) {
                o.headers['transfer-encoding'] = 'chunked';
            } else {
                o.headers['content-type'] = 'application/json';
                o.headers['content-length'] = data ? Buffer.byteLength(data) : 0;

                if (data && typeof data !== 'string')
                    data = JSON.stringify(data); // note: throws on error
            }

            if (this.oauth.access_token) {
                o.headers['api-access-token'] = this.oauth.access_token;
            }

            if (typeof options === 'object') {
                if (options.timeout != undefined) {
                    request.socket.setTimeout(options.timeout);
                }

                if (options.headers != undefined) {
                    for (var key in options.headers)
                        o.headers[key] = options.headers[key];
                }

                if (options.encoding) encoding = options.encoding;
            }

            if (options.writable) {
                if (!data && !data.on && typeof data.on !== 'function')
                    throw new PandaError(ERROR.STREAM);

                transmit = request(o).on('response', function(message) {
                    var statusCode = message.statusCode,
                        statusMessage = message.statusMessage;

                    if (statusCode && statusCode < 400) {
                        message.pipe(data);
                    } else {
                        data.emit('error', new PandaError({
                            code: statusCode,
                            error: HTTP_ERROR[statusCode],
                            message: statusMessage,
                            data: message
                        }));
                    }

                });
            } else {
                transmit = request(o, function(response) {
                    var buffer = [];

                    response.on('data', function(chunk) {
                        buffer.push(chunk);
                    }).on('end', function() {
                        var headers = response.headers || {},
                            result = result = buffer.join(''),
                            statusCode = response.statusCode,
                            contentType = headers['content-type'];

                        if (contentType.indexOf('json') >= 0) {
                            result = JSON.parse(result);
                        }

                        self.logger("response: status code %d, content type %s %s %s",
                                    statusCode, contentType,
                                    JSON.stringify(headers, null, 1),
                                    JSON.stringify(result, null, 1));

                        if (statusCode && statusCode < 400) {
                            callback(undefined, result);
                        } else {
                            callback(new PandaError({
                                code: statusCode,
                                error: HTTP_ERROR[statusCode],
                                message: JSON.stringify(result),
                                data: result
                            }), response);
                        }

                    }).on('error', function(error) {
                        callback(error, response);
                    });
                });
            }

            transmit.on('error', function(error) {
                if (options.readable || options.writable) {
                    if (!data && !data.on && typeof data.on !== 'function')
                        throw new PandaError(ERROR.STREAM);

                    data.emit('error', error);
                }

                if (callback) callback(error);
                else throw new PandaError(error);
            });

            if (options.readable) {
                if (!data && !data.pipe && typeof data.pipe !== 'function')
                    throw new PandaError(ERROR.STREAM);

                data.pipe(transmit);
            } else {
                transmit.end(data, encoding);
            }

        }
    };

    // request methods
    ['head', 'get', 'put', 'post', 'delete', 'patch', 'trace', 'connect', 'options']
        .forEach(function(method) {
            PandaAPI.prototype[method] = function(url, data, opt, res) {
                return this.request(method, url, data, opt, res);
            };
        });

    return PandaAPI;

})(this);
<|endoftext|>


<commit_before>(function () {
    if ('modules' in app && 'adminLectures' in app.modules) {
        return;
    }

    /**
     * Creates a new AdminLectures class.
     * @class
     */
    var AdminLectures = function () {
        var self = this;

        self.body = '';
        self.alert = '';
        self.getLecture = '';
        self.getSchool = '';
        self.getAuthor = '';
        self.getPlace = '';
        self.insert = '';
        self.update = '';
        self.container = document.querySelector('.adminLectures');
        self.content = self.container.querySelector('.adminLectures__content');
        self.lectureSelect = self.container.querySelector('.formSelect__select[name="lecture"]');
        self.editTemplate = self.container.querySelector('#adminLectures-template').innerHTML;

        /**
         * Add event listeners
         */
        self.setupListener = function() {
            self.lectureSelect.addEventListener('change', self.handleState);
            self.container.addEventListener('click', self.router);
        };

        /**
         * Route container click event
         * @param {Object} event
         */
        self.router = function(event) {
            event.preventDefault();

            var action = event.target.dataset.action;

            if (action) {

                if (self[action]) {

                    self[action]();

                }

            }
        };

        /**
         * Add item to database
         */
        self.add = function() {
            self.insert('lectures', {
                id: parseInt(self.container.querySelector('.formInput__input[name="id"]').value),
                title: self.container.querySelector('.formInput__input[name="title"]').value,
                school: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="school"] option:checked')).map(function(item) {return parseInt(item.value)}),
                author: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="author"] option:checked')).map(function(item) {return parseInt(item.value)}),
                date: self.container.querySelector('.formInput__input[name="date"]').value,
                time: self.container.querySelector('.formInput__input[name="time"]').value,
                place: parseInt(self.container.querySelector('.formSelect__select[name="place"]').value),
                isOver: parseInt(self.container.querySelector('.formSelect__select[name="isOver"]').value) === 1,
                resources: self.container.querySelector('.formInput__input[name="resources"]').value
            }).then(function(response) {
                self.alert({
                    title: '',
                    text: response,
                    type: 'success'
                });

                self.setupLectureFilter();
                self.setNewState();
            }, function(response) {
                self.alert({
                    title: '',
                    text: response,
                    type: 'error'
                });
            });
        };

        /**
         * Edit item in database
         */
        self.edit = function() {
            self.update('lectures', {
                id: parseInt(self.container.querySelector('.formInput__input[name="id"]').value),
                title: self.container.querySelector('.formInput__input[name="title"]').value,
                school: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="school"] option:checked')).map(function(item) {return parseInt(item.value)}),
                author: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="author"] option:checked')).map(function(item) {return parseInt(item.value)}),
                date: self.container.querySelector('.formInput__input[name="date"]').value,
                time: self.container.querySelector('.formInput__input[name="time"]').value,
                place: parseInt(self.container.querySelector('.formSelect__select[name="place"]').value),
                isOver: parseInt(self.container.querySelector('.formSelect__select[name="isOver"]').value) === 1,
                resources: self.container.querySelector('.formInput__input[name="resources"]').value
            }).then(function(response) {
                self.alert({
                    title: '',
                    text: response,
                    type: 'success'
                });
            }, function(response) {
                self.alert({
                    title: '',
                    text: response,
                    type: 'error'
                });
            });
        };

        /**
         * Remove item from database
         */
        self.remove = function() {
            if (confirm('Вы действительно хотите удалить выбранную аудиторию?')) {

                self.delete('places', {
                    id: parseInt(self.container.querySelector('.formInput__input[name="id"]').value),
                    title: self.container.querySelector('.formInput__input[name="title"]').value,
                    address: self.container.querySelector('.formInput__input[name="address"]').value,
                    link: self.container.querySelector('.formInput__input[name="link"]').value,
                    capacity: parseInt(self.container.querySelector('.formInput__input[name="capacity"]').value)
                }).then(function(response) {
                    self.alert({
                        title: '',
                        text: response,
                        type: 'success'
                    });

                    self.setupPlaceFilter();
                    self.setNewState();
                }, function(response) {
                    self.alert({
                        title: '',
                        text: response,
                        type: 'error'
                    });
                });

            }
        };

        /**
         * Setup lecture filter values
         */
        self.setupLectureFilter = function() {
            self.getLecture().then(function(response) {
                var html = '<option value=""></option>';

                response.forEach(function(item) {
                    html += '<option value="'+ item.id +'">'+ item.title +'</option>';
                });

                self.lectureSelect.innerHTML = html;
            });
        };

        /**
         * Change block state according to filter select value
         */
        self.handleState = function() {
            if (self.lectureSelect.value) {

                self.setEditState();

            } else {

                self.setNewState();

            }
        };

        /**
         * Prepare template for edit/delete item state
         */
        self.setEditState = function() {
            var lectureId = self.lectureSelect.value ? parseInt(self.lectureSelect.value) : '',
                data = {};

            self.getSchool().then(function(response) {
                data['schools'] = response;

                return self.getAuthor();
            }).then(function(response) {
                data['authors'] = response;

                return self.getPlace();
            }).then(function(response) {
                data['places'] = response;

                self.getLecture(lectureId).then(function(response) {
                    data['response'] = response[0];

                    var box = document.createElement('div');

                    box.classList.add('adminLectures__edit');

                    box.innerHTML = self.editTemplate;

                    var schoolSelect = '',
                        authorSelect = '',
                        placeSelect = '';

                    data.schools.forEach(function(item) {
                        var isSelected = data.response.school.indexOf(item.id) !== -1 ? 'selected' : '';

                        schoolSelect += '<option '+ isSelected +' value="'+ item.id +'">'+ item.title +'</option>';
                    });

                    data.authors.forEach(function(item) {
                        var isSelected = data.response.author.indexOf(item.id) !== -1 ? 'selected' : '';

                        authorSelect += '<option '+ isSelected +' value="'+ item.id +'">'+ item.title +'</option>';
                    });

                    data.places.forEach(function(item) {
                        var isSelected = data.response.place === item.id ? 'selected' : '';

                        placeSelect += '<option '+ isSelected +' value="'+ item.id +'">'+ item.title +'</option>';
                    });

                    box.querySelector('.adminLectures__title').style.display = 'none';

                    box.querySelector('.formInput__input[name="id"]').value = data.response.id;

                    box.querySelector('.formInput__input[name="title"]').value = data.response.title;

                    box.querySelector('.formSelect__select[name="school"]').innerHTML = schoolSelect;

                    box.querySelector('.formSelect__select[name="author"]').innerHTML = authorSelect;

                    box.querySelector('.formInput__input[name="date"]').value = data.response.date;

                    box.querySelector('.formInput__input[name="time"]').value = data.response.time;

                    box.querySelector('.formSelect__select[name="place"]').innerHTML = placeSelect;

                    box.querySelector('.formSelect__select[name="isOver"] option[value="' + (data.response.isOver ? 1 : 0) + '"]').selected = true;

                    box.querySelector('.formInput__input[name="resources"]').value = data.response.resources;

                    box.querySelector('.adminLectures__box__button_type_add').style.display = 'none';

                    self.render(box);
                });
            });
        };

        /**
         * Prepare template for add new item state
         */
        self.setNewState = function() {
            var data = {};

            self.getSchool().then(function(response) {
                data['schools'] = response;

                return self.getAuthor();
            }).then(function(response) {
                data['authors'] = response;

                return self.getPlace();
            }).then(function(response) {
                data['places'] = response;

                var box = document.createElement('div');

                box.classList.add('adminLectures__add');

                box.innerHTML = self.editTemplate;

                var schoolSelect = '',
                    authorSelect = '',
                    placeSelect = '';

                data.schools.forEach(function(item) {
                    schoolSelect += '<option value="'+ item.id +'">'+ item.title +'</option>';
                });

                data.authors.forEach(function(item) {
                    authorSelect += '<option value="'+ item.id +'">'+ item.title +'</option>';
                });

                data.places.forEach(function(item) {
                    placeSelect += '<option value="'+ item.id +'">'+ item.title +'</option>';
                });

                box.querySelector('.adminLectures__box__col_1').style.display = 'none';

                box.querySelector('.formSelect__select[name="school"]').innerHTML = schoolSelect;

                box.querySelector('.formSelect__select[name="author"]').innerHTML = authorSelect;

                box.querySelector('.formSelect__select[name="place"]').innerHTML = placeSelect;

                box.querySelector('.adminLectures__box__button_type_edit').style.display = 'none';

                box.querySelector('.adminLectures__box__button_type_remove').style.display = 'none';

                self.render(box);
            });
        };

        /**
         * Render template
         */
        self.render = function(template) {
            self.content.innerHTML = '';

            self.content.appendChild(template);
        };

        /**
         * Import properties from Main class
         */
        self.importDefaults = function() {
            self.body = app.modules.main.body;
            self.alert = app.modules.admin.alert;
            self.getLecture = app.modules.main.library.getLecture;
            self.getSchool = app.modules.main.library.getSchool;
            self.getAuthor = app.modules.main.library.getAuthor;
            self.getPlace = app.modules.main.library.getPlace;
            self.insert = app.modules.main.library.insert;
            self.update = app.modules.main.library.update;
            self.delete = app.modules.main.library.delete;
        };

        /**
         * Init module
         */
        self.init = function() {
            if (self.container) {

                self.importDefaults();
                self.setupLectureFilter();
                self.handleState();
                self.setupListener();

            }
        };
    };

    if (!('modules' in app)) {
        app.modules = {};
    }

    app.modules.adminLectures = new AdminLectures();
})();
<commit_msg>Add method remove to block adminLectures<commit_after>(function () {
    if ('modules' in app && 'adminLectures' in app.modules) {
        return;
    }

    /**
     * Creates a new AdminLectures class.
     * @class
     */
    var AdminLectures = function () {
        var self = this;

        self.body = '';
        self.alert = '';
        self.getLecture = '';
        self.getSchool = '';
        self.getAuthor = '';
        self.getPlace = '';
        self.insert = '';
        self.update = '';
        self.container = document.querySelector('.adminLectures');
        self.content = self.container.querySelector('.adminLectures__content');
        self.lectureSelect = self.container.querySelector('.formSelect__select[name="lecture"]');
        self.editTemplate = self.container.querySelector('#adminLectures-template').innerHTML;

        /**
         * Add event listeners
         */
        self.setupListener = function() {
            self.lectureSelect.addEventListener('change', self.handleState);
            self.container.addEventListener('click', self.router);
        };

        /**
         * Route container click event
         * @param {Object} event
         */
        self.router = function(event) {
            event.preventDefault();

            var action = event.target.dataset.action;

            if (action) {

                if (self[action]) {

                    self[action]();

                }

            }
        };

        /**
         * Add item to database
         */
        self.add = function() {
            self.insert('lectures', {
                id: parseInt(self.container.querySelector('.formInput__input[name="id"]').value),
                title: self.container.querySelector('.formInput__input[name="title"]').value,
                school: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="school"] option:checked')).map(function(item) {return parseInt(item.value)}),
                author: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="author"] option:checked')).map(function(item) {return parseInt(item.value)}),
                date: self.container.querySelector('.formInput__input[name="date"]').value,
                time: self.container.querySelector('.formInput__input[name="time"]').value,
                place: parseInt(self.container.querySelector('.formSelect__select[name="place"]').value),
                isOver: parseInt(self.container.querySelector('.formSelect__select[name="isOver"]').value) === 1,
                resources: self.container.querySelector('.formInput__input[name="resources"]').value
            }).then(function(response) {
                self.alert({
                    title: '',
                    text: response,
                    type: 'success'
                });

                self.setupLectureFilter();
                self.setNewState();
            }, function(response) {
                self.alert({
                    title: '',
                    text: response,
                    type: 'error'
                });
            });
        };

        /**
         * Edit item in database
         */
        self.edit = function() {
            self.update('lectures', {
                id: parseInt(self.container.querySelector('.formInput__input[name="id"]').value),
                title: self.container.querySelector('.formInput__input[name="title"]').value,
                school: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="school"] option:checked')).map(function(item) {return parseInt(item.value)}),
                author: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="author"] option:checked')).map(function(item) {return parseInt(item.value)}),
                date: self.container.querySelector('.formInput__input[name="date"]').value,
                time: self.container.querySelector('.formInput__input[name="time"]').value,
                place: parseInt(self.container.querySelector('.formSelect__select[name="place"]').value),
                isOver: parseInt(self.container.querySelector('.formSelect__select[name="isOver"]').value) === 1,
                resources: self.container.querySelector('.formInput__input[name="resources"]').value
            }).then(function(response) {
                self.alert({
                    title: '',
                    text: response,
                    type: 'success'
                });
            }, function(response) {
                self.alert({
                    title: '',
                    text: response,
                    type: 'error'
                });
            });
        };

        /**
         * Remove item from database
         */
        self.remove = function() {
            if (confirm('Вы действительно хотите удалить выбранную лекцию?')) {

                self.delete('lectures', {
                    id: parseInt(self.container.querySelector('.formInput__input[name="id"]').value),
                    title: self.container.querySelector('.formInput__input[name="title"]').value,
                    school: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="school"] option:checked')).map(function(item) {return parseInt(item.value)}),
                    author: [].slice.call(self.container.querySelectorAll('.formSelect__select[name="author"] option:checked')).map(function(item) {return parseInt(item.value)}),
                    date: self.container.querySelector('.formInput__input[name="date"]').value,
                    time: self.container.querySelector('.formInput__input[name="time"]').value,
                    place: parseInt(self.container.querySelector('.formSelect__select[name="place"]').value),
                    isOver: parseInt(self.container.querySelector('.formSelect__select[name="isOver"]').value) === 1,
                    resources: self.container.querySelector('.formInput__input[name="resources"]').value
                }).then(function(response) {
                    self.alert({
                        title: '',
                        text: response,
                        type: 'success'
                    });

                    self.setupLectureFilter();
                    self.setNewState();
                }, function(response) {
                    self.alert({
                        title: '',
                        text: response,
                        type: 'error'
                    });
                });

            }
        };

        /**
         * Setup lecture filter values
         */
        self.setupLectureFilter = function() {
            self.getLecture().then(function(response) {
                var html = '<option value=""></option>';

                response.forEach(function(item) {
                    html += '<option value="'+ item.id +'">'+ item.title +'</option>';
                });

                self.lectureSelect.innerHTML = html;
            });
        };

        /**
         * Change block state according to filter select value
         */
        self.handleState = function() {
            if (self.lectureSelect.value) {

                self.setEditState();

            } else {

                self.setNewState();

            }
        };

        /**
         * Prepare template for edit/delete item state
         */
        self.setEditState = function() {
            var lectureId = self.lectureSelect.value ? parseInt(self.lectureSelect.value) : '',
                data = {};

            self.getSchool().then(function(response) {
                data['schools'] = response;

                return self.getAuthor();
            }).then(function(response) {
                data['authors'] = response;

                return self.getPlace();
            }).then(function(response) {
                data['places'] = response;

                self.getLecture(lectureId).then(function(response) {
                    data['response'] = response[0];

                    var box = document.createElement('div');

                    box.classList.add('adminLectures__edit');

                    box.innerHTML = self.editTemplate;

                    var schoolSelect = '',
                        authorSelect = '',
                        placeSelect = '';

                    data.schools.forEach(function(item) {
                        var isSelected = data.response.school.indexOf(item.id) !== -1 ? 'selected' : '';

                        schoolSelect += '<option '+ isSelected +' value="'+ item.id +'">'+ item.title +'</option>';
                    });

                    data.authors.forEach(function(item) {
                        var isSelected = data.response.author.indexOf(item.id) !== -1 ? 'selected' : '';

                        authorSelect += '<option '+ isSelected +' value="'+ item.id +'">'+ item.title +'</option>';
                    });

                    data.places.forEach(function(item) {
                        var isSelected = data.response.place === item.id ? 'selected' : '';

                        placeSelect += '<option '+ isSelected +' value="'+ item.id +'">'+ item.title +'</option>';
                    });

                    box.querySelector('.adminLectures__title').style.display = 'none';

                    box.querySelector('.formInput__input[name="id"]').value = data.response.id;

                    box.querySelector('.formInput__input[name="title"]').value = data.response.title;

                    box.querySelector('.formSelect__select[name="school"]').innerHTML = schoolSelect;

                    box.querySelector('.formSelect__select[name="author"]').innerHTML = authorSelect;

                    box.querySelector('.formInput__input[name="date"]').value = data.response.date;

                    box.querySelector('.formInput__input[name="time"]').value = data.response.time;

                    box.querySelector('.formSelect__select[name="place"]').innerHTML = placeSelect;

                    box.querySelector('.formSelect__select[name="isOver"] option[value="' + (data.response.isOver ? 1 : 0) + '"]').selected = true;

                    box.querySelector('.formInput__input[name="resources"]').value = data.response.resources;

                    box.querySelector('.adminLectures__box__button_type_add').style.display = 'none';

                    self.render(box);
                });
            });
        };

        /**
         * Prepare template for add new item state
         */
        self.setNewState = function() {
            var data = {};

            self.getSchool().then(function(response) {
                data['schools'] = response;

                return self.getAuthor();
            }).then(function(response) {
                data['authors'] = response;

                return self.getPlace();
            }).then(function(response) {
                data['places'] = response;

                var box = document.createElement('div');

                box.classList.add('adminLectures__add');

                box.innerHTML = self.editTemplate;

                var schoolSelect = '',
                    authorSelect = '',
                    placeSelect = '';

                data.schools.forEach(function(item) {
                    schoolSelect += '<option value="'+ item.id +'">'+ item.title +'</option>';
                });

                data.authors.forEach(function(item) {
                    authorSelect += '<option value="'+ item.id +'">'+ item.title +'</option>';
                });

                data.places.forEach(function(item) {
                    placeSelect += '<option value="'+ item.id +'">'+ item.title +'</option>';
                });

                box.querySelector('.adminLectures__box__col_1').style.display = 'none';

                box.querySelector('.formSelect__select[name="school"]').innerHTML = schoolSelect;

                box.querySelector('.formSelect__select[name="author"]').innerHTML = authorSelect;

                box.querySelector('.formSelect__select[name="place"]').innerHTML = placeSelect;

                box.querySelector('.adminLectures__box__button_type_edit').style.display = 'none';

                box.querySelector('.adminLectures__box__button_type_remove').style.display = 'none';

                self.render(box);
            });
        };

        /**
         * Render template
         */
        self.render = function(template) {
            self.content.innerHTML = '';

            self.content.appendChild(template);
        };

        /**
         * Import properties from Main class
         */
        self.importDefaults = function() {
            self.body = app.modules.main.body;
            self.alert = app.modules.admin.alert;
            self.getLecture = app.modules.main.library.getLecture;
            self.getSchool = app.modules.main.library.getSchool;
            self.getAuthor = app.modules.main.library.getAuthor;
            self.getPlace = app.modules.main.library.getPlace;
            self.insert = app.modules.main.library.insert;
            self.update = app.modules.main.library.update;
            self.delete = app.modules.main.library.delete;
        };

        /**
         * Init module
         */
        self.init = function() {
            if (self.container) {

                self.importDefaults();
                self.setupLectureFilter();
                self.handleState();
                self.setupListener();

            }
        };
    };

    if (!('modules' in app)) {
        app.modules = {};
    }

    app.modules.adminLectures = new AdminLectures();
})();
<|endoftext|>


<commit_before>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* Utility functions for dealing with Arrays.
*
* @class Phaser.ArrayUtils
* @static
*/
Phaser.ArrayUtils = {

    /**
    * Fetch a random entry from the given array.
    *
    * Will return null if there are no array items that fall within the specified range
    * or if there is no item for the randomly chosen index.
    *
    * @method Phaser.ArrayUtils.getRandomItem
    * @param {any[]} objects - An array of objects.
    * @param {integer} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array.
    * @param {integer} length - Optional restriction on the number of values you want to randomly select from.
    * @return {object} The random object that was selected.
    */
    getRandomItem: function (objects, startIndex, length) {

        if (objects === null) { return null; }
        if (startIndex === undefined) { startIndex = 0; }
        if (length === undefined) { length = objects.length; }

        var randomIndex = startIndex + Math.floor(Math.random() * length);

        return objects[randomIndex] === undefined ? null : objects[randomIndex];

    },

    /**
    * Removes a random object from the given array and returns it.
    *
    * Will return null if there are no array items that fall within the specified range
    * or if there is no item for the randomly chosen index.
    *
    * @method Phaser.ArrayUtils.removeRandomItem
    * @param {any[]} objects - An array of objects.
    * @param {integer} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array.
    * @param {integer} length - Optional restriction on the number of values you want to randomly select from.
    * @return {object} The random object that was removed.
    */
    removeRandomItem: function (objects, startIndex, length) {

        if (objects == null) { // undefined or null
            return null;
        }

        if (startIndex === undefined) { startIndex = 0; }
        if (length === undefined) { length = objects.length; }

        var randomIndex = startIndex + Math.floor(Math.random() * length);
        if (randomIndex < objects.length)
        {
            var removed = objects.splice(randomIndex, 1);
            return removed[0] === undefined ? null : removed[0];
        }
        else
        {
            return null;
        }

    },

    /**
    * Remove one or more items at the given index and reorder the array.
    *
    * The new array length will be `array.length - count`.
    *
    * This is an alternative to `array.splice(startIndex, count)`.
    *
    * @see https://github.com/mreinstein/remove-array-items
    * @see https://gamealchemist.wordpress.com/2013/05/01/lets-get-those-javascript-arrays-to-work-fast/
    *
    * @method Phaser.ArrayUtils.remove
    * @param {any[]} array
    * @param {integer} startIndex
    * @param {integer} [count=1]
    * @return {any[]} The modified array.
    */
    remove: function (array, startIndex, count)
    {
        var length = array.length;

        if (startIndex >= length || count === 0) { return; }

        if (count == null) { count = 1; }

        var newLength = length - count;

        for (var i = startIndex; i < newLength; ++i)
        {
            array[i] = array[i + count];
        }

        array.length = newLength;
    },

    /**
    * A standard Fisher-Yates Array shuffle implementation which modifies the array in place.
    *
    * @method Phaser.ArrayUtils.shuffle
    * @param {any[]} array - The array to shuffle.
    * @return {any[]} The original array, now shuffled.
    */
    shuffle: function (array) {

        for (var i = array.length - 1; i > 0; i--)
        {
            var j = Math.floor(Math.random() * (i + 1));
            var temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }

        return array;

    },

    /**
    * Transposes the elements of the given matrix (array of arrays).
    *
    * @method Phaser.ArrayUtils.transposeMatrix
    * @param {Array<any[]>} array - The matrix to transpose.
    * @return {Array<any[]>} A new transposed matrix
    */
    transposeMatrix: function (array) {

        var sourceRowCount = array.length;
        var sourceColCount = array[0].length;

        var result = new Array(sourceColCount);

        for (var i = 0; i < sourceColCount; i++)
        {
            result[i] = new Array(sourceRowCount);

            for (var j = sourceRowCount - 1; j > -1; j--)
            {
                result[i][j] = array[j][i];
            }
        }

        return result;

    },

    /**
    * Rotates the given matrix (array of arrays).
    *
    * Based on the routine from {@link http://jsfiddle.net/MrPolywhirl/NH42z/}.
    *
    * @method Phaser.ArrayUtils.rotateMatrix
    * @param {Array<any[]>} matrix - The array to rotate; this matrix _may_ be altered.
    * @param {number|string} direction - The amount to rotate: the rotation in degrees (90, -90, 270, -270, 180) or a string command ('rotateLeft', 'rotateRight' or 'rotate180').
    * @return {Array<any[]>} The rotated matrix. The source matrix should be discarded for the returned matrix.
    */
    rotateMatrix: function (matrix, direction) {

        if (typeof direction !== 'string')
        {
            direction = ((direction % 360) + 360) % 360;
        }

        if (direction === 90 || direction === -270 || direction === 'rotateLeft')
        {
            matrix = Phaser.ArrayUtils.transposeMatrix(matrix);
            matrix = matrix.reverse();
        }
        else if (direction === -90 || direction === 270 || direction === 'rotateRight')
        {
            matrix = matrix.reverse();
            matrix = Phaser.ArrayUtils.transposeMatrix(matrix);
        }
        else if (Math.abs(direction) === 180 || direction === 'rotate180')
        {
            for (var i = 0; i < matrix.length; i++)
            {
                matrix[i].reverse();
            }

            matrix = matrix.reverse();
        }

        return matrix;

    },

    /**
    * Snaps a value to the nearest value in a sorted numeric array.
    * The result will always be in the range `[first_value, last_value]`.
    *
    * @method Phaser.ArrayUtils.findClosest
    * @param {number} value - The search value
    * @param {number[]} arr - The input array which _must_ be sorted.
    * @return {number} The nearest value found.
    */
    findClosest: function (value, arr) {

        if (!arr.length)
        {
            return NaN;
        }
        else if (arr.length === 1 || value < arr[0])
        {
            return arr[0];
        }

        var i = 1;
        while (arr[i] < value) {
            i++;
        }

        var low = arr[i - 1];
        var high = (i < arr.length) ? arr[i] : Number.POSITIVE_INFINITY;

        return ((high - value) <= (value - low)) ? high : low;

    },

    /**
    * Moves the element from the end of the array to the start, shifting all items in the process.
    * The "rotation" happens to the right.
    *
    * Before: `[ A, B, C, D, E, F ]`
    * After: `[ F, A, B, C, D, E ]`
    *
    * See also Phaser.ArrayUtils.rotateLeft.
    *
    * @method Phaser.ArrayUtils.rotateRight
    * @param {any[]} array - The array to rotate. The array is modified.
    * @return {any} The shifted value.
    */
    rotateRight: function (array) {

        var s = array.pop();
        array.unshift(s);

        return s;

    },

    /**
    * Moves the element from the start of the array to the end, shifting all items in the process.
    * The "rotation" happens to the left.
    *
    * Before: `[ A, B, C, D, E, F ]`
    * After: `[ B, C, D, E, F, A ]`
    *
    * See also Phaser.ArrayUtils.rotateRight
    *
    * @method Phaser.ArrayUtils.rotateLeft
    * @param {any[]} array - The array to rotate. The array is modified.
    * @return {any} The rotated value.
    */
    rotateLeft: function (array) {

        var s = array.shift();
        array.push(s);

        return s;

    },

    /**
    * Moves the element from the start of the array to the end, shifting all items in the process.
    * The "rotation" happens to the left.
    *
    * Before: `[ A, B, C, D, E, F ]`
    * After: `[ B, C, D, E, F, A ]`
    *
    * See also Phaser.ArrayUtils.rotateRight
    *
    * @method Phaser.ArrayUtils.rotate
    * @deprecated Please use Phaser.ArrayUtils.rotate instead.
    * @param {any[]} array - The array to rotate. The array is modified.
    * @return {any} The rotated value.
    */
    rotate: function (array) {

        var s = array.shift();
        array.push(s);

        return s;

    },

    /**
    * Create an array representing the inclusive range of numbers (usually integers) in `[start, end]` (or `[0, start]`, if `end` is omitted).
    * This is equivalent to `numberArrayStep(start, 1 + end, 1)`.
    *
    * When exactly one argument is passed, it's used as `end` and 0 is used as `start`. The length of the result is (1 + end).
    *
    * ##### Examples
    *
    * ```javascript
    * numberArray(3);    // -> [0, 1, 2, 3]
    * numberArray(0, 3); // -> [0, 1, 2, 3]
    * numberArray(1, 3); // -> [1, 2, 3]
    * ```
    *
    * @method Phaser.ArrayUtils.numberArray
    * @param {number} start - The minimum value the array starts with.
    * @param {number} [end] - The maximum value the array contains.
    * @return {number[]} The array of number values.
    */
    numberArray: function (start, end) {

        if (end === undefined || end === null)
        {
            end = start;
            start = 0;
        }

        var result = [];

        for (var i = start; i <= end; i++)
        {
            result.push(i);
        }

        return result;

    },

    /**
    * Create an array of numbers (positive and/or negative) progressing from `start`
    * up to but not including `end` by advancing by `step`.
    *
    * If `start` is less than `end` a zero-length range is created unless a negative `step` is specified.
    *
    * Certain values for `start` and `end` (eg. NaN/undefined/null) are currently coerced to 0;
    * for forward compatibility make sure to pass in actual numbers.
    *
    * @method Phaser.ArrayUtils.numberArrayStep
    * @param {number} start - The start of the range.
    * @param {number} [end] - The end of the range.
    * @param {number} [step=1] - The value to increment or decrement by.
    * @returns {Array} Returns the new array of numbers.
    * @example
    * Phaser.ArrayUtils.numberArrayStep(4);
    * // => [0, 1, 2, 3]
    *
    * Phaser.ArrayUtils.numberArrayStep(1, 5);
    * // => [1, 2, 3, 4]
    *
    * Phaser.ArrayUtils.numberArrayStep(0, 20, 5);
    * // => [0, 5, 10, 15]
    *
    * Phaser.ArrayUtils.numberArrayStep(0, -4, -1);
    * // => [0, -1, -2, -3]
    *
    * Phaser.ArrayUtils.numberArrayStep(1, 4, 0);
    * // => [1, 1, 1]
    *
    * Phaser.ArrayUtils.numberArrayStep(0);
    * // => []
    */
    numberArrayStep: function (start, end, step) {

        if (start === undefined || start === null) { start = 0; }

        if (end === undefined || end === null)
        {
            end = start;
            start = 0;
        }

        if (step === undefined) { step = 1; }

        var result = [];
        var total = Math.max(Phaser.Math.roundAwayFromZero((end - start) / (step || 1)), 0);

        for (var i = 0; i < total; i++)
        {
            result.push(start);
            start += step;
        }

        return result;

    }

};
<commit_msg>Correct deprecation notice<commit_after>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* Utility functions for dealing with Arrays.
*
* @class Phaser.ArrayUtils
* @static
*/
Phaser.ArrayUtils = {

    /**
    * Fetch a random entry from the given array.
    *
    * Will return null if there are no array items that fall within the specified range
    * or if there is no item for the randomly chosen index.
    *
    * @method Phaser.ArrayUtils.getRandomItem
    * @param {any[]} objects - An array of objects.
    * @param {integer} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array.
    * @param {integer} length - Optional restriction on the number of values you want to randomly select from.
    * @return {object} The random object that was selected.
    */
    getRandomItem: function (objects, startIndex, length) {

        if (objects === null) { return null; }
        if (startIndex === undefined) { startIndex = 0; }
        if (length === undefined) { length = objects.length; }

        var randomIndex = startIndex + Math.floor(Math.random() * length);

        return objects[randomIndex] === undefined ? null : objects[randomIndex];

    },

    /**
    * Removes a random object from the given array and returns it.
    *
    * Will return null if there are no array items that fall within the specified range
    * or if there is no item for the randomly chosen index.
    *
    * @method Phaser.ArrayUtils.removeRandomItem
    * @param {any[]} objects - An array of objects.
    * @param {integer} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array.
    * @param {integer} length - Optional restriction on the number of values you want to randomly select from.
    * @return {object} The random object that was removed.
    */
    removeRandomItem: function (objects, startIndex, length) {

        if (objects == null) { // undefined or null
            return null;
        }

        if (startIndex === undefined) { startIndex = 0; }
        if (length === undefined) { length = objects.length; }

        var randomIndex = startIndex + Math.floor(Math.random() * length);
        if (randomIndex < objects.length)
        {
            var removed = objects.splice(randomIndex, 1);
            return removed[0] === undefined ? null : removed[0];
        }
        else
        {
            return null;
        }

    },

    /**
    * Remove one or more items at the given index and reorder the array.
    *
    * The new array length will be `array.length - count`.
    *
    * This is an alternative to `array.splice(startIndex, count)`.
    *
    * @see https://github.com/mreinstein/remove-array-items
    * @see https://gamealchemist.wordpress.com/2013/05/01/lets-get-those-javascript-arrays-to-work-fast/
    *
    * @method Phaser.ArrayUtils.remove
    * @param {any[]} array
    * @param {integer} startIndex
    * @param {integer} [count=1]
    * @return {any[]} The modified array.
    */
    remove: function (array, startIndex, count)
    {
        var length = array.length;

        if (startIndex >= length || count === 0) { return; }

        if (count == null) { count = 1; }

        var newLength = length - count;

        for (var i = startIndex; i < newLength; ++i)
        {
            array[i] = array[i + count];
        }

        array.length = newLength;
    },

    /**
    * A standard Fisher-Yates Array shuffle implementation which modifies the array in place.
    *
    * @method Phaser.ArrayUtils.shuffle
    * @param {any[]} array - The array to shuffle.
    * @return {any[]} The original array, now shuffled.
    */
    shuffle: function (array) {

        for (var i = array.length - 1; i > 0; i--)
        {
            var j = Math.floor(Math.random() * (i + 1));
            var temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }

        return array;

    },

    /**
    * Transposes the elements of the given matrix (array of arrays).
    *
    * @method Phaser.ArrayUtils.transposeMatrix
    * @param {Array<any[]>} array - The matrix to transpose.
    * @return {Array<any[]>} A new transposed matrix
    */
    transposeMatrix: function (array) {

        var sourceRowCount = array.length;
        var sourceColCount = array[0].length;

        var result = new Array(sourceColCount);

        for (var i = 0; i < sourceColCount; i++)
        {
            result[i] = new Array(sourceRowCount);

            for (var j = sourceRowCount - 1; j > -1; j--)
            {
                result[i][j] = array[j][i];
            }
        }

        return result;

    },

    /**
    * Rotates the given matrix (array of arrays).
    *
    * Based on the routine from {@link http://jsfiddle.net/MrPolywhirl/NH42z/}.
    *
    * @method Phaser.ArrayUtils.rotateMatrix
    * @param {Array<any[]>} matrix - The array to rotate; this matrix _may_ be altered.
    * @param {number|string} direction - The amount to rotate: the rotation in degrees (90, -90, 270, -270, 180) or a string command ('rotateLeft', 'rotateRight' or 'rotate180').
    * @return {Array<any[]>} The rotated matrix. The source matrix should be discarded for the returned matrix.
    */
    rotateMatrix: function (matrix, direction) {

        if (typeof direction !== 'string')
        {
            direction = ((direction % 360) + 360) % 360;
        }

        if (direction === 90 || direction === -270 || direction === 'rotateLeft')
        {
            matrix = Phaser.ArrayUtils.transposeMatrix(matrix);
            matrix = matrix.reverse();
        }
        else if (direction === -90 || direction === 270 || direction === 'rotateRight')
        {
            matrix = matrix.reverse();
            matrix = Phaser.ArrayUtils.transposeMatrix(matrix);
        }
        else if (Math.abs(direction) === 180 || direction === 'rotate180')
        {
            for (var i = 0; i < matrix.length; i++)
            {
                matrix[i].reverse();
            }

            matrix = matrix.reverse();
        }

        return matrix;

    },

    /**
    * Snaps a value to the nearest value in a sorted numeric array.
    * The result will always be in the range `[first_value, last_value]`.
    *
    * @method Phaser.ArrayUtils.findClosest
    * @param {number} value - The search value
    * @param {number[]} arr - The input array which _must_ be sorted.
    * @return {number} The nearest value found.
    */
    findClosest: function (value, arr) {

        if (!arr.length)
        {
            return NaN;
        }
        else if (arr.length === 1 || value < arr[0])
        {
            return arr[0];
        }

        var i = 1;
        while (arr[i] < value) {
            i++;
        }

        var low = arr[i - 1];
        var high = (i < arr.length) ? arr[i] : Number.POSITIVE_INFINITY;

        return ((high - value) <= (value - low)) ? high : low;

    },

    /**
    * Moves the element from the end of the array to the start, shifting all items in the process.
    * The "rotation" happens to the right.
    *
    * Before: `[ A, B, C, D, E, F ]`
    * After: `[ F, A, B, C, D, E ]`
    *
    * See also Phaser.ArrayUtils.rotateLeft.
    *
    * @method Phaser.ArrayUtils.rotateRight
    * @param {any[]} array - The array to rotate. The array is modified.
    * @return {any} The shifted value.
    */
    rotateRight: function (array) {

        var s = array.pop();
        array.unshift(s);

        return s;

    },

    /**
    * Moves the element from the start of the array to the end, shifting all items in the process.
    * The "rotation" happens to the left.
    *
    * Before: `[ A, B, C, D, E, F ]`
    * After: `[ B, C, D, E, F, A ]`
    *
    * See also Phaser.ArrayUtils.rotateRight
    *
    * @method Phaser.ArrayUtils.rotateLeft
    * @param {any[]} array - The array to rotate. The array is modified.
    * @return {any} The rotated value.
    */
    rotateLeft: function (array) {

        var s = array.shift();
        array.push(s);

        return s;

    },

    /**
    * Moves the element from the start of the array to the end, shifting all items in the process.
    * The "rotation" happens to the left.
    *
    * Before: `[ A, B, C, D, E, F ]`
    * After: `[ B, C, D, E, F, A ]`
    *
    * See also Phaser.ArrayUtils.rotateRight
    *
    * @method Phaser.ArrayUtils.rotate
    * @deprecated Please use Phaser.ArrayUtils.rotateLeft instead.
    * @param {any[]} array - The array to rotate. The array is modified.
    * @return {any} The rotated value.
    */
    rotate: function (array) {

        var s = array.shift();
        array.push(s);

        return s;

    },

    /**
    * Create an array representing the inclusive range of numbers (usually integers) in `[start, end]` (or `[0, start]`, if `end` is omitted).
    * This is equivalent to `numberArrayStep(start, 1 + end, 1)`.
    *
    * When exactly one argument is passed, it's used as `end` and 0 is used as `start`. The length of the result is (1 + end).
    *
    * ##### Examples
    *
    * ```javascript
    * numberArray(3);    // -> [0, 1, 2, 3]
    * numberArray(0, 3); // -> [0, 1, 2, 3]
    * numberArray(1, 3); // -> [1, 2, 3]
    * ```
    *
    * @method Phaser.ArrayUtils.numberArray
    * @param {number} start - The minimum value the array starts with.
    * @param {number} [end] - The maximum value the array contains.
    * @return {number[]} The array of number values.
    */
    numberArray: function (start, end) {

        if (end === undefined || end === null)
        {
            end = start;
            start = 0;
        }

        var result = [];

        for (var i = start; i <= end; i++)
        {
            result.push(i);
        }

        return result;

    },

    /**
    * Create an array of numbers (positive and/or negative) progressing from `start`
    * up to but not including `end` by advancing by `step`.
    *
    * If `start` is less than `end` a zero-length range is created unless a negative `step` is specified.
    *
    * Certain values for `start` and `end` (eg. NaN/undefined/null) are currently coerced to 0;
    * for forward compatibility make sure to pass in actual numbers.
    *
    * @method Phaser.ArrayUtils.numberArrayStep
    * @param {number} start - The start of the range.
    * @param {number} [end] - The end of the range.
    * @param {number} [step=1] - The value to increment or decrement by.
    * @returns {Array} Returns the new array of numbers.
    * @example
    * Phaser.ArrayUtils.numberArrayStep(4);
    * // => [0, 1, 2, 3]
    *
    * Phaser.ArrayUtils.numberArrayStep(1, 5);
    * // => [1, 2, 3, 4]
    *
    * Phaser.ArrayUtils.numberArrayStep(0, 20, 5);
    * // => [0, 5, 10, 15]
    *
    * Phaser.ArrayUtils.numberArrayStep(0, -4, -1);
    * // => [0, -1, -2, -3]
    *
    * Phaser.ArrayUtils.numberArrayStep(1, 4, 0);
    * // => [1, 1, 1]
    *
    * Phaser.ArrayUtils.numberArrayStep(0);
    * // => []
    */
    numberArrayStep: function (start, end, step) {

        if (start === undefined || start === null) { start = 0; }

        if (end === undefined || end === null)
        {
            end = start;
            start = 0;
        }

        if (step === undefined) { step = 1; }

        var result = [];
        var total = Math.max(Phaser.Math.roundAwayFromZero((end - start) / (step || 1)), 0);

        for (var i = 0; i < total; i++)
        {
            result.push(start);
            start += step;
        }

        return result;

    }

};
<|endoftext|>