<commit_before>"use strict";

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange')
const { ExchangeError } = require ('./base/errors')

//  ---------------------------------------------------------------------------

module.exports = class coinmarketcap extends Exchange {

    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'coinmarketcap',
            'name': 'CoinMarketCap',
            'rateLimit': 10000,
            'version': 'v1',
            'countries': 'US',
            'hasCORS': true,
            'hasPrivateAPI': false,
            'hasCreateOrder': false,
            'hasCancelOrder': false,
            'hasFetchBalance': false,
            'hasFetchOrderBook': false,
            'hasFetchTrades': false,
            'hasFetchTickers': true,
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/28244244-9be6312a-69ed-11e7-99c1-7c1797275265.jpg',
                'api': 'https://api.coinmarketcap.com',
                'www': 'https://coinmarketcap.com',
                'doc': 'https://coinmarketcap.com/api',
            },
            'requiredCredentials': {
                'apiKey': false,
                'secret': false,
            },
            'api': {
                'public': {
                    'get': [
                        'ticker/',
                        'ticker/{id}/',
                        'global/',
                    ],
                },
            },
            'currencies': [
                'AUD',
                'BRL',
                'CAD',
                'CHF',
                'CNY',
                'EUR',
                'GBP',
                'HKD',
                'IDR',
                'INR',
                'JPY',
                'KRW',
                'MXN',
                'RUB',
                'USD',
            ],
        });
    }

    async fetchOrderBook (symbol, params = {}) {
        throw new ExchangeError ('Fetching order books is not supported by the API of ' + this.id);
    }

    async fetchMarkets () {
        let markets = await this.publicGetTicker ();
        let result = [];
        for (let p = 0; p < markets.length; p++) {
            let market = markets[p];
            for (let c = 0; c < this.currencies.length; c++) {
                let base = market['symbol'];
                let baseId = market['id'];
                let quote = this.currencies[c];
                let quoteId = quote.toLowerCase ();
                let symbol = base + '/' + quote;
                let id = baseId + '/' + quote;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': market,
                });
            }
        }
        return result;
    }

    async fetchGlobal (currency = 'USD') {
        await this.loadMarkets ();
        let request = {};
        if (currency)
            request['convert'] = currency;
        return await this.publicGetGlobal (request);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.milliseconds ();
        if ('last_updated' in ticker)
            if (ticker['last_updated'])
                timestamp = parseInt (ticker['last_updated']) * 1000;
        let change = undefined;
        let changeKey = 'percent_change_24h';
        if (changeKey in ticker)
            change = parseFloat (ticker[changeKey]);
        let last = undefined;
        let symbol = undefined;
        let volume = undefined;
        if (market) {
            let price = 'price_' + market['quoteId'];
            if (price in ticker)
                if (ticker[price])
                    last = parseFloat (ticker[price]);
            symbol = market['symbol'];
            let volumeKey = '24h_volume_' + market['quoteId'];
            if (volumeKey in ticker)
                volume = parseFloat (ticker[volumeKey]);
        }
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': undefined,
            'low': undefined,
            'bid': undefined,
            'ask': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': last,
            'change': change,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': undefined,
            'quoteVolume': volume,
            'info': ticker,
        };
    }

    async fetchTickers (currency = 'USD', params = {}) {
        await this.loadMarkets ();
        let request = {};
        if (currency)
            request['convert'] = currency;
        let response = await this.publicGetTicker (this.extend (request, params));
        let tickers = {};
        for (let t = 0; t < response.length; t++) {
            let ticker = response[t];
            let id = ticker['id'] + '/' + currency;
            let symbol = id;
            let market = undefined;
            if (id in this.markets_by_id) {
                market = this.markets_by_id[id];
                symbol = market['symbol'];
            }
            tickers[symbol] = this.parseTicker (ticker, market);
        }
        return tickers;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = this.extend ({
            'convert': market['quote'],
            'id': market['baseId'],
        }, params);
        let response = await this.publicGetTickerId (request);
        let ticker = response[0];
        return this.parseTicker (ticker, market);
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (Object.keys (query).length)
            url += '?' + this.urlencode (query);
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('error' in response) {
            if (response['error']) {
                throw new ExchangeError (this.id + ' ' + this.json (response));
            }
        }
        return response;
    }
}
<commit_msg>coinmarketcap now returns all tickers<commit_after>"use strict";

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange')
const { ExchangeError } = require ('./base/errors')

//  ---------------------------------------------------------------------------

module.exports = class coinmarketcap extends Exchange {

    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'coinmarketcap',
            'name': 'CoinMarketCap',
            'rateLimit': 10000,
            'version': 'v1',
            'countries': 'US',
            'hasCORS': true,
            'hasPrivateAPI': false,
            'hasCreateOrder': false,
            'hasCancelOrder': false,
            'hasFetchBalance': false,
            'hasFetchOrderBook': false,
            'hasFetchTrades': false,
            'hasFetchTickers': true,
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/28244244-9be6312a-69ed-11e7-99c1-7c1797275265.jpg',
                'api': 'https://api.coinmarketcap.com',
                'www': 'https://coinmarketcap.com',
                'doc': 'https://coinmarketcap.com/api',
            },
            'requiredCredentials': {
                'apiKey': false,
                'secret': false,
            },
            'api': {
                'public': {
                    'get': [
                        'ticker/',
                        'ticker/{id}/',
                        'global/',
                    ],
                },
            },
            'currencies': [
                'AUD',
                'BRL',
                'CAD',
                'CHF',
                'CNY',
                'EUR',
                'GBP',
                'HKD',
                'IDR',
                'INR',
                'JPY',
                'KRW',
                'MXN',
                'RUB',
                'USD',
            ],
        });
    }

    async fetchOrderBook (symbol, params = {}) {
        throw new ExchangeError ('Fetching order books is not supported by the API of ' + this.id);
    }

    async fetchMarkets () {
        let markets = await this.publicGetTicker ();
        let result = [];
        for (let p = 0; p < markets.length; p++) {
            let market = markets[p];
            for (let c = 0; c < this.currencies.length; c++) {
                let base = market['symbol'];
                let baseId = market['id'];
                let quote = this.currencies[c];
                let quoteId = quote.toLowerCase ();
                let symbol = base + '/' + quote;
                let id = baseId + '/' + quote;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': market,
                });
            }
        }
        return result;
    }

    async fetchGlobal (currency = 'USD') {
        await this.loadMarkets ();
        let request = {};
        if (currency)
            request['convert'] = currency;
        return await this.publicGetGlobal (request);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.milliseconds ();
        if ('last_updated' in ticker)
            if (ticker['last_updated'])
                timestamp = parseInt (ticker['last_updated']) * 1000;
        let change = undefined;
        let changeKey = 'percent_change_24h';
        if (changeKey in ticker)
            change = parseFloat (ticker[changeKey]);
        let last = undefined;
        let symbol = undefined;
        let volume = undefined;
        if (market) {
            let price = 'price_' + market['quoteId'];
            if (price in ticker)
                if (ticker[price])
                    last = parseFloat (ticker[price]);
            symbol = market['symbol'];
            let volumeKey = '24h_volume_' + market['quoteId'];
            if (volumeKey in ticker)
                volume = parseFloat (ticker[volumeKey]);
        }
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': undefined,
            'low': undefined,
            'bid': undefined,
            'ask': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': last,
            'change': change,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': undefined,
            'quoteVolume': volume,
            'info': ticker,
        };
    }

    async fetchTickers (currency = 'USD', params = {}) {
        await this.loadMarkets ();
        let request = {
            'limit': 10000,
        };
        if (currency)
            request['convert'] = currency;
        let response = await this.publicGetTicker (this.extend (request, params));
        let tickers = {};
        for (let t = 0; t < response.length; t++) {
            let ticker = response[t];
            let id = ticker['id'] + '/' + currency;
            let symbol = id;
            let market = undefined;
            if (id in this.markets_by_id) {
                market = this.markets_by_id[id];
                symbol = market['symbol'];
            }
            tickers[symbol] = this.parseTicker (ticker, market);
        }
        return tickers;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = this.extend ({
            'convert': market['quote'],
            'id': market['baseId'],
        }, params);
        let response = await this.publicGetTickerId (request);
        let ticker = response[0];
        return this.parseTicker (ticker, market);
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (Object.keys (query).length)
            url += '?' + this.urlencode (query);
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('error' in response) {
            if (response['error']) {
                throw new ExchangeError (this.id + ' ' + this.json (response));
            }
        }
        return response;
    }
}
<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict'

let firewalla = require('./Firewalla.js');

var spawn = require('child_process').spawn;

let spawnProcess = null;

let spoofLogFile = firewalla.getLogFolder() + "/spoof.log";

let SysManager = require("./SysManager.js");
let sysManager = new SysManager();

let Promise = require('bluebird');

let redis = require('redis');
let rclient = redis.createClient();

let log = require("./logger.js")(__filename, 'info');

let fs = require('fs');

let cp = require('child_process');

let monitoredKey = "monitored_hosts";
let unmonitoredKey = "unmonitored_hosts";

let spoofStarted = false;

// add promises to all redis functions
Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);

function getBinary() {
  if(firewalla.getPlatform() === "x86_64") {
    return firewalla.getFirewallaHome() + "/bin/real.x86_64/bitbridge7";
  }

  return firewalla.getFirewallaHome() + "/bin/bitbridge7";
}

// WORKAROUND VERSION HERE, will move to a better place
function startSpoofing() {

  if(spoofStarted) {
    return Promise.resolve();
  }

  // clean up redis key
  log.info("startSpoofing is called");

  return rclient.delAsync(monitoredKey)
    .then(() => rclient.delAsync(unmonitoredKey))
    .then(() => {
      let ifName = sysManager.monitoringInterface().name;
      let routerIP = sysManager.myGateway();
      let myIP = sysManager.myIp();

      if(!ifName || !myIP || !routerIP) {
        return Promise.reject("require valid interface name, ip address and gateway ip address");
      }

      if (firewalla.isProduction()) {
          spoofLogFile="/dev/null";
      }

      let logStream = fs.createWriteStream(spoofLogFile, {flags: 'a'});
      let binary = null, args = null;

      if(firewalla.isDocker() || firewalla.isTravis()) {
        binary = "sudo";
        args = [getBinary(), ifName, routerIP, myIP,'-m','-q','-n'];
      } else {
        binary = getBinary();
        args = [ifName, routerIP, myIP,'-m','-q','-n'];
      }
      spawnProcess = spawn(binary, args);
      log.info("starting new spoofing: ", binary, args);

      spawnProcess.stdout.pipe(logStream);
      spawnProcess.stderr.pipe(logStream);

      spawnProcess.on('exit', (code) => {
        log.info("spoofing binary exited with code " + code);
      });

      spoofStarted = true;
      return Promise.resolve();

    });

}

function stopSpoofing() {
  return new Promise((resolve, reject) => {
    spoofStarted = false;

    // if(spawnProcess) {
    //   spawnProcess.kill();
    // }
    cp.exec("sudo pkill bitbridge7", (err) => {
      // ignore err, since bitbridge7 may not exist
      resolve();
    })
  }).catch((err) => {
    //catch everything here
  })
}

module.exports = {
  startSpoofing: startSpoofing,
  stopSpoofing: stopSpoofing
}
<commit_msg>magic {} to output array<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict'

let firewalla = require('./Firewalla.js');

var spawn = require('child_process').spawn;

let spawnProcess = null;

let spoofLogFile = firewalla.getLogFolder() + "/spoof.log";

let SysManager = require("./SysManager.js");
let sysManager = new SysManager();

let Promise = require('bluebird');

let redis = require('redis');
let rclient = redis.createClient();

let log = require("./logger.js")(__filename, 'info');

let fs = require('fs');

let cp = require('child_process');

let monitoredKey = "monitored_hosts";
let unmonitoredKey = "unmonitored_hosts";

let spoofStarted = false;

// add promises to all redis functions
Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);

function getBinary() {
  if(firewalla.getPlatform() === "x86_64") {
    return firewalla.getFirewallaHome() + "/bin/real.x86_64/bitbridge7";
  }

  return firewalla.getFirewallaHome() + "/bin/bitbridge7";
}

// WORKAROUND VERSION HERE, will move to a better place
function startSpoofing() {

  if(spoofStarted) {
    return Promise.resolve();
  }

  // clean up redis key
  log.info("startSpoofing is called");

  return rclient.delAsync(monitoredKey)
    .then(() => rclient.delAsync(unmonitoredKey))
    .then(() => {
      let ifName = sysManager.monitoringInterface().name;
      let routerIP = sysManager.myGateway();
      let myIP = sysManager.myIp();

      if(!ifName || !myIP || !routerIP) {
        return Promise.reject("require valid interface name, ip address and gateway ip address");
      }

      if (firewalla.isProduction()) {
          spoofLogFile="/dev/null";
      }

      let logStream = fs.createWriteStream(spoofLogFile, {flags: 'a'});
      let binary = null, args = null;

      if(firewalla.isDocker() || firewalla.isTravis()) {
        binary = "sudo";
        args = [getBinary(), ifName, routerIP, myIP,'-m','-q','-n'];
      } else {
        binary = getBinary();
        args = [ifName, routerIP, myIP,'-m','-q','-n'];
      }
      spawnProcess = spawn(binary, args);
      log.info("starting new spoofing: ", binary, args, {});

      spawnProcess.stdout.pipe(logStream);
      spawnProcess.stderr.pipe(logStream);

      spawnProcess.on('exit', (code) => {
        log.info("spoofing binary exited with code " + code);
      });

      spoofStarted = true;
      return Promise.resolve();

    });

}

function stopSpoofing() {
  return new Promise((resolve, reject) => {
    spoofStarted = false;

    // if(spawnProcess) {
    //   spawnProcess.kill();
    // }
    cp.exec("sudo pkill bitbridge7", (err) => {
      // ignore err, since bitbridge7 may not exist
      resolve();
    })
  }).catch((err) => {
    //catch everything here
  })
}

module.exports = {
  startSpoofing: startSpoofing,
  stopSpoofing: stopSpoofing
}
<|endoftext|>


<commit_before>/*
 * timer.jquery
 * https://github.com/walmik/timer.jquery
 *
 * Copyright (c) 2013 Walmik Deshpande
 * Licensed under the MIT license.
 */

 /*
 Usage:
 $("#timerDiv").timer('start');
 $("#timerDiv").timer('start', {seconds: 100}); //provide 100 seconds to start from
 $("#timerDiv").timer('pause');
 $("#timerDiv").timer('reset');
 */

;(function($)
{
    var secs = 0,
        mins = 0,
        hrs = 0,
        secsStr = "00",
        minsStr = "00",
        hrsStr = "00",
        timerId = null,
        delay = 1000,
        type = "string",
        isTimerRunning = false;

    $.fn.timer = function(method, options)
    {
        var element = this;
        var settings = $.extend({showHours: false}, options);

        if (settings.seconds !== undefined)
        {
            hrs = Math.floor(settings.seconds / 3600);
            mins = Math.floor((settings.seconds - (hrs * 3600))/60);
            secs = settings.seconds - (hrs * 3600) - (mins * 60);

            timeToString();
        }

        switch(method)
        {
            case "start":
                if(!isTimerRunning) startTimer();
                break;

            case "pause":
                pauseTimer();
                break;

            case "resume":
                if(!isTimerRunning) startTimerInterval();
                break;

            case "reset":
                secs = 0;
                mins = 0;
                hrs = 0;
                break;

            case "get_seconds":
                return ( (hrs*3600) + (mins*60) + secs - 1 );
                break;
        }

        function pauseTimer()
        {
            clearInterval(timerId);
            isTimerRunning = false;
        }

        function startTimer()
        {
            updateTimerDisplay();
            incrementTime(); //to avoid the 1 second gap that gets created if the seconds are not incremented
            startTimerInterval();
        }

        function startTimerInterval()
        {
            timerId = setInterval(incrementTime, delay);
            isTimerRunning = true;
        }

        function updateTimerDisplay()
        {
            if(hrs > 0) settings.showHours = true;
            if(settings.showHours) $(element).html(hrsStr + ":" + minsStr + ":" + secsStr);
            else $(element).html(minsStr + ":" + secsStr);
        }

        function timeToString()
        {
            if(secs < 10) secsStr = "0" + secs;
            else secsStr = secs;

            if(mins < 10) minsStr = "0" + mins;
            else minsStr = mins;

            if(hrs < 10) hrsStr = "0" + hrs;
            else hrsStr = hrs;
        }

        function incrementTime()
        {
            timeToString();
            updateTimerDisplay();

            secs++;
            if(secs % 60 == 0)
            {
                mins++;
                secs = 0;
            }

            //handle time exceeding 60 mins!
            if(mins > 59 && mins % 60 == 0)
            {
                hrs++;
                mins = 0;
            }
        }

    }
})(jQuery);
<commit_msg>refactored timeToString function<commit_after>/*
 * timer.jquery
 * https://github.com/walmik/timer.jquery
 *
 * Copyright (c) 2013 Walmik Deshpande
 * Licensed under the MIT license.
 */

 /*
 Usage:
 $("#timerDiv").timer('start');
 $("#timerDiv").timer('start', {seconds: 100}); //provide 100 seconds to start from
 $("#timerDiv").timer('pause');
 $("#timerDiv").timer('reset');
 */

;(function($)
{
    var secsNum = 0,
        minsNum = 0,
        hrsNum = 0,
        secsStr = "00",
        minsStr = "00",
        hrsStr = "00",
        timerId = null,
        delay = 1000,
        isTimerRunning = false;

    /*
        @method STRING = start,pause,resume
        @options OBJECT = { seconds NUMBER: 2000 }
    */
    $.fn.timer = function(method, options)
    {
        var element = this,
            settings = $.extend({ showHours: false }, options );

        if (settings.seconds !== undefined)
        {
            hrsNum = Math.floor(settings.seconds / 3600);
            minsNum = Math.floor((settings.seconds - (hrsNum * 3600))/60);
            secsNum = settings.seconds - (hrsNum * 3600) - (minsNum * 60);

            timeToString();
        }

        switch(method)
        {
            case "start":
                if(!isTimerRunning) startTimer();
                break;

            case "pause":
                pauseTimer();
                break;

            case "resume":
                if(!isTimerRunning) startTimerInterval();
                break;

            case "reset":
                secsNum = 0;
                minsNum = 0;
                hrsNum = 0;
                break;

            case "get_seconds":
                return ( (hrsNum*3600) + (minsNum*60) + secsNum - 1 );
                break;
        }

        function pauseTimer()
        {
            clearInterval(timerId);
            isTimerRunning = false;
        }

        function startTimer()
        {
            updateTimerDisplay();
            incrementTime(); //to avoid the 1 second gap that gets created if the seconds are not incremented
            startTimerInterval();
        }

        function startTimerInterval()
        {
            timerId = setInterval(incrementTime, delay);
            isTimerRunning = true;
        }

        function updateTimerDisplay()
        {
            if(hrsNum > 0) settings.showHours = true;
            if(settings.showHours) $(element).html(hrsStr + ":" + minsStr + ":" + secsStr);
            else $(element).html(minsStr + ":" + secsStr);
        }

        function timeToString()
        {
            secsStr = secsNum < 10 ? "0" + secsNum : secsNum;
            minsStr = minsNum < 10 ? "0" + minsNum : minsNum;
            hrsStr = hrsNum < 10 ? "0" + hrsNum : hrsNum;
        }

        function incrementTime()
        {
            timeToString();
            updateTimerDisplay();

            secsNum++;
            if(secsNum % 60 == 0)
            {
                minsNum++;
                secsNum = 0;
            }

            //handle time exceeding 60 minsNum!
            if(minsNum > 59 && minsNum % 60 == 0)
            {
                hrsNum++;
                minsNum = 0;
            }
        }

    }
})(jQuery);
<|endoftext|>


<commit_before>import {Emitter} from 'event-kit';

import React from 'react';
import {mount} from 'enzyme';

import ObserveModelDecorator from '../../lib/decorators/observe-model';

class TestModel {
  constructor(data) {
    this.emitter = new Emitter();
    this.data = data;
  }

  update(data) {
    this.data = data;
    this.didUpdate();
  }

  getData() {
    return Promise.resolve(this.data);
  }

  didUpdate() {
    return this.emitter.emit('did-update');
  }

  onDidUpdate(cb) {
    return this.emitter.on('did-update', cb);
  }
}

@ObserveModelDecorator({
  getModel: props => props.testModel,
  fetchData: model => model.getData(),
})
class TestComponent extends React.Component {
  static testMethod() {
    return 'Hi!';
  }

  render() {
    return null;
  }
}

describe('ObserveModelDecorator', function() {
  it('wraps a component, re-rendering with specified props when it changes', async function() {
    const model = new TestModel({one: 1, two: 2});
    const app = <TestComponent testModel={model} />;
    const wrapper = mount(app);

    assert.isTrue(wrapper.is('ObserveModelDecorator(TestComponent)'));

    await assert.async.equal(wrapper.find('TestComponent').prop('one'), 1);
    await assert.async.equal(wrapper.find('TestComponent').prop('two'), 2);
    await assert.async.equal(wrapper.find('TestComponent').prop('testModel'), model);

    model.update({one: 'one', two: 'two'});
    await assert.async.equal(wrapper.find('TestComponent').prop('one'), 'one');
    await assert.async.equal(wrapper.find('TestComponent').prop('two'), 'two');
    await assert.async.equal(wrapper.find('TestComponent').prop('testModel'), model);

    wrapper.setProps({testModel: null});
    await assert.async.equal(wrapper.find('TestComponent').prop('one'), undefined);
    await assert.async.equal(wrapper.find('TestComponent').prop('two'), undefined);
    await assert.async.isNull(wrapper.find('TestComponent').prop('testModel'));

    const model2 = new TestModel({one: 1, two: 2});
    wrapper.setProps({testModel: model2});
    await assert.async.equal(wrapper.find('TestComponent').prop('one'), 1);
    await assert.async.equal(wrapper.find('TestComponent').prop('two'), 2);
    await assert.async.equal(wrapper.find('TestComponent').prop('testModel'), model2);
  });

  it('hosts static methods', function() {
    assert.equal(TestComponent.testMethod(), 'Hi!');
  });
});
<commit_msg>ObserveModelDecorator :white_check_mark:<commit_after>import {Emitter} from 'event-kit';

import React from 'react';
import {mount} from 'enzyme';

import ObserveModelDecorator from '../../lib/decorators/observe-model';

class TestModel {
  constructor(data) {
    this.emitter = new Emitter();
    this.data = data;
  }

  update(data) {
    this.data = data;
    this.didUpdate();
  }

  getData() {
    return Promise.resolve(this.data);
  }

  didUpdate() {
    return this.emitter.emit('did-update');
  }

  onDidUpdate(cb) {
    return this.emitter.on('did-update', cb);
  }
}

@ObserveModelDecorator({
  getModel: props => props.testModel,
  fetchData: model => model.getData(),
})
class TestComponent extends React.Component {
  static testMethod() {
    return 'Hi!';
  }

  render() {
    return null;
  }
}

describe('ObserveModelDecorator', function() {
  it('wraps a component, re-rendering with specified props when it changes', async function() {
    const model = new TestModel({one: 1, two: 2});
    const app = <TestComponent testModel={model} />;
    const wrapper = mount(app);

    assert.isTrue(wrapper.is('ObserveModelDecorator(TestComponent)'));

    await assert.async.equal(wrapper.update().find('TestComponent').prop('one'), 1);
    await assert.async.equal(wrapper.update().find('TestComponent').prop('two'), 2);
    await assert.async.equal(wrapper.update().find('TestComponent').prop('testModel'), model);

    model.update({one: 'one', two: 'two'});
    await assert.async.equal(wrapper.update().find('TestComponent').prop('one'), 'one');
    await assert.async.equal(wrapper.update().find('TestComponent').prop('two'), 'two');
    await assert.async.equal(wrapper.update().find('TestComponent').prop('testModel'), model);

    wrapper.setProps({testModel: null});
    await assert.async.equal(wrapper.update().find('TestComponent').prop('one'), undefined);
    await assert.async.equal(wrapper.update().find('TestComponent').prop('two'), undefined);
    await assert.async.isNull(wrapper.update().find('TestComponent').prop('testModel'));

    const model2 = new TestModel({one: 1, two: 2});
    wrapper.setProps({testModel: model2});
    await assert.async.equal(wrapper.update().find('TestComponent').prop('one'), 1);
    await assert.async.equal(wrapper.update().find('TestComponent').prop('two'), 2);
    await assert.async.equal(wrapper.update().find('TestComponent').prop('testModel'), model2);
  });

  it('hosts static methods', function() {
    assert.equal(TestComponent.testMethod(), 'Hi!');
  });
});
<|endoftext|>


<commit_before>function replaceSelection(replaceText) {
	var input = $(document.activeElement);
	if ((input.is('input') && input.attr('type') == 'text') || input.is('textarea')) {
		//input.val(input.val().substring(0, input.selectionStart) + replaceText + input.val().substring(input.selectionEnd));
		input.selection('replace', {text: replaceText});
	}
}
if(window.location.href.substring(0, 30) == "http://scratch.mit.edu/discuss") {
//$(".markItUpContainer .markItUpHeader > ul").append('
---------------

TextTools - WIP

Rainbow - BBCode

Flipped

Reversed

');
//$(".markItUpTextTools > a").css("background-image", "url(" + chrome.extension.getURL("icon_16.png") + ")");
//$(".markItUpRainbow").css("background-image", "url(" + chrome.extension.getURL("icon_rainbow.png") + ")");
//$(".markItUpFlip").css("background-image", "url(" + chrome.extension.getURL("icon_flip.png") + ")");
//$(".markItUpReverse").css("background-image", "url(" + chrome.extension.getURL("icon_reverse.png") + ")");
//Uncomment above to add button to scratch forum post editor. WIP - will add more later
}
chrome.runtime.onMessage.addListener(
	function(request, sender, sendResponse) {
		switch (request.textTool) {
			case 'reverse':
				replaceSelection(reverse($(document.activeElement).selection()));
				break;
			case 'flip':
				replaceSelection(flip($(document.activeElement).selection()));
				break;
			case 'rainbowBBCode':
				replaceSelection(rainbowBBCode($(document.activeElement).selection()));
				break;
			case 'rainbowHTML':
				replaceSelection(rainbowHTML($(document.activeElement).selection()));
				break;
		}
	});
<commit_msg>add programming when clicked - buggy and WIP<commit_after>function replaceSelection(replaceText) {
	var input = $(document.activeElement);
	if ((input.is('input') && input.attr('type') == 'text') || input.is('textarea')) {
		//input.val(input.val().substring(0, input.selectionStart) + replaceText + input.val().substring(input.selectionEnd));
		input.selection('replace', {text: replaceText});
	}
}
function replaceScratch(replaceText) {
	var input = $(document.forms['post']['body']);
	if ((input.is('input') && input.attr('type') == 'text') || input.is('textarea')) {
		//input.val(input.val().substring(0, input.selectionStart) + replaceText + input.val().substring(input.selectionEnd));
		input.selection('replace', {text: replaceText});
	}
}
if(window.location.href.substring(0, 30) == "http://scratch.mit.edu/discuss") {
//$(".markItUpContainer .markItUpHeader > ul").append('
---------------

TextTools - WIP

Rainbow - BBCode

Flipped

Reversed

');
//$(".markItUpTextTools > a").css("background-image", "url(" + chrome.extension.getURL("icon_16.png") + ")");
//$(".markItUpRainbow").css("background-image", "url(" + chrome.extension.getURL("icon_rainbow.png") + ")");
//$(".markItUpFlip").css("background-image", "url(" + chrome.extension.getURL("icon_flip.png") + ")");
//$(".markItUpReverse").css("background-image", "url(" + chrome.extension.getURL("icon_reverse.png") + ")");
//$(".markItUpTextTools > a").click(function(e) {
//    e.preventDefault();
//});
//$(".markItUpRainbow").click(function(e) {
//    e.preventDefault();
//    var textarea = document.forms['post']['body'];
//    replaceScratch(rainbowBBCode($(textarea).selection()));
//});
//$(".markItUpFlip").click();
//$(".markItUpReverse").click();
//Uncomment above to add button to scratch forum post editor. WIP - will add more later
}
chrome.runtime.onMessage.addListener(
	function(request, sender, sendResponse) {
		switch (request.textTool) {
			case 'reverse':
				replaceSelection(reverse($(document.activeElement).selection()));
				break;
			case 'flip':
				replaceSelection(flip($(document.activeElement).selection()));
				break;
			case 'rainbowBBCode':
				replaceSelection(rainbowBBCode($(document.activeElement).selection()));
				break;
			case 'rainbowHTML':
				replaceSelection(rainbowHTML($(document.activeElement).selection()));
				break;
		}
	});
<|endoftext|>


<commit_before>/*
 Copyright 2014 Google Inc. 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.
 */

/*
 * For more information on this file, see http://www.cookiechoices.org/
 */

(function(window) {

  if (!!window.cookieChoices) {
    return window.cookieChoices;
  }

  var document = window.document;
  // IE8 does not support textContent, so we should fallback to innerText.
  var supportsTextContent = 'textContent' in document.body;

  var cookieChoices = (function() {

    var cookieName = 'displayCookieConsent';
    var cookieConsentId = 'cookieChoiceInfo';
    var divClass = 'cookie-choices-info';
    var innerDivClass = 'cookie-choices-inner';
    var textSpan = 'cookie-choices-text';
    var buttonsClass = 'cookie-choices-buttons';
    var buttonClass = 'cookie-choices-button';
    var singletonClass = 'singleton-element';
    var dismissLinkId = 'cookieChoiceDismiss';

    function _createHeaderElement(cookieText, dismissText, linkText, linkHref) {
      var cookieInnerElement = document.createElement('div');
      cookieInnerElement.className = innerDivClass;
      cookieInnerElement.appendChild(_createConsentText(cookieText));

      var buttonsElement = document.createElement('span');
      buttonsElement.className = buttonsClass;
      cookieInnerElement.appendChild(buttonsElement);

      if (!!linkText && !!linkHref) {
        buttonsElement.appendChild(_createInformationLink(linkText, linkHref));
      }

      buttonsElement.appendChild(_createDismissLink(dismissText));

      var cookieConsentElement = document.createElement('div');
      cookieConsentElement.id = cookieConsentId;
      cookieConsentElement.className = divClass + ' ' + singletonClass;
      cookieConsentElement.appendChild(cookieInnerElement);
      return cookieConsentElement;
    }

    function _createStyleElement() {
      var style = document.createElement('style');
      style.className = singletonClass;
      style.type = 'text/css';
      _setElementText(style,
          '.' + divClass + ' { ' +
              'position:fixed;width:100%;background-color:#666;margin:0;' +
              'left:0;bottom:0;padding:0;z-index:4000;text-align:center;' +
              'color:#fff;line-height:140%;' +
              'font-family:roboto,Arial; } ' +
          '.' + divClass + ' .' + innerDivClass + ' { ' +
              'position:relative;width:initial;margin:0;left:0;bottom:0; } ' +
          '.' + divClass + ' .' + textSpan + ' { ' +
              'display:inline-block;vertical-align:middle;font-size:16px;' +
              'margin:2px 20px;color:#ccc;max-width:800px;' +
              'text-align:left; }' +
          '.' + divClass + ' .' + buttonsClass + ' { ' +
              'display:inline-block;vertical-align:middle;' +
              'white-space:nowrap;margin:0 10px; } ' +
          '.' + divClass + ' .' + buttonClass + ':hover { ' +
              ' color: #fff; } ' +
          '.' + divClass + ' .' + buttonClass + ' { ' +
              'text-transform:UPPERCASE;' +
              'white-space:nowrap;' +
              'color:#eee;margin-left:8px;padding:0 6px; ' +
              'text-decoration:none; }');
      document.getElementsByTagName('head')[0].appendChild(style);
    }

    function _setElementText(element, text) {
      if (supportsTextContent) {
        element.textContent = text;
      } else {
        element.innerText = text;
      }
    }

    function _createConsentText(cookieText) {
      var consentText = document.createElement('span');
      _setElementText(consentText, cookieText);
      consentText.className = textSpan;
      return consentText;
    }

    function _createDismissLink(dismissText) {
      var dismissLink = document.createElement('a');
      _setElementText(dismissLink, dismissText);
      dismissLink.id = dismissLinkId;
      dismissLink.href = '#';
      dismissLink.className = buttonClass;
      return dismissLink;
    }

    function _createInformationLink(linkText, linkHref) {
      var infoLink = document.createElement('a');
      _setElementText(infoLink, linkText);
      infoLink.href = linkHref;
      infoLink.target = '_blank';
      infoLink.className = buttonClass;
      return infoLink;
    }

    function _dismissLinkClick(e) {
      _saveUserPreference();
      _removeCookieConsent();
      e.stopPropagation && e.stopPropagation();
      e.cancelBubble = true;
      return false;
    }

    function _showCookieConsent(cookieText, dismissText, linkText, linkHref) {
      if (_shouldDisplayConsent()) {
        _removeCookieConsent();
        _createStyleElement();
        var consentElement =
            _createHeaderElement(cookieText, dismissText, linkText, linkHref);
        var fragment = document.createDocumentFragment();
        fragment.appendChild(consentElement);
        document.body.appendChild(fragment.cloneNode(true));
        document.getElementById(dismissLinkId).onclick = _dismissLinkClick;
      }
    }

    function _removeCookieConsent() {
      var cookieChoiceElement = document.getElementById(cookieConsentId);
      if (cookieChoiceElement != null) {
        cookieChoiceElement.parentNode.removeChild(cookieChoiceElement);
      }
    }

    function _saveUserPreference() {
      // Set the cookie expiry to one year after today.
      var expiryDate = new Date();
      expiryDate.setFullYear(expiryDate.getFullYear() + 1);
      document.cookie = cookieName + '=y; expires=' + expiryDate.toGMTString();
    }

    function _shouldDisplayConsent() {
      // Display the header only if the cookie has not been set.
      return !document.cookie.match(new RegExp(cookieName + '=([^;]+)'));
    }

    var exports = {};
    exports.showCookieConsentBar = _showCookieConsent;
    return exports;
  })();

  window.cookieChoices = cookieChoices;
  return cookieChoices;
})(this);
<commit_msg>ustawienie ciastka dla wszystkich podstron<commit_after>/*
 Copyright 2014 Google Inc. 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.
 */

/*
 * For more information on this file, see http://www.cookiechoices.org/
 */

(function(window) {

  if (!!window.cookieChoices) {
    return window.cookieChoices;
  }

  var document = window.document;
  // IE8 does not support textContent, so we should fallback to innerText.
  var supportsTextContent = 'textContent' in document.body;

  var cookieChoices = (function() {

    var cookieName = 'displayCookieConsent';
    var cookieConsentId = 'cookieChoiceInfo';
    var divClass = 'cookie-choices-info';
    var innerDivClass = 'cookie-choices-inner';
    var textSpan = 'cookie-choices-text';
    var buttonsClass = 'cookie-choices-buttons';
    var buttonClass = 'cookie-choices-button';
    var singletonClass = 'singleton-element';
    var dismissLinkId = 'cookieChoiceDismiss';

    function _createHeaderElement(cookieText, dismissText, linkText, linkHref) {
      var cookieInnerElement = document.createElement('div');
      cookieInnerElement.className = innerDivClass;
      cookieInnerElement.appendChild(_createConsentText(cookieText));

      var buttonsElement = document.createElement('span');
      buttonsElement.className = buttonsClass;
      cookieInnerElement.appendChild(buttonsElement);

      if (!!linkText && !!linkHref) {
        buttonsElement.appendChild(_createInformationLink(linkText, linkHref));
      }

      buttonsElement.appendChild(_createDismissLink(dismissText));

      var cookieConsentElement = document.createElement('div');
      cookieConsentElement.id = cookieConsentId;
      cookieConsentElement.className = divClass + ' ' + singletonClass;
      cookieConsentElement.appendChild(cookieInnerElement);
      return cookieConsentElement;
    }

    function _createStyleElement() {
      var style = document.createElement('style');
      style.className = singletonClass;
      style.type = 'text/css';
      _setElementText(style,
          '.' + divClass + ' { ' +
              'position:fixed;width:100%;background-color:#666;margin:0;' +
              'left:0;bottom:0;padding:0;z-index:4000;text-align:center;' +
              'color:#fff;line-height:140%;' +
              'font-family:roboto,Arial; } ' +
          '.' + divClass + ' .' + innerDivClass + ' { ' +
              'position:relative;width:initial;margin:0;left:0;bottom:0; } ' +
          '.' + divClass + ' .' + textSpan + ' { ' +
              'display:inline-block;vertical-align:middle;font-size:16px;' +
              'margin:2px 20px;color:#ccc;max-width:800px;' +
              'text-align:left; }' +
          '.' + divClass + ' .' + buttonsClass + ' { ' +
              'display:inline-block;vertical-align:middle;' +
              'white-space:nowrap;margin:0 10px; } ' +
          '.' + divClass + ' .' + buttonClass + ':hover { ' +
              ' color: #fff; } ' +
          '.' + divClass + ' .' + buttonClass + ' { ' +
              'text-transform:UPPERCASE;' +
              'white-space:nowrap;' +
              'color:#eee;margin-left:8px;padding:0 6px; ' +
              'text-decoration:none; }');
      document.getElementsByTagName('head')[0].appendChild(style);
    }

    function _setElementText(element, text) {
      if (supportsTextContent) {
        element.textContent = text;
      } else {
        element.innerText = text;
      }
    }

    function _createConsentText(cookieText) {
      var consentText = document.createElement('span');
      _setElementText(consentText, cookieText);
      consentText.className = textSpan;
      return consentText;
    }

    function _createDismissLink(dismissText) {
      var dismissLink = document.createElement('a');
      _setElementText(dismissLink, dismissText);
      dismissLink.id = dismissLinkId;
      dismissLink.href = '#';
      dismissLink.className = buttonClass;
      return dismissLink;
    }

    function _createInformationLink(linkText, linkHref) {
      var infoLink = document.createElement('a');
      _setElementText(infoLink, linkText);
      infoLink.href = linkHref;
      infoLink.target = '_blank';
      infoLink.className = buttonClass;
      return infoLink;
    }

    function _dismissLinkClick(e) {
      _saveUserPreference();
      _removeCookieConsent();
      e.stopPropagation && e.stopPropagation();
      e.cancelBubble = true;
      return false;
    }

    function _showCookieConsent(cookieText, dismissText, linkText, linkHref) {
      if (_shouldDisplayConsent()) {
        _removeCookieConsent();
        _createStyleElement();
        var consentElement =
            _createHeaderElement(cookieText, dismissText, linkText, linkHref);
        var fragment = document.createDocumentFragment();
        fragment.appendChild(consentElement);
        document.body.appendChild(fragment.cloneNode(true));
        document.getElementById(dismissLinkId).onclick = _dismissLinkClick;
      }
    }

    function _removeCookieConsent() {
      var cookieChoiceElement = document.getElementById(cookieConsentId);
      if (cookieChoiceElement != null) {
        cookieChoiceElement.parentNode.removeChild(cookieChoiceElement);
      }
    }

    function _saveUserPreference() {
      // Set the cookie expiry to one year after today.
      var expiryDate = new Date();
      expiryDate.setFullYear(expiryDate.getFullYear() + 1);
      document.cookie = cookieName + '=y; expires=' + expiryDate.toGMTString() + '; path=/';
    }

    function _shouldDisplayConsent() {
      // Display the header only if the cookie has not been set.
      return !document.cookie.match(new RegExp(cookieName + '=([^;]+)'));
    }

    var exports = {};
    exports.showCookieConsentBar = _showCookieConsent;
    return exports;
  })();

  window.cookieChoices = cookieChoices;
  return cookieChoices;
})(this);
<|endoftext|>


<commit_before>/* global App */

/**
 * Features module
 *
 * HTML5 feature detection based on the concepts and ideas by Modernizr
 * @link http://html5please.com/
 * @link http://caniuse.com/
 * @link https://github.com/NielsLeenheer/html5test/
 *
 * Modified: 2015/11/05
 * @author softwarespot
 */
App.namespace('core').features = (function featuresModule(window, document, $, core, undefined) {
    // Constants

    // SemVer version number of the module
    var VERSION = '1.0.0';

    // Unique global identifier. Internal usage only
    // var GUID = '3B8C44B7-6A3B-4F84-A031-2D7B5D4ED211';

    // Fields

    // Store if the module has been initialised
    var _isInitialised = false;

    // Hold the input data, with the key being the input type and the value of either true or false as
    // whether or not it's supported by the following browser
    var _inputs = null;

    // Methods

    /**
     * Initialise the module
     *
     * @return {undefined}
     */
    function init() {
        if (_isInitialised) {
            return;
        }

        // Initialise the input object literal
        _getInputs();

        _isInitialised = true;
    }

    /**
     * Destroy the module
     *
     * @return {undefined}
     */
    function destroy() {
        _isInitialised = false;
    }

    /**
     * Get the version number of the module
     *
     * @returns {number} Version number
     */
    function getVersion() {
        return VERSION;
    }

    // EVENTS
    // https://github.com/Modernizr/Modernizr/blob/master/src/hasEvent.js
    // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/hashchange.js

    // HISTORY

    /**
     * Check if the History API exists
     *
     * Based on the concept by Modernizr URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasHistory() {
        var history = window.history;
        return core.isObject(history) &&
            'pushState' in history;
    }

    // GEOLOCATION

    /**
     * Check if the geolocation API exists
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/geolocation.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasGeoLocation() {
        return core.isObject(window.navigator) && 'geolocation' in window.navigator;
    }

    // INPUTS

    /**
     * Check if an input type is supported
     *
     * @param {string} inputType Input type
     * @return {boolean} True the type is supported; otherwise, false
     */
    function hasInput(inputType) {
        // _getInputs();

        // Coerce as a string
        var hasInputResult = _inputs[core.toString(inputType)];
        return core.isUndefined(hasInputResult) ? false : hasInputResult;
    }

    /**
     * Initialise the input object literal
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/commits/master/feature-detects/inputtypes.js
     *
     * @return {undefined}
     */
    function _getInputs() {
        if (!core.isNull(_inputs)) {
            return;
        }

        // Regular expressions
        var regExp = {
            EMAIL_NUMBER_URL: /^(?:email|number|url)$/,
            RANGE: /(?:^range$)/,
            SEARCH_AND_TEL: /^(?:search|tel)$/,
        };

        // Create an empty object literal
        _inputs = {};

        // Input types that will are split as an array
        var inputTypes = 'color date datetime datetime-local email month number range search tel time url week'.split(' ');

        // Create a temporary input element
        var input = document.createElement('input');

        // Document element to bind the input element to
        var documentElem = document.documentElement;

        // Inline stylesheet
        var cssStyles = 'position:absolute;visibility:hidden;';

        // Data to test with
        var testData = '=)';

        // Iterate through the array. A polyfill is available at
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Polyfill
        inputTypes.forEach(function forEachInputType(inputType) {

            // Set the attribute type of the input element
            input.setAttribute('type', inputType);

            // Check the type is not text and that it contains a 'style' property
            var isValid = input.type !== 'text' && 'style' in input;

            // If invalid then simply return and don't continue
            if (!isValid) {
                return;
            }

            // First check to see if the type we give it sticks
            // If the type does, we feed it a textual value, which shouldn't be valid
            // If the value doesn't stick, we know there's input sanitization which infers a custom UI
            input.value = testData;

            // Set the style of the input element
            input.style.cssText = cssStyles;

            // If a range type
            if (regExp.RANGE.test(inputType) && core.isDefined(input.style.WebkitAppearance)) {

                // Append the input element to the current document
                documentElem.appendChild(input);

                // Cache the defaultView
                var defaultView = document.defaultView;

                // Safari 2-4 allows the smiley as a value, despite making a slider
                isValid = defaultView.getComputedStyle &&

                    defaultView.getComputedStyle(input, null)
                    .WebkitAppearance !== 'textfield' &&

                    // The mobile Android web browser has a false positive, so
                    // check the height to see if the widget is actually there
                    (input.offsetHeight !== 0);

                documentElem.removeChild(input);

                // If a search or tel type
            } else if (regExp.SEARCH_AND_TEL.test(inputType)) {
                // Specification doesn't define any special parsing or detectable UI
                // behaviours so we pass these through as true

                // Interestingly, Opera fails the earlier test, so it doesn't
                // even make it here (I doubt anymore!)

                // If a email, number or url type
            } else if (regExp.EMAIL_NUMBER_URL.test(inputType)) {

                // The following types come with pre-backed validation
                isValid = input.checkValidity && input.checkValidity() === false;

                // For all other types check if the value has been sanitised
            } else {

                // If the upgraded input component rejects the :) text, we've got a winner
                isValid = input.value !== testData;

            }

            // Add to the internal object literal
            _inputs[inputType] = !!isValid;

        });
    }

    // PROMISE

    /**
     * Check if the Promise API exists
     *
     * Based on the concept by Modernizr URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/es6/promises.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasPromise() {
        var promise = window.Promise;
        return core.isFunction(promise) &&
            'all' in promise &&
            'race' in promise &&
            'reject' in promise &&
            'resolve' in promise;
    }

    // STORAGE

    /**
     * Check if the localStorage API exists
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasLocalStorage() {
        return _isStorage(window.localStorage);
    }

    /**
     * Check if the sessionStorage API exists
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/sessionstorage.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasSessionStorage() {
        return _isStorage(window.sessionStorage);
    }

    /**
     * Check if a valid storage object
     *
     * @param {type} storage Storage object
     * @return {boolean} storage True, is valid storage object; otherwise, false
     */
    function _isStorage(storage) {
        return core.isObject(storage) &&
            'key' in storage &&
            'getItem' in storage &&
            'setItem' in storage &&
            'removeItem' in storage &&
            'clear' in storage;
    }

    /**
     * Check if the webStorage API exists
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasWebStorage() {
        return hasLocalStorage() && hasSessionStorage();
    }

    // Public API
    return {
        init: init,
        destroy: destroy,
        getVersion: getVersion,
        hasHistory: hasHistory,
        hasPromise: hasPromise,
        hasInput: hasInput,
        hasLocalStorage: hasLocalStorage,
        hasSessionStorage: hasSessionStorage,
        hasWebStorage: hasWebStorage,
        hasGeoLocation: hasGeoLocation,
    };
})(window, window.document, window.jQuery, window.App.core);
<commit_msg>Using fields<commit_after>/* global App */

/**
 * Features module
 *
 * HTML5 feature detection based on the concepts and ideas by Modernizr
 * @link http://html5please.com/
 * @link http://caniuse.com/
 * @link https://github.com/NielsLeenheer/html5test/
 *
 * Modified: 2015/11/05
 * @author softwarespot
 */
App.namespace('core').features = (function featuresModule(window, document, $, core, undefined) {
    // Constants

    // SemVer version number of the module
    var VERSION = '1.0.0';

    // Unique global identifier. Internal usage only
    // var GUID = '3B8C44B7-6A3B-4F84-A031-2D7B5D4ED211';

    // Fields

    // Store if the module has been initialised
    var _isInitialised = false;

    // Hold the input data, with the key being the input type and the value of either true or false as
    // whether or not it's supported by the following browser
    var _inputs = null;

    // Methods

    /**
     * Initialise the module
     *
     * @return {undefined}
     */
    function init() {
        if (_isInitialised) {
            return;
        }

        // Initialise the input object literal
        _getInputs();

        _isInitialised = true;
    }

    /**
     * Destroy the module
     *
     * @return {undefined}
     */
    function destroy() {
        _isInitialised = false;
    }

    /**
     * Get the version number of the module
     *
     * @returns {number} Version number
     */
    function getVersion() {
        return VERSION;
    }

    // EVENTS
    // https://github.com/Modernizr/Modernizr/blob/master/src/hasEvent.js
    // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/hashchange.js

    // HISTORY

    /**
     * Check if the History API exists
     *
     * Based on the concept by Modernizr URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasHistory() {
        var history = window.history;
        return core.isObject(history) &&
            'pushState' in history;
    }

    // GEOLOCATION

    /**
     * Check if the geolocation API exists
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/geolocation.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasGeoLocation() {
        return core.isObject(window.navigator) && 'geolocation' in window.navigator;
    }

    // INPUTS

    /**
     * Check if an input type is supported
     *
     * @param {string} inputType Input type
     * @return {boolean} True the type is supported; otherwise, false
     */
    function hasInput(inputType) {
        // _getInputs();

        // Coerce as a string
        var hasInputResult = _inputs[core.toString(inputType)];
        return core.isUndefined(hasInputResult) ? false : hasInputResult;
    }

    /**
     * Initialise the input object literal
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/commits/master/feature-detects/inputtypes.js
     *
     * @return {undefined}
     */
    function _getInputs() {
        if (!core.isNull(_inputs)) {
            return;
        }

        // Regular expressions
        var reEmailNumberUrl = /^(?:email|number|url)$/;
        var reRange = /(?:^range$)/;
        var reSearchTel = /^(?:search|tel)$/;

        // Create an empty object literal
        _inputs = {};

        // Input types that will are split as an array
        var inputTypes = 'color date datetime datetime-local email month number range search tel time url week'.split(' ');

        // Create a temporary input element
        var input = document.createElement('input');

        // Document element to bind the input element to
        var documentElem = document.documentElement;

        // Inline stylesheet
        var cssStyles = 'position:absolute;visibility:hidden;';

        // Data to test with
        var testData = '=)';

        // Iterate through the array. A polyfill is available at
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Polyfill
        inputTypes.forEach(function forEachInputType(inputType) {

            // Set the attribute type of the input element
            input.setAttribute('type', inputType);

            // Check the type is not text and that it contains a 'style' property
            var isValid = input.type !== 'text' && 'style' in input;

            // If invalid then simply return and don't continue
            if (!isValid) {
                return;
            }

            // First check to see if the type we give it sticks
            // If the type does, we feed it a textual value, which shouldn't be valid
            // If the value doesn't stick, we know there's input sanitization which infers a custom UI
            input.value = testData;

            // Set the style of the input element
            input.style.cssText = cssStyles;

            // If a range type
            if (reRange.test(inputType) && core.isDefined(input.style.WebkitAppearance)) {

                // Append the input element to the current document
                documentElem.appendChild(input);

                // Cache the defaultView
                var defaultView = document.defaultView;

                // Safari 2-4 allows the smiley as a value, despite making a slider
                isValid = defaultView.getComputedStyle &&

                    defaultView.getComputedStyle(input, null)
                    .WebkitAppearance !== 'textfield' &&

                    // The mobile Android web browser has a false positive, so
                    // check the height to see if the widget is actually there
                    (input.offsetHeight !== 0);

                documentElem.removeChild(input);

                // If a search or tel type
            } else if (reSearchTel.test(inputType)) {
                // Specification doesn't define any special parsing or detectable UI
                // behaviours so we pass these through as true

                // Interestingly, Opera fails the earlier test, so it doesn't
                // even make it here (I doubt anymore!)

                // If a email, number or url type
            } else if (reEmailNumberUrl.test(inputType)) {

                // The following types come with pre-backed validation
                isValid = input.checkValidity && input.checkValidity() === false;

                // For all other types check if the value has been sanitised
            } else {

                // If the upgraded input component rejects the :) text, we've got a winner
                isValid = input.value !== testData;

            }

            // Add to the internal object literal
            _inputs[inputType] = !!isValid;

        });
    }

    // PROMISE

    /**
     * Check if the Promise API exists
     *
     * Based on the concept by Modernizr URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/es6/promises.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasPromise() {
        var promise = window.Promise;
        return core.isFunction(promise) &&
            'all' in promise &&
            'race' in promise &&
            'reject' in promise &&
            'resolve' in promise;
    }

    // STORAGE

    /**
     * Check if the localStorage API exists
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasLocalStorage() {
        return _isStorage(window.localStorage);
    }

    /**
     * Check if the sessionStorage API exists
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/sessionstorage.js
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasSessionStorage() {
        return _isStorage(window.sessionStorage);
    }

    /**
     * Check if a valid storage object
     *
     * @param {type} storage Storage object
     * @return {boolean} storage True, is valid storage object; otherwise, false
     */
    function _isStorage(storage) {
        return core.isObject(storage) &&
            'key' in storage &&
            'getItem' in storage &&
            'setItem' in storage &&
            'removeItem' in storage &&
            'clear' in storage;
    }

    /**
     * Check if the webStorage API exists
     *
     * Based on the concept by Modernizr, URL: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/
     *
     * @return {boolean} True the feature exists; otherwise, false
     */
    function hasWebStorage() {
        return hasLocalStorage() && hasSessionStorage();
    }

    // Public API
    return {
        init: init,
        destroy: destroy,
        getVersion: getVersion,
        hasHistory: hasHistory,
        hasPromise: hasPromise,
        hasInput: hasInput,
        hasLocalStorage: hasLocalStorage,
        hasSessionStorage: hasSessionStorage,
        hasWebStorage: hasWebStorage,
        hasGeoLocation: hasGeoLocation,
    };
})(window, window.document, window.jQuery, window.App.core);
<|endoftext|>


<commit_before>/**
 * @class Alloy
 * Top-level module for Alloy functions.
 *
 */
var 	   _ = require('alloy/underscore')._,
	Backbone = require('alloy/backbone');

exports._ = _;
exports.Backbone = Backbone;

function ucfirst(text) {
    if (!text) { return text; }
    return text[0].toUpperCase() + text.substr(1);
};

// Ti.Analytics.featureEvent('alloy.start', {
// 	guid: Ti.Platform.id,
// 	tiversion: Ti.version,
// 	osname: Ti.Platform.osname,
// 	osversion: Ti.Platform.version,
// 	locale: Ti.Platform.locale
// });

exports.M = function(name, modelDesc, migrations) {
	var config = modelDesc.config;
    var type = (config.adapter ? config.adapter.type : null) || 'localDefault';
    if (type === 'localDefault') {
    	type = OS_MOBILEWEB ? 'localStorage' : 'sql';
    }

	var adapter = require('alloy/sync/'+type);
    var extendObj = {
		defaults: config.defaults,
        sync: function(method, model, opts) {
			var config = model.config || {};
			var adapterObj = config.adapter || {};
			var type = (config.adapter ? config.adapter.type : null) || 'localDefault';
			if (type === 'localDefault') {
		    	type = OS_MOBILEWEB ? 'localStorage' : 'sql';
		    }

			require('alloy/sync/'+type).sync(model,method,opts);
		}
	};

	var extendClass = {};

	// construct the model based on the current adapter type
	if (migrations) { extendClass.migrations = migrations; }
    if (_.isFunction(adapter.beforeModelCreate)) { config = adapter.beforeModelCreate(config) || config; }
	var Model = Backbone.Model.extend(extendObj, extendClass);
	Model.prototype.config = config;
	if (_.isFunction(adapter.afterModelCreate)) { adapter.afterModelCreate(Model); }

	if (_.isFunction(modelDesc.extendModel)) {
		Model = modelDesc.extendModel(Model) || Model;
	}

	return Model;
};

exports.C = function(name, modelDesc, model) {
    var extendObj = {
		model: model,
        sync: function(method, model, opts) {
			var config = (model.config || {});
			var type = (config.adapter ? config.adapter.type : null) || 'localDefault';
			if (type === 'localDefault') {
		    	type = OS_MOBILEWEB ? 'localStorage' : 'sql';
		    }

			require('alloy/sync/'+type).sync(model,method,opts);
		}
	};

	var Collection = Backbone.Collection.extend(extendObj);
	var config = Collection.prototype.config = model.prototype.config;

	var type = (config.adapter ? config.adapter.type : null) || 'localDefault';
	var adapter = require('alloy/sync/'+type);
	if (_.isFunction(adapter.afterCollectionCreate)) { adapter.afterCollectionCreate(Collection); }

	if (_.isFunction(modelDesc.extendModel)) {
		Collection = modelDesc.extendCollection(Collection) || Collection;
	}

	return Collection;
};

exports.A = function(t, type, parent) {
	_.extend(t,Backbone.Events);

	(function() {

		// we are going to wrap addEventListener and removeEventListener
		// with on, off so we can use the Backbone events
		var al = t.addEventListener,
			rl = t.removeEventListener,
			oo = t.on,
			of = t.off,
			tg = t.trigger,
		   cbs = {},
		   ctx = _.extend({}, Backbone.Events);

		if (!al || !rl) {
			return;
		}

		t.trigger = function() {
			ctx.trigger.apply(ctx, Array.prototype.slice.apply(arguments));
		}

		t.on = function(e,cb,context) {
			var wcb = function(evt) {
				try {
					_.bind(tg,ctx,e,evt)();
				}
				catch(E) {
					Ti.API.error("Error triggering '"+e+"' event: "+E);
				}
			};

			if (!cbs[e]) {
				cbs[e] = {};
				if (OS_IOS) {
					al(e, wcb);
				} else {
					al.call(t, e, wcb);
				}
			}
			cbs[e][cb] = wcb;

			_.bind(oo,ctx,e,cb,context)();
		};

		t.off = function(e,cb,context) {
			var f = cbs[e] ? cbs[e][cb] : null;
			if (f) {
				_.bind(of,ctx,e,cb,context)();
				delete cbs[e][cb];

				if (cbs[e].length === 0) {
					delete cbs[e];
					if (OS_IOS) {
						rl(e, f);
					} else {
						rl.call(t, e, f);
					}
				}
				f = null;
			}
		};

	})();

	return t;
}

/**
 * @method getWidget
 * Factory method for instantiating a widget controller. Creates and returns an instance of the
 * named widget.
 * @param {String} id Id of widget to instantiate.
 * @param {String} name Name of the view within the widget to instantiate ('widget' by default)
 * @param {*...} [args] Arguments to pass to the widget.
 * @return {Alloy.Controller} Alloy widget controller object.
 *
 * @deprecated 0.3 Use {Alloy#createWidget} instead
 */
exports.getWidget = function(id, name, args) {
	Ti.API.warn('Alloy.getWidget() is deprecated, use Alloy.createWidget() instead.');
	Ti.API.warn('Alloy.getWidget() will be removed in Alloy 0.4.0');
	return exports.createWidget(id, name, args);
}

/**
 * @method createWidget
 * Factory method for instantiating a widget controller. Creates and returns an instance of the
 * named widget.
 * @param {String} id Id of widget to instantiate.
 * @param {String} name Name of the view within the widget to instantiate ('widget' by default)
 * @param {*...} [args] Arguments to pass to the widget.
 * @return {Alloy.Controller} Alloy widget controller object.
 */
exports.createWidget = function(id, name, args) {
	return new (require('alloy/widgets/' + id + '/controllers/' + (name || 'widget')))(args);
}

/**
 * @method getController
 * Factory method for instantiating a controller. Creates and returns an instance of the
 * named controller.
 * @param {String} name Name of controller to instantiate.
 * @param {*...} [args] Arguments to pass to the controller.
 * @return {Alloy.Controller} Alloy controller object.
 *
 * @deprecated 0.3 Use {Alloy#createController} instead
 */
exports.getController = function(name, args) {
	Ti.API.warn('Alloy.getController() is deprecated, use Alloy.createController() instead.');
	Ti.API.warn('Alloy.getController() will be removed in Alloy 0.4.0');
	return exports.createController(name, args);
}

/**
 * @method createController
 * Factory method for instantiating a controller. Creates and returns an instance of the
 * named controller.
 * @param {String} name Name of controller to instantiate.
 * @param {*...} [args] Arguments to pass to the controller.
 * @return {Alloy.Controller} Alloy controller object.
 */
exports.createController = function(name, args) {
	return new (require('alloy/controllers/' + name))(args);
}

/**
 * @method getModel
 * Factory method for instantiating a Backbone Model object. Creates and returns an instance of the
 * named model.
 *
 * See [Backbone.Model](http://backbonejs.org/#Model) in the Backbone.js documentation for
 * information on the methods and properties provided by the Model object.
 * @param {String} name Name of model to instantiate.
 * @param {*...} [args] Arguments to pass to the model.
 * @return {Backbone.Model} Backbone model object.
 *
 * @deprecated 0.3 Use {Alloy#createModel} instead
 */
exports.getModel = function(name, args) {
	Ti.API.warn('Alloy.getModel() is deprecated, use Alloy.createModel() instead.');
	Ti.API.warn('Alloy.getModel() will be removed in Alloy 0.4.0');
	return exports.createModel(name, args);
}

/**
 * @method createModel
 * Factory method for instantiating a Backbone Model object. Creates and returns an instance of the
 * named model.
 *
 * See [Backbone.Model](http://backbonejs.org/#Model) in the Backbone.js documentation for
 * information on the methods and properties provided by the Model object.
 * @param {String} name Name of model to instantiate.
 * @param {*...} [args] Arguments to pass to the model.
 * @return {Backbone.Model} Backbone model object.
 */
exports.createModel = function(name, args) {
	return new (require('alloy/models/' + ucfirst(name)).Model)(args);
}

/**
 * @method getCollection
 * Factory method for instantiating a Backbone collection of model objects. Creates and returns a
 * collection for holding the named type of model objects.
 *
 * See [Backbone.Collection](http://backbonejs.org/#Collection) in the Backbone.js
 * documentation for  information on the methods and  properties provided by the
 * Collection object.
 * @param {String} name Name of model to hold in this collection.
 * @param {*...} [args] Arguments to pass to the collection.
 * @return {Backbone.Collection} Backbone collection object.
 *
 * @deprecated 0.3 Use {Alloy#createCollection} instead
 */
exports.getCollection = function(name, args) {
	Ti.API.warn('Alloy.getCollection() is deprecated, use Alloy.createCollection() instead.');
	Ti.API.warn('Alloy.getCollection() will be removed in Alloy 0.4.0');
	return exports.createCollection(name, args);
}

/**
 * @method createCollection
 * Factory method for instantiating a Backbone collection of model objects. Creates and returns a
 * collection for holding the named type of model objects.
 *
 * See [Backbone.Collection](http://backbonejs.org/#Collection) in the Backbone.js
 * documentation for  information on the methods and  properties provided by the
 * Collection object.
 * @param {String} name Name of model to hold in this collection.
 * @param {*...} [args] Arguments to pass to the collection.
 * @return {Backbone.Collection} Backbone collection object.
 */
exports.createCollection = function(name, args) {
	return new (require('alloy/models/' + ucfirst(name)).Collection)(args);
}

function isTabletFallback() {
	return !(Math.min(
		Ti.Platform.displayCaps.platformHeight,
		Ti.Platform.displayCaps.platformWidth
	) < 700);
}

/**
 * @property {Boolean} isTablet
 * `true` if the current device is a tablet.
 *
 */
exports.isTablet = (function() {
	if (OS_IOS) {
		return Ti.Platform.osname === 'ipad';
	}
	if (OS_ANDROID) {
		try {
			var psc = require('ti.physicalSizeCategory');
			return psc.physicalSizeCategory === 'large' ||
				   psc.physicalSizeCategory === 'xlarge';
		} catch(e) {
			Ti.API.warn('Could not find ti.physicalSizeCategory module, using fallback for Alloy.isTablet');
			return isTabletFallback();
		}
	}
	if (OS_MOBILEWEB) {
		return !(Math.min(
			Ti.Platform.displayCaps.platformHeight,
			Ti.Platform.displayCaps.platformWidth
		) < 400);
	}

	// Last resort. Don't worry, uglifyjs cleans up this dead code if necessary.
	return isTabletFallback();
})();

/**
 * @property {Boolean} isHandheld
 * `true` if the current device is a handheld device (not a tablet).
 *
 */
exports.isHandheld = !exports.isTablet;

/**
 * @property {Object} globals
 * An object for storing globally accessible variables and functions. Items
 * added to Alloy.globals will be accessible in any controller in your app.
 *
 * @deprecated 0.3.3 Use {Alloy#Globals} instead
 */
exports.globals = {};

/**
 * @property {Object} Globals
 * An object for storing globally accessible variables and functions.
 * Alloy.CFG is accessible in any controller in your app:
 *
 *     Alloy.Globals.someGlobalObject = { key: 'value' };
 *     Alloy.Globals.someGlobalFunction = function(){};
 *
 * Alloy.Globals can be accessed in other non-controller Javascript files
 * like this:
 *
 *     var theObject = require('alloy').Globals.someGlobalObject;
 *
 */
exports.Globals = {};

/**
 * @property {Object} Models
 * An object for storing globally accessible Alloy models. Singleton models
 * created via markup will be stored on this object.
 *
 *     <Model src="myModel"/>
 *
 * The above markup would effectively generate the following code:
 *
 *     Alloy.Models.myModel = Alloy.createModel('MyModel');
 *
 * Alloy.Models.myModel would then be accessible in any controller in your app.
 *
 */
exports.Models = {};

/**
 * @method instance
 * Creates a singleton instance of a Model based on the given model, or
 * returns an existing instance if one has already been created.
 * @param {String} the name of the base model for the Model
 * @return An Alloy Model object singleton
 *
 */
 exports.Models.instance = function(name) {
 	return exports.Models[name] || (exports.Models[name] = exports.createModel(name));
 };

/**
 * @property {Object} Collections
 * An object for storing globally accessible Alloy collections. Singleton collections
 * created via markup will be stored on this object.
 *
 *     <Collection src="myModel"/>
 *
 * The above markup would effectively generate the following code:
 *
 *     Alloy.Collections.myModel = Alloy.createCollection('MyModel');
 *
 * Alloy.Collections.myModel would then be accessible in any controller in your app.
 *
 */
exports.Collections = {};

/**
 * @method instance
 * Creates a singleton instance of a Collection based on the given model, or
 * returns an existing instance if one has already been created.
 * @param {String} the name of the base model for the collection
 * @return An Alloy Collection object singleton
 *
 */
 exports.Collections.instance = function(name) {
 	return exports.Collections[name] || (exports.Collections[name] = exports.createCollection(name));
 };

/**
 * @property {Object} CFG
 * An object that stores Alloy configuration values as defined in your app's
 * app/config.json file. Here's a what a typical config.json file might look
 * like in an Alloy app.
 *
 *     {
 *         "global": { "key": "defaultValue", "anotherKey": 12345 },
 *         "env:development": {},
 *         "env:test": {},
 *         "env:production": {},
 *         "os:ios": { "key": "iosValue" },
 *         "os:android": { "key": "androidValue" },
 *         "dependencies": {}
 *     }
 *
 * If this app was compiled for iOS, the Alloy.CFG would look like this:
 *
 *     Alloy.CFG = {
 *         "key": "iosValue",
 *         "anotherKey": 12345
 *     }
 *
 * Alloy.CFG is accessible in any controller in your app, and can be accessed
 * in other non-controller Javascript files like this:
 *
 *     var theKey = require('alloy').CFG.key;
 *
 */
exports.CFG = require('alloy/CFG');

if (OS_ANDROID) {
	exports.Android = {};
	exports.Android.menuItemCreateArgs = ['itemId','groupId','title','order'];
}
<commit_msg>fixed function validation for collection extending<commit_after>/**
 * @class Alloy
 * Top-level module for Alloy functions.
 *
 */
var 	   _ = require('alloy/underscore')._,
	Backbone = require('alloy/backbone');

exports._ = _;
exports.Backbone = Backbone;

function ucfirst(text) {
    if (!text) { return text; }
    return text[0].toUpperCase() + text.substr(1);
};

// Ti.Analytics.featureEvent('alloy.start', {
// 	guid: Ti.Platform.id,
// 	tiversion: Ti.version,
// 	osname: Ti.Platform.osname,
// 	osversion: Ti.Platform.version,
// 	locale: Ti.Platform.locale
// });

exports.M = function(name, modelDesc, migrations) {
	var config = modelDesc.config;
    var type = (config.adapter ? config.adapter.type : null) || 'localDefault';
    if (type === 'localDefault') {
    	type = OS_MOBILEWEB ? 'localStorage' : 'sql';
    }

	var adapter = require('alloy/sync/'+type);
    var extendObj = {
		defaults: config.defaults,
        sync: function(method, model, opts) {
			var config = model.config || {};
			var adapterObj = config.adapter || {};
			var type = (config.adapter ? config.adapter.type : null) || 'localDefault';
			if (type === 'localDefault') {
		    	type = OS_MOBILEWEB ? 'localStorage' : 'sql';
		    }

			require('alloy/sync/'+type).sync(model,method,opts);
		}
	};

	var extendClass = {};

	// construct the model based on the current adapter type
	if (migrations) { extendClass.migrations = migrations; }
    if (_.isFunction(adapter.beforeModelCreate)) { config = adapter.beforeModelCreate(config) || config; }
	var Model = Backbone.Model.extend(extendObj, extendClass);
	Model.prototype.config = config;
	if (_.isFunction(adapter.afterModelCreate)) { adapter.afterModelCreate(Model); }

	if (_.isFunction(modelDesc.extendModel)) {
		Model = modelDesc.extendModel(Model) || Model;
	}

	return Model;
};

exports.C = function(name, modelDesc, model) {
    var extendObj = {
		model: model,
        sync: function(method, model, opts) {
			var config = (model.config || {});
			var type = (config.adapter ? config.adapter.type : null) || 'localDefault';
			if (type === 'localDefault') {
		    	type = OS_MOBILEWEB ? 'localStorage' : 'sql';
		    }

			require('alloy/sync/'+type).sync(model,method,opts);
		}
	};

	var Collection = Backbone.Collection.extend(extendObj);
	var config = Collection.prototype.config = model.prototype.config;

	var type = (config.adapter ? config.adapter.type : null) || 'localDefault';
	var adapter = require('alloy/sync/'+type);
	if (_.isFunction(adapter.afterCollectionCreate)) { adapter.afterCollectionCreate(Collection); }

	if (_.isFunction(modelDesc.extendCollection)) {
		Collection = modelDesc.extendCollection(Collection) || Collection;
	}

	return Collection;
};

exports.A = function(t, type, parent) {
	_.extend(t,Backbone.Events);

	(function() {

		// we are going to wrap addEventListener and removeEventListener
		// with on, off so we can use the Backbone events
		var al = t.addEventListener,
			rl = t.removeEventListener,
			oo = t.on,
			of = t.off,
			tg = t.trigger,
		   cbs = {},
		   ctx = _.extend({}, Backbone.Events);

		if (!al || !rl) {
			return;
		}

		t.trigger = function() {
			ctx.trigger.apply(ctx, Array.prototype.slice.apply(arguments));
		}

		t.on = function(e,cb,context) {
			var wcb = function(evt) {
				try {
					_.bind(tg,ctx,e,evt)();
				}
				catch(E) {
					Ti.API.error("Error triggering '"+e+"' event: "+E);
				}
			};

			if (!cbs[e]) {
				cbs[e] = {};
				if (OS_IOS) {
					al(e, wcb);
				} else {
					al.call(t, e, wcb);
				}
			}
			cbs[e][cb] = wcb;

			_.bind(oo,ctx,e,cb,context)();
		};

		t.off = function(e,cb,context) {
			var f = cbs[e] ? cbs[e][cb] : null;
			if (f) {
				_.bind(of,ctx,e,cb,context)();
				delete cbs[e][cb];

				if (cbs[e].length === 0) {
					delete cbs[e];
					if (OS_IOS) {
						rl(e, f);
					} else {
						rl.call(t, e, f);
					}
				}
				f = null;
			}
		};

	})();

	return t;
}

/**
 * @method getWidget
 * Factory method for instantiating a widget controller. Creates and returns an instance of the
 * named widget.
 * @param {String} id Id of widget to instantiate.
 * @param {String} name Name of the view within the widget to instantiate ('widget' by default)
 * @param {*...} [args] Arguments to pass to the widget.
 * @return {Alloy.Controller} Alloy widget controller object.
 *
 * @deprecated 0.3 Use {Alloy#createWidget} instead
 */
exports.getWidget = function(id, name, args) {
	Ti.API.warn('Alloy.getWidget() is deprecated, use Alloy.createWidget() instead.');
	Ti.API.warn('Alloy.getWidget() will be removed in Alloy 0.4.0');
	return exports.createWidget(id, name, args);
}

/**
 * @method createWidget
 * Factory method for instantiating a widget controller. Creates and returns an instance of the
 * named widget.
 * @param {String} id Id of widget to instantiate.
 * @param {String} name Name of the view within the widget to instantiate ('widget' by default)
 * @param {*...} [args] Arguments to pass to the widget.
 * @return {Alloy.Controller} Alloy widget controller object.
 */
exports.createWidget = function(id, name, args) {
	return new (require('alloy/widgets/' + id + '/controllers/' + (name || 'widget')))(args);
}

/**
 * @method getController
 * Factory method for instantiating a controller. Creates and returns an instance of the
 * named controller.
 * @param {String} name Name of controller to instantiate.
 * @param {*...} [args] Arguments to pass to the controller.
 * @return {Alloy.Controller} Alloy controller object.
 *
 * @deprecated 0.3 Use {Alloy#createController} instead
 */
exports.getController = function(name, args) {
	Ti.API.warn('Alloy.getController() is deprecated, use Alloy.createController() instead.');
	Ti.API.warn('Alloy.getController() will be removed in Alloy 0.4.0');
	return exports.createController(name, args);
}

/**
 * @method createController
 * Factory method for instantiating a controller. Creates and returns an instance of the
 * named controller.
 * @param {String} name Name of controller to instantiate.
 * @param {*...} [args] Arguments to pass to the controller.
 * @return {Alloy.Controller} Alloy controller object.
 */
exports.createController = function(name, args) {
	return new (require('alloy/controllers/' + name))(args);
}

/**
 * @method getModel
 * Factory method for instantiating a Backbone Model object. Creates and returns an instance of the
 * named model.
 *
 * See [Backbone.Model](http://backbonejs.org/#Model) in the Backbone.js documentation for
 * information on the methods and properties provided by the Model object.
 * @param {String} name Name of model to instantiate.
 * @param {*...} [args] Arguments to pass to the model.
 * @return {Backbone.Model} Backbone model object.
 *
 * @deprecated 0.3 Use {Alloy#createModel} instead
 */
exports.getModel = function(name, args) {
	Ti.API.warn('Alloy.getModel() is deprecated, use Alloy.createModel() instead.');
	Ti.API.warn('Alloy.getModel() will be removed in Alloy 0.4.0');
	return exports.createModel(name, args);
}

/**
 * @method createModel
 * Factory method for instantiating a Backbone Model object. Creates and returns an instance of the
 * named model.
 *
 * See [Backbone.Model](http://backbonejs.org/#Model) in the Backbone.js documentation for
 * information on the methods and properties provided by the Model object.
 * @param {String} name Name of model to instantiate.
 * @param {*...} [args] Arguments to pass to the model.
 * @return {Backbone.Model} Backbone model object.
 */
exports.createModel = function(name, args) {
	return new (require('alloy/models/' + ucfirst(name)).Model)(args);
}

/**
 * @method getCollection
 * Factory method for instantiating a Backbone collection of model objects. Creates and returns a
 * collection for holding the named type of model objects.
 *
 * See [Backbone.Collection](http://backbonejs.org/#Collection) in the Backbone.js
 * documentation for  information on the methods and  properties provided by the
 * Collection object.
 * @param {String} name Name of model to hold in this collection.
 * @param {*...} [args] Arguments to pass to the collection.
 * @return {Backbone.Collection} Backbone collection object.
 *
 * @deprecated 0.3 Use {Alloy#createCollection} instead
 */
exports.getCollection = function(name, args) {
	Ti.API.warn('Alloy.getCollection() is deprecated, use Alloy.createCollection() instead.');
	Ti.API.warn('Alloy.getCollection() will be removed in Alloy 0.4.0');
	return exports.createCollection(name, args);
}

/**
 * @method createCollection
 * Factory method for instantiating a Backbone collection of model objects. Creates and returns a
 * collection for holding the named type of model objects.
 *
 * See [Backbone.Collection](http://backbonejs.org/#Collection) in the Backbone.js
 * documentation for  information on the methods and  properties provided by the
 * Collection object.
 * @param {String} name Name of model to hold in this collection.
 * @param {*...} [args] Arguments to pass to the collection.
 * @return {Backbone.Collection} Backbone collection object.
 */
exports.createCollection = function(name, args) {
	return new (require('alloy/models/' + ucfirst(name)).Collection)(args);
}

function isTabletFallback() {
	return !(Math.min(
		Ti.Platform.displayCaps.platformHeight,
		Ti.Platform.displayCaps.platformWidth
	) < 700);
}

/**
 * @property {Boolean} isTablet
 * `true` if the current device is a tablet.
 *
 */
exports.isTablet = (function() {
	if (OS_IOS) {
		return Ti.Platform.osname === 'ipad';
	}
	if (OS_ANDROID) {
		try {
			var psc = require('ti.physicalSizeCategory');
			return psc.physicalSizeCategory === 'large' ||
				   psc.physicalSizeCategory === 'xlarge';
		} catch(e) {
			Ti.API.warn('Could not find ti.physicalSizeCategory module, using fallback for Alloy.isTablet');
			return isTabletFallback();
		}
	}
	if (OS_MOBILEWEB) {
		return !(Math.min(
			Ti.Platform.displayCaps.platformHeight,
			Ti.Platform.displayCaps.platformWidth
		) < 400);
	}

	// Last resort. Don't worry, uglifyjs cleans up this dead code if necessary.
	return isTabletFallback();
})();

/**
 * @property {Boolean} isHandheld
 * `true` if the current device is a handheld device (not a tablet).
 *
 */
exports.isHandheld = !exports.isTablet;

/**
 * @property {Object} globals
 * An object for storing globally accessible variables and functions. Items
 * added to Alloy.globals will be accessible in any controller in your app.
 *
 * @deprecated 0.3.3 Use {Alloy#Globals} instead
 */
exports.globals = {};

/**
 * @property {Object} Globals
 * An object for storing globally accessible variables and functions.
 * Alloy.CFG is accessible in any controller in your app:
 *
 *     Alloy.Globals.someGlobalObject = { key: 'value' };
 *     Alloy.Globals.someGlobalFunction = function(){};
 *
 * Alloy.Globals can be accessed in other non-controller Javascript files
 * like this:
 *
 *     var theObject = require('alloy').Globals.someGlobalObject;
 *
 */
exports.Globals = {};

/**
 * @property {Object} Models
 * An object for storing globally accessible Alloy models. Singleton models
 * created via markup will be stored on this object.
 *
 *     <Model src="myModel"/>
 *
 * The above markup would effectively generate the following code:
 *
 *     Alloy.Models.myModel = Alloy.createModel('MyModel');
 *
 * Alloy.Models.myModel would then be accessible in any controller in your app.
 *
 */
exports.Models = {};

/**
 * @method instance
 * Creates a singleton instance of a Model based on the given model, or
 * returns an existing instance if one has already been created.
 * @param {String} the name of the base model for the Model
 * @return An Alloy Model object singleton
 *
 */
 exports.Models.instance = function(name) {
 	return exports.Models[name] || (exports.Models[name] = exports.createModel(name));
 };

/**
 * @property {Object} Collections
 * An object for storing globally accessible Alloy collections. Singleton collections
 * created via markup will be stored on this object.
 *
 *     <Collection src="myModel"/>
 *
 * The above markup would effectively generate the following code:
 *
 *     Alloy.Collections.myModel = Alloy.createCollection('MyModel');
 *
 * Alloy.Collections.myModel would then be accessible in any controller in your app.
 *
 */
exports.Collections = {};

/**
 * @method instance
 * Creates a singleton instance of a Collection based on the given model, or
 * returns an existing instance if one has already been created.
 * @param {String} the name of the base model for the collection
 * @return An Alloy Collection object singleton
 *
 */
 exports.Collections.instance = function(name) {
 	return exports.Collections[name] || (exports.Collections[name] = exports.createCollection(name));
 };

/**
 * @property {Object} CFG
 * An object that stores Alloy configuration values as defined in your app's
 * app/config.json file. Here's a what a typical config.json file might look
 * like in an Alloy app.
 *
 *     {
 *         "global": { "key": "defaultValue", "anotherKey": 12345 },
 *         "env:development": {},
 *         "env:test": {},
 *         "env:production": {},
 *         "os:ios": { "key": "iosValue" },
 *         "os:android": { "key": "androidValue" },
 *         "dependencies": {}
 *     }
 *
 * If this app was compiled for iOS, the Alloy.CFG would look like this:
 *
 *     Alloy.CFG = {
 *         "key": "iosValue",
 *         "anotherKey": 12345
 *     }
 *
 * Alloy.CFG is accessible in any controller in your app, and can be accessed
 * in other non-controller Javascript files like this:
 *
 *     var theKey = require('alloy').CFG.key;
 *
 */
exports.CFG = require('alloy/CFG');

if (OS_ANDROID) {
	exports.Android = {};
	exports.Android.menuItemCreateArgs = ['itemId','groupId','title','order'];
}
<|endoftext|>