<commit_before>'use strict'
const subscribe = require('vigour-state/subscribe')
const s = require('vigour-state/s')

// https://github.com/Matt-Esch/virtual-dom/issues/371 <-- hahahaha! wining all
require('./style.less')
console.time('START')
// -------------------------
const raf = window.requestAnimationFrame
const isNumber = require('vigour-util/is/number')
// -------------------------
const state = s({ name: 'trees' })
const obj = {}

const amount = 2000

for (var i = 0; i < amount; i++) { obj[i] = { title: i } }
state.set({
  collection: obj,
  ms: {
    $transform (val) {
      return isNumber(val) ? Math.round(val) : 'not measured'
    },
    $add: ' ms'
  },
  settings: {}
})
// // -------------------------

const operator = require('vigour-observable/lib/operator/constructor').prototype
operator.set({
  properties: { $: true },
  inject: require('../lib/map')
})

const Element = require('../lib/element')
const svgNS = 'http://www.w3.org/2000/svg'

var app = new Element({
  key: 'app',
  holder: {
    init: {
      text: { $: 'first', $add: ' ms initial render' }
    },
    ms: {
      text: {
        $: 'ms',
        $transform (val) {
          return isNumber(val) ? Math.round(val) : 'not measured'
        },
        $add: ' ms periodic updates'
      }
    },
    elems: {
      text: { $: 'elems', $add: ' dom-nodes' }
    }
  },
  main: {
    // holder2: {
    //   $: 'collection',
    //   $any: true,
    //   // css: null,
    //   namespace: svgNS,
    //   node: 'svg',
    //   attr: {
    //     width: 1000,
    //     height: 1000
    //   },
    //   Child: { // if you reuse here stuff here as a Child uid is not enough!
    //     namespace: svgNS,
    //     node: 'circle',
    //     // css: null,
    //     attr: {
    //       cx: {
    //         $: 'title',
    //         $transform (val) { return Math.sin(val / 30) * (val / 5) + 250 }
    //       }, // 50,
    //       cy: {
    //         $: 'title', $transform (val) { return Math.cos(val / 30) * (val / 5) + 250 }
    //       }, // 50,
    //       r: 10,
    //       'stroke-width': 1,
    //       fill: 'red',
    //       stroke: 'black'
    //     }
    //     // $transform () {
    //     // ambitious but doable -- do this later
    //     // hard parts -- needs to add the stuff to subscriptions
    //     // same for 'property definitions (although that can be an operator'
    //     // now there is no way to switch etc
    //     //   return {
    //     //     text: { $: 'title' }
    //     //   }
    //     // },
    //     // text: { $: 'title' }
    //   }
    // },
    // holder3: {
    //   $: 'collection',
    //   $any: true,
    //   Child: {
    //     css: 'weirdChild',
    //     text: { $: 'title' }
    //   }
    // },
    holder: {
      $: 'collection',
      $any: true,
      Child: {
        css: 'nestchild',
        on: {
          remove (val, stamp, node) {
            console.log('FIRE REMOVE:', val, stamp, node)
          }
        },
        star: {},
        something: {
          a: {
            b: {
              c: {
                text: 'haha'
              }
            }
          }
        },
        title: {
          text: { $: 'title' }
        },
        // more: {
        //   text: { $: '$root.ms' } -- root is not yet supported (needs some minor revisions)
        // },
        header: {
          a: {
            bla: {
              // $: true,
              // $: 'title',
              x: {
                text: { $: 'x', $prepend: 'x:' }
              },
              lastname: {
                text: {
                  $: 'title.lastname',
                  $prepend: 'lname: '
                }
              }
            },
            text: {
              $: 'title',
              // $prepend: 'h:',
              $transform (val) {
                return val
              }
            }
          }
        }
      }
    }
  }
  // menu: {
  //   // this needs to be rendered of course -- even if there is no data -- else its pretty strange
  //   // for now we cna work arround this (leave it!) but alter we need to change this
  //   // it just weird that if there is a state it allways takes over and takes care of the handeling
  //   button: { text: 'a button' },
  //   settings: {
  //     $: 'settings',
  //     button: { text: { $: 'languages' } }
  //   }
  // },
  // footer: {
  //   left: { text: 'on the left' },
  //   right: { text: 'on the right' }
  // }
}, false)

var subs = app.$map()

var render = require('../lib/render').multiple

var tree = { parent: true }
// need to do initial render as well

console.timeEnd('START')

setTimeout(function () {

  var ms = Date.now()
  render.call(state, 'new', state._lstamp, subs, tree, void 0, tree)
  tree = subscribe(state, subs, function (type, stamp, subs, ctree, ptree) {
    // console.group()
    // console.log('FIRE', this.path(), type, subs)
    // console.log('tree:', tree)
    // console.log('ptree:', ptree)
    if (subs._) {
      render.call(this, type, stamp, subs, ctree, ptree, tree)
    } else {
      console.warn('no _ ?', this.path())
    }
    // console.groupEnd()
    // render.f
  }, tree)
  // unsubscribe for more tests
  document.body.appendChild(tree._[app.uid()])
  state.set({ first: Date.now() - ms })

  // -------------------------
  console.log('subs:', subs)
  // -------------------------

  global.state = state
  global.tree = tree
  global.subs = subs
  // console.log(tree._[app.uid()])
  var cnt = 0
  var total = 0
  function loop () {
    cnt++
    var ms = Date.now()
    var obj = {}
    for (var i = 0; i < amount; i++) {
      // obj[i] = { title: i + cnt }
      obj[i] = {
        title: { val: i + cnt, lastname: i + cnt },
        x: i + cnt
      }
    }
    state.collection.set(obj)
    if (!state.first) {
      state.set({ first: Date.now() - ms })
    } else {
      total += (Date.now() - ms)
      state.ms.set(total / cnt)
    }
    // if (cnt < 10) {
    raf(loop)
    // }
  }

  state.collection[0].remove()

  loop()

  console.log('----------------------------')
  console.log('tree:', tree)
  state.set({ elems: document.getElementsByTagName('*').length })
  // if i do this correctly dont need parent ever -- just need to store
  // element and then find it by checking parent yes better
  // document.appendChild(elem)
  // tree.node or something...
})
<commit_msg>simple benchmark<commit_after>'use strict'
const subscribe = require('vigour-state/subscribe')
const s = require('vigour-state/s')

// https://github.com/Matt-Esch/virtual-dom/issues/371 <-- hahahaha! wining all
require('./style.less')
console.time('START')
// -------------------------
const raf = window.requestAnimationFrame
const isNumber = require('vigour-util/is/number')
// -------------------------
const state = s({ name: 'trees' })
const obj = {}

const amount = 2500

for (var i = 0; i < amount; i++) { obj[i] = { title: i } }
state.set({
  collection: obj,
  ms: {
    $transform (val) {
      return isNumber(val) ? Math.round(val) : 'not measured'
    },
    $add: ' ms'
  },
  settings: {}
})
// // -------------------------

const operator = require('vigour-observable/lib/operator/constructor').prototype
operator.set({
  properties: { $: true },
  inject: require('../lib/map')
})

const Element = require('../lib/element')
const svgNS = 'http://www.w3.org/2000/svg'

var app = new Element({
  key: 'app',
  holder: {
    init: {
      text: { $: 'first', $add: ' ms initial render' }
    },
    ms: {
      text: {
        $: 'ms',
        $transform (val) {
          return isNumber(val) ? Math.round(val) : 'not measured'
        },
        $add: ' ms periodic updates'
      }
    },
    elems: {
      text: { $: 'elems', $add: ' dom-nodes' }
    }
  },
  main: {
    // holder2: {
    //   $: 'collection',
    //   $any: true,
    //   // css: null,
    //   namespace: svgNS,
    //   node: 'svg',
    //   attr: {
    //     width: 1000,
    //     height: 1000
    //   },
    //   Child: { // if you reuse stuff here as a Child uid is not enough!
    //     namespace: svgNS, // this will become a prop ofcourse
    //     node: 'circle',
    //     // css: null,
    //     attr: {
    //       cx: {
    //         $: 'title',
    //         $transform (val) { return Math.sin(val / 30) * (val / 2) + 250 }
    //       }, // 50,
    //       cy: {
    //         $: 'title', $transform (val) { return Math.cos(val / 30) * (val / 2) + 250 }
    //       }, // 50,
    //       r: { $: 'title', $transform (val) { return val/50 + 1 } },
    //       'stroke-width': 1,
    //       fill: 'red',
    //       stroke: 'black'
    //     }
    //     // $transform () {
    //     // ambitious but doable -- do this later
    //     // hard parts -- needs to add the stuff to subscriptions
    //     // same for 'property definitions (although that can be an operator'
    //     // now there is no way to switch etc
    //     //   return {
    //     //     text: { $: 'title' }
    //     //   }
    //     // },
    //     // text: { $: 'title' }
    //   }
    // },
    holder3: {
      $: 'collection',
      $any: true,
      Child: {
        css: 'weirdChild',
        text: { $: 'title' }
      }
    },
    holder: {
      // $: 'collection',
      $any: true,
      Child: {
        css: 'nestchild',
        on: {
          remove (val, stamp, node) {
            console.log('FIRE REMOVE:', val, stamp, node)
          }
        },
        star: {},
        something: {
          a: {
            b: {
              c: {
                text: 'haha'
              }
            }
          }
        },
        title: {
          text: { $: 'title' }
        },
        // more: {
        //   text: { $: '$root.ms' } -- root is not yet supported (needs some minor revisions)
        // },
        header: {
          a: {
            bla: {
              // $: true,
              // $: 'title',
              x: {
                text: { $: 'x', $prepend: 'x:' }
              },
              lastname: {
                text: {
                  $: 'title.lastname',
                  $prepend: 'lname: '
                }
              }
            },
            text: {
              $: 'title',
              // $prepend: 'h:',
              $transform (val) {
                return val
              }
            }
          }
        }
      }
    }
  }
  // menu: {
  //   // this needs to be rendered of course -- even if there is no data -- else its pretty strange
  //   // for now we cna work arround this (leave it!) but alter we need to change this
  //   // it just weird that if there is a state it allways takes over and takes care of the handeling
  //   button: { text: 'a button' },
  //   settings: {
  //     $: 'settings',
  //     button: { text: { $: 'languages' } }
  //   }
  // },
  // footer: {
  //   left: { text: 'on the left' },
  //   right: { text: 'on the right' }
  // }
}, false)

var subs = app.$map()

var render = require('../lib/render').multiple

var tree = { parent: true }
// need to do initial render as well

console.timeEnd('START')

setTimeout(function () {

  var ms = Date.now()
  render.call(state, 'new', state._lstamp, subs, tree, void 0, tree)
  tree = subscribe(state, subs, function (type, stamp, subs, ctree, ptree) {
    // console.group()
    // console.log('FIRE', this.path(), type, subs)
    // console.log('tree:', tree)
    // console.log('ptree:', ptree)
    if (subs._) {
      render.call(this, type, stamp, subs, ctree, ptree, tree)
    } else {
      console.warn('no _ ?', this.path())
    }
    // console.groupEnd()
    // render.f
  }, tree)
  // unsubscribe for more tests
  document.body.appendChild(tree._[app.uid()])
  state.set({ first: Date.now() - ms })

  // -------------------------
  console.log('subs:', subs)
  // -------------------------

  global.state = state
  global.tree = tree
  global.subs = subs
  // console.log(tree._[app.uid()])
  var cnt = 0
  var total = 0
  function loop () {
    cnt++
    var ms = Date.now()
    var obj = {}
    for (var i = 0; i < amount; i++) {
      obj[i] = { title: i + cnt }
      // obj[i] = {
      //   title: { val: i + cnt, lastname: i + cnt },
      //   x: i + cnt
      // }
    }
    state.collection.set(obj)
    if (!state.first) {
      state.set({ first: Date.now() - ms })
    } else {
      total += (Date.now() - ms)
      state.ms.set(total / cnt)
    }
    // if (cnt < 10) {
    raf(loop)
    // }
  }

  state.collection[0].remove()

  loop()

  console.log('----------------------------')
  console.log('tree:', tree)
  state.set({ elems: document.getElementsByTagName('*').length })
  // if i do this correctly dont need parent ever -- just need to store
  // element and then find it by checking parent yes better
  // document.appendChild(elem)
  // tree.node or something...
})
<|endoftext|>


<commit_before>import {
    START_RECORDING_WORKOUT,
    STOP_RECORDING_WORKOUT,
    DISMISS_WORKOUT_VIDEO_RECORDER,
    SAVE_WORKOUT_VIDEO,
    SAVE_VIDEO_ERROR,
} from 'app/ActionTypes';
import * as DurationsSelectors from 'app/redux/selectors/DurationsSelectors';
import * as Analytics from 'app/services/Analytics';
import * as SetsSelectors from 'app/redux/selectors/SetsSelectors';

export const startRecording = (setID) => (dispatch, getState) => {
    const state = getState();
    logStartRecordingVideoAnalytics(setID, state);

    dispatch({
        type: START_RECORDING_WORKOUT,
        setID: setID
    });
};

export const stopRecording = () => ({
    type: STOP_RECORDING_WORKOUT
});

export const dismissRecording = () => (dispatch, getState) => {
    const state = getState();
    logCancelRecordVideoAnalytics(state);
    Analytics.setCurrentScreen('workout');

    dispatch({
        type: DISMISS_WORKOUT_VIDEO_RECORDER
    });
};

export const saveVideo = (setID, videoFileURL, videoType) => (dispatch, getState) => {
    const state = getState();
    logSaveVideoAnalytics(state);

    dispatch({
        type: SAVE_WORKOUT_VIDEO,
        setID: setID,
        videoFileURL: videoFileURL,
        videoType: videoType
    });
};

export const saveVideoError = (setID) => (dispatch, getState) => {
    const state = getState();
    logSaveVideoErrorAnalytics(state);

    dispatch({
        type: SAVE_VIDEO_ERROR,
    });
};

const logStartRecordingVideoAnalytics = (setID, state) => {
    const is_working_set = SetsSelectors.getIsWorkingSet(state, setID);

    Analytics.logEventWithAppState('start_recording_video', {
        is_working_set: is_working_set,
    }, state);
};

const logSaveVideoAnalytics = (state) => {
    const duration = DurationsSelectors.getWorkoutVideoRecorderDuration(state);
    const is_working_set = SetsSelectors.getIsWorkingSet(state, setID);

    Analytics.logEventWithAppState('save_video', {
        duration: duration,
        is_working_set: is_working_set,
    }, state);
};

const logCancelRecordVideoAnalytics = (state) => {
    const duration = DurationsSelectors.getWorkoutVideoRecorderDuration(state);
    const is_working_set = SetsSelectors.getIsWorkingSet(state, setID);

    Analytics.logEventWithAppState('cancel_record_video', {
        duration: duration,
        is_working_set: is_working_set,
    }, state);
};

const logSaveVideoErrorAnalytics = (state) => {
    const duration = DurationsSelectors.getWorkoutVideoRecorderDuration(state);
    const is_working_set = SetsSelectors.getIsWorkingSet(state, setID);

    Analytics.logEventWithAppState('save_video_error', {
        duration: duration,
        is_working_set: is_working_set,
    }, state);
};
<commit_msg>fixed workout video recorder crashes from analytics<commit_after>import {
    START_RECORDING_WORKOUT,
    STOP_RECORDING_WORKOUT,
    DISMISS_WORKOUT_VIDEO_RECORDER,
    SAVE_WORKOUT_VIDEO,
    SAVE_VIDEO_ERROR,
} from 'app/ActionTypes';
import * as DurationsSelectors from 'app/redux/selectors/DurationsSelectors';
import * as Analytics from 'app/services/Analytics';
import * as SetsSelectors from 'app/redux/selectors/SetsSelectors';

export const startRecording = (setID) => (dispatch, getState) => {
    const state = getState();
    logStartRecordingVideoAnalytics(setID, state);

    dispatch({
        type: START_RECORDING_WORKOUT,
        setID: setID
    });
};

export const stopRecording = () => ({
    type: STOP_RECORDING_WORKOUT
});

export const dismissRecording = () => (dispatch, getState) => {
    const state = getState();
    logCancelRecordVideoAnalytics(setID, state);
    Analytics.setCurrentScreen('workout');

    dispatch({
        type: DISMISS_WORKOUT_VIDEO_RECORDER
    });
};

export const saveVideo = (setID, videoFileURL, videoType) => (dispatch, getState) => {
    const state = getState();
    logSaveVideoAnalytics(setID, state);

    dispatch({
        type: SAVE_WORKOUT_VIDEO,
        setID: setID,
        videoFileURL: videoFileURL,
        videoType: videoType
    });
};

export const saveVideoError = (setID) => (dispatch, getState) => {
    const state = getState();
    logSaveVideoErrorAnalytics(setID, state);

    dispatch({
        type: SAVE_VIDEO_ERROR,
    });
};

const logStartRecordingVideoAnalytics = (setID, state) => {
    const is_working_set = SetsSelectors.getIsWorkingSet(state, setID);

    Analytics.logEventWithAppState('start_recording_video', {
        is_working_set: is_working_set,
    }, state);
};

const logSaveVideoAnalytics = (setID, state) => {
    const duration = DurationsSelectors.getWorkoutVideoRecorderDuration(state);
    const is_working_set = SetsSelectors.getIsWorkingSet(state, setID);

    Analytics.logEventWithAppState('save_video', {
        duration: duration,
        is_working_set: is_working_set,
    }, state);
};

const logCancelRecordVideoAnalytics = (setID, state) => {
    const duration = DurationsSelectors.getWorkoutVideoRecorderDuration(state);
    const is_working_set = SetsSelectors.getIsWorkingSet(state, setID);

    Analytics.logEventWithAppState('cancel_record_video', {
        duration: duration,
        is_working_set: is_working_set,
    }, state);
};

const logSaveVideoErrorAnalytics = (setID, state) => {
    const duration = DurationsSelectors.getWorkoutVideoRecorderDuration(state);
    const is_working_set = SetsSelectors.getIsWorkingSet(state, setID);

    Analytics.logEventWithAppState('save_video_error', {
        duration: duration,
        is_working_set: is_working_set,
    }, state);
};
<|endoftext|>


<commit_before>if (!process.env.token) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var Botkit = require('./lib/Botkit.js'),
    os = require('os'),
    controller = Botkit.slackbot({
        //debug: true
    }),
    bot = controller.spawn({
        token: process.env.token
    }).startRTM(),
    request = require('request-promise'),
    Promise = require('bluebird');

controller.setupWebserver(process.env.PORT, function(err, webserver) {
  controller.createWebhookEndpoints(controller.webserver, 'PsRh1Hn3lbVjQpYtf3UaLwKH');
});

var games = {},
    suitMappings = {'HEARTS': 'red', 'SPADES': 'green', 'CLUBS': 'yellow', 'DIAMONDS': 'blue'},
    valueMappings = {'JACK': 'Draw 2', 'QUEEN': 'Skip', 'KING': 'Reverse'};

//TODO: Allow for commands via @mentions as well

controller.hears('new', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    initializeGame(bot, message);
});

controller.hears('join', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    joinGame(bot, message);
});

controller.hears('quit', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    quitGame(bot, message);
});

controller.hears('order', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    reportTurnOrder(bot, message, true);
});

controller.hears('setup', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    for (var i = 2; i <= 5; i++){
        var mockUser = 'Player' + i;

        joinGame(bot, message, mockUser);
    }
});

controller.hears('start', ['slash_command'], function(bot, message){
    console.log(message);
    beginGame(bot, message);
});

function beginGame(bot, message){
    var user = message.user_name,
        game = getGame(bot, message);

    if (!game){
        return;
    }

    if (game.player1 !== user){
        console.log(game);
        bot.replyPrivate(message, 'Only player 1 (' + game.player1 + ') can start the game.');
        return;
    }

    console.log(Object.keys(game.players).length + ' players at beginning of game.');

    if (Object.keys(game.players).length < 2){
        console.log(game);
        bot.replyPrivate(message, 'You need at least two players to begin playing.');
        return;
    }

    if (game.started){
        console.log(game);
        bot.replyPrivate(message, 'The game is already started.');
        reportTurnOrder(bot, message, true, true);
        return;
    }

    game.started = true;

    //Create the deck
    var deckRequest = request('http://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=2')
        .then(function(result){
            console.log(result);

            game.deckId = result.deck_id;

            console.log(game);

            announceTurn(bot, message);
        });

    Promise.all([deckRequest]);

    var drawRequests = [];

    //Deal to each of the players
    for (playerName in game.players){
        var player = game.players[playerName];
        var cardRequest = request('http://deckofcardsapi.com/api/deck/' + game.deckId + '/draw/?count=7')
            .then(function(result){
                console.log(result);

                for (var j = 0; j < result.cards.length; j++){
                    player.cards[j] = getUnoCard(result.cards[j]);
                }
            });

        drawRequests.push(cardRequest);
    }

    Promise.all(drawRequests);

    announceTurn(bot, message);
}

function getUnoCard(standardCard){
    var value = valueMappings[standardCard.value] || (standardCard.value - 1),
        color = suitMappings[standardCard.suit];

    if (value === 'ACE'){
        color = 'Wild';
        switch (standardCard.suit){
            case 'CLUBS':
            case 'SPADES':
                value = 'Wild';
                break;
            case 'HEARTS':
            case 'DIAMONDS':
                value = 'Draw 4';
                break;
        }
    }

    return {
        color: color,
        value: value
    }
}

function getStandardCard(unoCard){

}

function announceTurn(bot, message){
    var game = getGame(bot, message);

    bot.replyPublicDelayed(message, 'It is ' + game.turnOrder[0] + '\'s turn.\nType \\uno cards, \\uno draw, \\uno skip or \\uno play.')
}

function quitGame(bot, message){
    var user = message.user_name,
        game = getGame(bot, message),
        channel = message.channel;

    if (!game.players[user]){
        bot.replyPrivate(message, 'You weren\'t playing to begin with.');
        return;
    }

    delete game.players[user];

    var player = game.turnOrder.indexOf(user);
    game.turnOrder.splice(player, 1);

    bot.replyPublic(message, user + ' has left the game.');

    if (Object.keys(game.players).length === 0){
        bot.replyPublicDelayed(message, 'No more players. Ending the game.');
        games[channel] = newGame();
        return;
    }

    if (game.player1 === user){
        game.player1 = Object.keys(game.players)[0];
        bot.replyPublicDelayed(message, game.player1 + ' is the new player 1.');
    }

    if (Object.keys(game.players).length === 1){
        game.started = false;
        bot.replyPublicDelayed(message, 'Only one player remaining. Waiting for more players.');
    }

    reportTurnOrder(bot, message);
}

function joinGame(bot, message, userName){
    var user = userName || message.user_name,
        channel = message.channel;

    var game = getGame(bot, message);

    if (!game){
        return;
    }

    if (game.players[user]){
        bot.replyPrivate(message, user + ', you\'ve already joined the game!');
        return;
    }

    game.players[user] = {
        hand: []
    };
    game.turnOrder.push(user);

    bot.replyPublic(message, user + ' has joined the game.');

    reportTurnOrder(bot, message);
}

function getGame(bot, message, suppressReport){
    var channel = message.channel;

    if (!games[channel] || !games[channel].initialized){
        if (!suppressReport)
        {
            bot.replyPrivate(message, 'There is no game yet.');
        }
        return undefined;
    }

    return games[channel];
}

function reportTurnOrder(bot, message, isPrivate, isDelayed){
    var game = getGame(bot, message);

    if (!game){
        return;
    }

    var currentOrder = '';

    for (var i = 1; i < game.turnOrder.length + 1; i++){
        if (i > 1){
            currentOrder = currentOrder + ', ';
        }

        currentOrder = currentOrder + '\n' + i + '. ' + game.turnOrder[i - 1];
    }

    if (isPrivate){
        if (isDelayed){
            bot.replyPrivateDelayed(message, 'Current playing order:\n' + currentOrder);
        } else{
            bot.replyPrivate(message, 'Current playing order:\n' + currentOrder);
        }
    }
    else{
        bot.replyPublicDelayed(message, 'Current playing order:\n' + currentOrder);
    }
}

function initializeGame(bot, message){
    var user = message.user_name,
        channel = message.channel;

    var game = getGame(bot, message, true);

    if (!game){
        game = newGame();

        games[channel] = game;
    }

    if (game.initialized){
        bot.replyPrivate(message, 'There is already an uno game in progress. Type !join to join the game.');
        return;
    }

    game = newGame();
    games[channel] = game;

    game.initialized = true;
    game.player1 = user;
    game.players[user] = {
        hand: []
    };
    game.turnOrder.push(user);

    bot.replyPublic(message, user + ' has started UNO. Type !join to join the game.');

    reportTurnOrder(bot, message);
};

function newGame(){
    return {
        initialized: false,
        started: false,
        players: [],
        deckId: '',
        turnOrder: []
    };
}

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':robot_face: I am a bot named <@' + bot.identity.name +
             '>. I have been running for ' + uptime + ' on ' + hostname + '.');

    });

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<commit_msg>Added debugging messages to help with promises<commit_after>if (!process.env.token) {
    console.log('Error: Specify token in environment');
    process.exit(1);
}

var Botkit = require('./lib/Botkit.js'),
    os = require('os'),
    controller = Botkit.slackbot({
        //debug: true
    }),
    bot = controller.spawn({
        token: process.env.token
    }).startRTM(),
    request = require('request-promise'),
    Promise = require('bluebird');

controller.setupWebserver(process.env.PORT, function(err, webserver) {
  controller.createWebhookEndpoints(controller.webserver, 'PsRh1Hn3lbVjQpYtf3UaLwKH');
});

var games = {},
    suitMappings = {'HEARTS': 'red', 'SPADES': 'green', 'CLUBS': 'yellow', 'DIAMONDS': 'blue'},
    valueMappings = {'JACK': 'Draw 2', 'QUEEN': 'Skip', 'KING': 'Reverse'};

//TODO: Allow for commands via @mentions as well

controller.hears('new', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    initializeGame(bot, message);
});

controller.hears('join', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    joinGame(bot, message);
});

controller.hears('quit', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    quitGame(bot, message);
});

controller.hears('order', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    reportTurnOrder(bot, message, true);
});

controller.hears('setup', ['slash_command'/*, 'direct_mention', 'mention'*/], function(bot, message){
    console.log(message);
    for (var i = 2; i <= 5; i++){
        var mockUser = 'Player' + i;

        joinGame(bot, message, mockUser);
    }
});

controller.hears('start', ['slash_command'], function(bot, message){
    console.log(message);
    beginGame(bot, message);
});

function beginGame(bot, message){
    var user = message.user_name,
        game = getGame(bot, message);

    if (!game){
        return;
    }

    if (game.player1 !== user){
        console.log(game);
        bot.replyPrivate(message, 'Only player 1 (' + game.player1 + ') can start the game.');
        return;
    }

    console.log(Object.keys(game.players).length + ' players at beginning of game.');

    if (Object.keys(game.players).length < 2){
        console.log(game);
        bot.replyPrivate(message, 'You need at least two players to begin playing.');
        return;
    }

    if (game.started){
        console.log(game);
        bot.replyPrivate(message, 'The game is already started.');
        reportTurnOrder(bot, message, true, true);
        return;
    }

    game.started = true;

    //Create the deck
    var deckRequest = request('http://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=2')
        .then(function(result){
            console.log(result);
            game.deckId = result.deck_id;
        }).then(function(){
            console.log('Deck request finished');
        });

    Promise.all([deckRequest]);

    console.log('Deck request should be finished by now.');

    var drawRequests = [];

    //Deal to each of the players
    for (playerName in game.players){
        var player = game.players[playerName];
        var cardRequest = request('http://deckofcardsapi.com/api/deck/' + game.deckId + '/draw/?count=7')
            .then(function(result){
                console.log(result);

                for (var j = 0; j < result.cards.length; j++){
                    player.cards[j] = getUnoCard(result.cards[j]);
                }
            })
            .then(function(){
                console.log('Draw request finished');
            });

        drawRequests.push(cardRequest);
    }

    Promise.all(drawRequests);
    console.log('All draw requests should be finished by now.');

    console.log(game);
    announceTurn(bot, message);
}

function getUnoCard(standardCard){
    var value = valueMappings[standardCard.value] || (standardCard.value - 1),
        color = suitMappings[standardCard.suit];

    if (value === 'ACE'){
        color = 'Wild';
        switch (standardCard.suit){
            case 'CLUBS':
            case 'SPADES':
                value = 'Wild';
                break;
            case 'HEARTS':
            case 'DIAMONDS':
                value = 'Draw 4';
                break;
        }
    }

    return {
        color: color,
        value: value
    }
}

function getStandardCard(unoCard){

}

function announceTurn(bot, message){
    var game = getGame(bot, message);

    bot.replyPublicDelayed(message, 'It is ' + game.turnOrder[0] + '\'s turn.\nType \\uno cards, \\uno draw, \\uno skip or \\uno play.')
}

function quitGame(bot, message){
    var user = message.user_name,
        game = getGame(bot, message),
        channel = message.channel;

    if (!game.players[user]){
        bot.replyPrivate(message, 'You weren\'t playing to begin with.');
        return;
    }

    delete game.players[user];

    var player = game.turnOrder.indexOf(user);
    game.turnOrder.splice(player, 1);

    bot.replyPublic(message, user + ' has left the game.');

    if (Object.keys(game.players).length === 0){
        bot.replyPublicDelayed(message, 'No more players. Ending the game.');
        games[channel] = newGame();
        return;
    }

    if (game.player1 === user){
        game.player1 = Object.keys(game.players)[0];
        bot.replyPublicDelayed(message, game.player1 + ' is the new player 1.');
    }

    if (Object.keys(game.players).length === 1){
        game.started = false;
        bot.replyPublicDelayed(message, 'Only one player remaining. Waiting for more players.');
    }

    reportTurnOrder(bot, message);
}

function joinGame(bot, message, userName){
    var user = userName || message.user_name,
        channel = message.channel;

    var game = getGame(bot, message);

    if (!game){
        return;
    }

    if (game.players[user]){
        bot.replyPrivate(message, user + ', you\'ve already joined the game!');
        return;
    }

    game.players[user] = {
        hand: []
    };
    game.turnOrder.push(user);

    bot.replyPublic(message, user + ' has joined the game.');

    reportTurnOrder(bot, message);
}

function getGame(bot, message, suppressReport){
    var channel = message.channel;

    if (!games[channel] || !games[channel].initialized){
        if (!suppressReport)
        {
            bot.replyPrivate(message, 'There is no game yet.');
        }
        return undefined;
    }

    return games[channel];
}

function reportTurnOrder(bot, message, isPrivate, isDelayed){
    var game = getGame(bot, message);

    if (!game){
        return;
    }

    var currentOrder = '';

    for (var i = 1; i < game.turnOrder.length + 1; i++){
        if (i > 1){
            currentOrder = currentOrder + ', ';
        }

        currentOrder = currentOrder + '\n' + i + '. ' + game.turnOrder[i - 1];
    }

    if (isPrivate){
        if (isDelayed){
            bot.replyPrivateDelayed(message, 'Current playing order:\n' + currentOrder);
        } else{
            bot.replyPrivate(message, 'Current playing order:\n' + currentOrder);
        }
    }
    else{
        bot.replyPublicDelayed(message, 'Current playing order:\n' + currentOrder);
    }
}

function initializeGame(bot, message){
    var user = message.user_name,
        channel = message.channel;

    var game = getGame(bot, message, true);

    if (!game){
        game = newGame();

        games[channel] = game;
    }

    if (game.initialized){
        bot.replyPrivate(message, 'There is already an uno game in progress. Type !join to join the game.');
        return;
    }

    game = newGame();
    games[channel] = game;

    game.initialized = true;
    game.player1 = user;
    game.players[user] = {
        hand: []
    };
    game.turnOrder.push(user);

    bot.replyPublic(message, user + ' has started UNO. Type !join to join the game.');

    reportTurnOrder(bot, message);
};

function newGame(){
    return {
        initialized: false,
        started: false,
        players: [],
        deckId: '',
        turnOrder: []
    };
}

controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'],
    'direct_message,direct_mention,mention', function(bot, message) {

        var hostname = os.hostname();
        var uptime = formatUptime(process.uptime());

        bot.reply(message,
            ':robot_face: I am a bot named <@' + bot.identity.name +
             '>. I have been running for ' + uptime + ' on ' + hostname + '.');

    });

function formatUptime(uptime) {
    var unit = 'second';
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'minute';
    }
    if (uptime > 60) {
        uptime = uptime / 60;
        unit = 'hour';
    }
    if (uptime != 1) {
        unit = unit + 's';
    }

    uptime = uptime + ' ' + unit;
    return uptime;
}
<|endoftext|>


<commit_before>
// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("hello", function(request, response) {
  response.success("Hello world!");
});
<commit_msg>Update main.js<commit_after>
// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
//Parse.Cloud.define("hello", function(request, response) {
  //response.success("Hello world!");
//});

Parse.Cloud.job("dropData", function(request, status) {
    Parse.Cloud.useMasterKey();
    ParseQuery<ParseObject> query = new ParseQuery<ParseObject>("Reviews");
        query.findInBackground(new FindCallback<ParseObject>() {
        public void done(List<ParseObject> reviews, ParseException e) {
            if (e == null) {
                try {
                   ParseObject.delete(reviews);
                }catch(ParseException pe) { pe.printStackTrace(); }

                // OR (do not use both!)

                // iterate over all messages and delete them
                for(ParseObject review : reviews)
                {
                     review.deleteEventually();
                }
            } else {
                Log.d("Semothing went wrong. ", e.getMessage());
            }
    }
});

}
<|endoftext|>


<commit_before>/*
 * @author              Mitchell Field <mitchell.field@live.com.au>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

const DISCARDABLE_TAB_URLS = new Set([
  "about:blank",
  "about:newtab",
  "about:privatebrowsing"
]);

const SORT_MODES = new Map([
  ["none", 0],
  ["host_title_path", 1],
  ["host_path_title", 2],
  ["title_host_path", 3]
]);

const DEFAULT_PREFS = {
  "pref_tabs_deduplicate": "false",
  "pref_tabs_sort_by_parts": "none",
  "pref_tabs_sort_by_query_string": "true"
};

const PREFS = Object.assign(DEFAULT_PREFS);

// Get "sync" storage contents.
browser.storage.sync.get().then((storedObject) => {

  // Merge preferences with preferences from storage.
  Object.assign(PREFS, DEFAULT_PREFS, storedObject.preferences);
  updateUI();
});

// Set browser action.
browser.browserAction.onClicked.addListener(onBrowserAction);

// Listen to changes in storage.
browser.storage.onChanged.addListener(onStorageChanged);

const processingWindows = new Set();

const hostnameTokenCache = new Map();
const pathnameTokenCache = new Map();

function TabProps(tab) {
  const { id, index, status, title, url, windowId } = tab;

  const {
    protocol = ':',
    hostname = '',
    pathname = '/',
    searchParams = '',
    hash = '#'
  } = new URL(url || '');

  Object.assign(this, {
    _lowerDomainTokens: null,
    _pathnameTokens: null,
    _tldTokens: null,
    hasAboutScheme: protocol === "about:",
    hasPathname: pathname !== '/',
    hash,
    hostname,
    id,
    index,
    isDiscardable: DISCARDABLE_TAB_URLS.has(url),
    isDuplicate: false,
    pathname,
    queryString: searchParams.toString(),
    status,
    tab,
    title: title || '',
    url,
    windowId
  });
}

TabProps.prototype = {
  get lowerDomainTokens() {
    if (!this._lowerDomainTokens) {
      const hostnameTokens = splitHostname(this.hostname, this.windowId);
      this._tldTokens = hostnameTokens[0];
      this._lowerDomainTokens = hostnameTokens[1];
    }
    return this._lowerDomainTokens;
  },
  get pathnameTokens() {
    if (!this._pathnameTokens) {
      this._pathnameTokens = splitPathname(this.pathname, this.windowId);
    }
    return this._pathnameTokens;
  },
  get tldTokens() {
    if (!this._tldTokens) {
      const hostnameTokens = splitHostname(this.hostname, this.windowId);
      this._tldTokens = hostnameTokens[0];
      this._lowerDomainTokens = hostnameTokens[1];
    }
    return this._tldTokens;
  }
}

/*
 * Called when the "browser action" is invoked.
 */
function onBrowserAction(tab, onClickData) {
  const sort = PREFS.pref_tabs_sort_by_parts !== "none";
  const deduplicate = PREFS.pref_tabs_deduplicate === "true";

  if (!sort && !deduplicate) {

    // The browser action is not configured to sort nor deduplicate tabs.
    browser.runtime.openOptionsPage();
    return;
  }

  processTabs(tab.windowId, sort, deduplicate);
}

/*
 * Called when a storage area is changed.
 */
function onStorageChanged(changes, areaName) {
  if (areaName !== "sync" || !("preferences" in changes)) {
    return;
  }

  Object.assign(PREFS, changes.preferences.newValue);
  updateUI();
}

/*
 * Updates UI elements such as titles and descriptions.
 */
function updateUI() {
  let sort = PREFS.pref_tabs_sort_by_parts !== "none";
  let deduplicate = PREFS.pref_tabs_deduplicate === "true";

  // Default browser action title.
  let titleID = "browser_action_none_label";
  let shortcutDescriptionID = "browser_action_shortcut_none_label";

  if (sort) {
    if (deduplicate) {

      // Sort and deduplicate.
      titleID = "browser_action_sort_deduplicate_label";
      shortcutDescriptionID = "browser_action_shortcut_sort_deduplicate_label";
    } else {

      // Sort.
      titleID = "browser_action_sort_label";
      shortcutDescriptionID = "browser_action_shortcut_sort_label";
    }
  } else if (deduplicate) {

    // Deduplicate.
    titleID = "browser_action_deduplicate_label";
    shortcutDescriptionID = "browser_action_deduplicate_label";
  }

  // Set browser action title.
  browser.browserAction.setTitle({
    title: browser.i18n.getMessage(titleID)
  });

  // Set browser action shortcut description.
  browser.commands.update({
    name: "_execute_browser_action",
    description: browser.i18n.getMessage(shortcutDescriptionID)
  });
}

/*
 * Sorts, deduplicates, and removes low-priority (or blank) tabs.
 *
 * @param windowId      window ID
 * @param sort          sort tabs
 * @param deduplicate   deduplicate tabs
 */
function processTabs(windowId, sort, deduplicate) {

  let tabPropsArray;

  const gettingTabs = browser.tabs.query({
    pinned: false,
    windowId,
  }).then(unpinnedTabs => {

    // Check if window is already being sorted.
    if (processingWindows.has(windowId))
      return;

    processingWindows.add(windowId);

    // Initialise various caches for the window.
    hostnameTokenCache.set(windowId, new Map());
    pathnameTokenCache.set(windowId, new Map());

    tabPropsArray = unpinnedTabs.map(unpinnedTab => new TabProps(unpinnedTab));

    // Get sorting order for tabs.
    tabPropsArray.sort(compareTabs);

    if (!sort)
      return;

    // Get first tab index.
    const {index} = unpinnedTabs[0];

    // Move tabs into place.
    return browser.tabs.move(
      tabPropsArray.map(tabProps => tabProps.id), { index });
  }).then(() => {

    // Filter duplicate and discardable tabs.
    const unwantedTabs = tabPropsArray.filter(tabProps =>
      tabProps.status === "complete" &&
        (tabProps.isDiscardable || deduplicate && tabProps.isDuplicate));

    // Check for discardable tabs.
    const hasDiscardableTabs = tabPropsArray.some(tabProps =>
      tabProps.status === "complete" && tabProps.isDiscardable);

    if (hasDiscardableTabs) {

      // Create a new tab to remain after culling.
      return browser.tabs.create({
        active: false,
        windowId: windowId
      }).then(browser.tabs.remove(unwantedTabs.map(tab => tab.id)));
    } else {
      return browser.tabs.remove(unwantedTabs.map(tab => tab.id));
    }
  }).then(removedTabs => {

    // Clear the window's caches.
    hostnameTokenCache.get(windowId).clear();
    pathnameTokenCache.get(windowId).clear();

    // Allow the window's tabs to be sorted again.
    processingWindows.delete(windowId);
  });
}

/*
 * Compares tabs based on certain criteria.
 *
 * @param propsA        first tab properties for comparison
 * @param propsB        second tab properties for comparison
 * @return              comparison numeric result
 */
function compareTabs(propsA, propsB) {

  // Map the string preference value to a number.
  const sortMode = SORT_MODES.get(PREFS.pref_tabs_sort_by_parts);
  let result;

  if ((result = propsB.hasAboutScheme - propsA.hasAboutScheme) !== 0)
    return result;

  if (propsA.hasAboutScheme) { // Both tab URLs have "about:" schemes.

    // Compare pathnames.
    return propsA.pathname.localeCompare(propsB.pathname);
  }

  if (sortMode === 3) { // title-host-path sorting. Compare titles.
    if ((result = propsA.title.localeCompare(propsB.title)) !== 0)
      return result;
  }

  // Compare hostnames.
  if ((result =
    compareTokens(propsA.lowerDomainTokens, propsB.lowerDomainTokens)) !== 0)
    return result;

  // Compare TLDs.
  if ((result = compareTokens(propsA.tldTokens, propsB.tldTokens)) !== 0)
    return result;

  // Compare pathlessness.
  if ((result = propsA.hasPathname - propsB.hasPathname) !== 0)
    return result;

  if (sortMode === 1) { // host-title-path sorting. Compare titles.
    if ((result = propsA.title.localeCompare(propsB.title)) !== 0)
      return result;
  }

  // Compare pathnames.
  result = compareTokens(propsA.pathnameTokens, propsB.pathnameTokens);
  if (result !== 0)
    return result;

  // Compare query strings.
  if (PREFS.pref_tabs_sort_by_query_string) {
    if ((result = propsA.queryString.localeCompare(propsB.queryString)) !== 0)
      return result;
  }

  // Compare hashes (fragments).
  if ((result = propsA.hash.localeCompare(propsB.hash)) !== 0)
    return result;

  if (sortMode === 2) { // host-path-title. Compare titles.
    if ((result = propsA.title.localeCompare(propsB.title)) !== 0)
      return result;
  }

  // The two tabs are considered duplicate. Mark the later tab as a duplicate.
  if (propsA.index < propsB.index) {
    propsB.isDuplicate = true;
  } else {
    propsA.isDuplicate = true;
  }

  return 0;
}

/*
 * Compares a list of tokens.
 *
 * Compare a token from one array to its corresponding token in the other array
 * until they sort differently or an array runs out of tokens to compare.
 *
 * @param tokensA       first array of tokens for comparison
 * @param tokensB       second array of tokens for comparison
 * @return              comparison numeric result
 */
function compareTokens(tokensA, tokensB) {

  const tokensLengthA = tokensA.length;
  const tokensLengthB = tokensB.length;
  const shortestLength = Math.min(tokensLengthA, tokensLengthB);
  let result = 0;

  for (let index = 0; index < shortestLength; ++index) {
    if ((result = tokensA[index].localeCompare(tokensB[index])) !== 0)
      break;
  }

  return result === 0 ? Math.sign(tokensLengthA - tokensLengthB) : result;
}

/*
 * Roughly split hostname into top-level and lower-level domains.
 *
 * Will set/get cache entries by hostname and window ID.
 *
 * @param hostname      URL hostname
 * @param windowId      ID of associated window
 * @return              [top-level domain tokens, lower-level domain tokens]
 */
function splitHostname(hostname, windowId) {

  const tokensMap = hostnameTokenCache.get(windowId);
  let hostnameTokens = tokensMap.get(hostname);

  if (hostnameTokens)
    return hostnameTokens;

  const tokens = hostname.split('.').reverse();
  const tokensLength = tokens.length;
  const splitIndex = (tokensLength > 2 && tokens[1].length <= 3) ? 2 : 1;
  hostnameTokens = [tokens.slice(0, splitIndex), tokens.slice(splitIndex)];
  tokensMap.set(hostname, hostnameTokens);

  return hostnameTokens;
}

/*
 * Split pathname into tokens.
 *
 * Will set/get cache entries by pathname and window ID.
 *
 * @param hostname      URL pathname
 * @param windowId      ID of associated window
 * @return              [pathname tokens]
 */
function splitPathname(pathname, windowId) {

  const tokensMap = pathnameTokenCache.get(windowId);
  let pathnameTokens = tokensMap.get(pathname);

  if (pathnameTokens)
    return pathnameTokens;

  pathnameTokens = pathname.split('/');

  tokensMap.set(pathname, pathnameTokens);

  return pathnameTokens;
}
<commit_msg>Avoid removing active tab during deduplication.<commit_after>/*
 * @author              Mitchell Field <mitchell.field@live.com.au>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

const DISCARDABLE_TAB_URLS = new Set([
  "about:blank",
  "about:newtab",
  "about:privatebrowsing"
]);

const SORT_MODES = new Map([
  ["none", 0],
  ["host_title_path", 1],
  ["host_path_title", 2],
  ["title_host_path", 3]
]);

const DEFAULT_PREFS = {
  "pref_tabs_deduplicate": "false",
  "pref_tabs_sort_by_parts": "none",
  "pref_tabs_sort_by_query_string": "true"
};

const PREFS = Object.assign(DEFAULT_PREFS);

// Get "sync" storage contents.
browser.storage.sync.get().then((storedObject) => {

  // Merge preferences with preferences from storage.
  Object.assign(PREFS, DEFAULT_PREFS, storedObject.preferences);
  updateUI();
});

// Set browser action.
browser.browserAction.onClicked.addListener(onBrowserAction);

// Listen to changes in storage.
browser.storage.onChanged.addListener(onStorageChanged);

const processingWindows = new Set();

const hostnameTokenCache = new Map();
const pathnameTokenCache = new Map();

function TabProps(tab) {
  const { active, id, index, status, title, url, windowId } = tab;

  const {
    protocol = ':',
    hostname = '',
    pathname = '/',
    searchParams = '',
    hash = '#'
  } = new URL(url || '');

  Object.assign(this, {
    _lowerDomainTokens: null,
    _pathnameTokens: null,
    _tldTokens: null,
    hasAboutScheme: protocol === "about:",
    hasPathname: pathname !== '/',
    hash,
    hostname,
    id,
    index,
    isActive: active,
    isDiscardable: DISCARDABLE_TAB_URLS.has(url),
    isDuplicate: false,
    pathname,
    queryString: searchParams.toString(),
    status,
    tab,
    title: title || '',
    url,
    windowId
  });
}

TabProps.prototype = {
  get lowerDomainTokens() {
    if (!this._lowerDomainTokens) {
      const hostnameTokens = splitHostname(this.hostname, this.windowId);
      this._tldTokens = hostnameTokens[0];
      this._lowerDomainTokens = hostnameTokens[1];
    }
    return this._lowerDomainTokens;
  },
  get pathnameTokens() {
    if (!this._pathnameTokens) {
      this._pathnameTokens = splitPathname(this.pathname, this.windowId);
    }
    return this._pathnameTokens;
  },
  get tldTokens() {
    if (!this._tldTokens) {
      const hostnameTokens = splitHostname(this.hostname, this.windowId);
      this._tldTokens = hostnameTokens[0];
      this._lowerDomainTokens = hostnameTokens[1];
    }
    return this._tldTokens;
  }
}

/*
 * Called when the "browser action" is invoked.
 */
function onBrowserAction(tab, onClickData) {
  const sort = PREFS.pref_tabs_sort_by_parts !== "none";
  const deduplicate = PREFS.pref_tabs_deduplicate === "true";

  if (!sort && !deduplicate) {

    // The browser action is not configured to sort nor deduplicate tabs.
    browser.runtime.openOptionsPage();
    return;
  }

  processTabs(tab.windowId, sort, deduplicate);
}

/*
 * Called when a storage area is changed.
 */
function onStorageChanged(changes, areaName) {
  if (areaName !== "sync" || !("preferences" in changes)) {
    return;
  }

  Object.assign(PREFS, changes.preferences.newValue);
  updateUI();
}

/*
 * Updates UI elements such as titles and descriptions.
 */
function updateUI() {
  let sort = PREFS.pref_tabs_sort_by_parts !== "none";
  let deduplicate = PREFS.pref_tabs_deduplicate === "true";

  // Default browser action title.
  let titleID = "browser_action_none_label";
  let shortcutDescriptionID = "browser_action_shortcut_none_label";

  if (sort) {
    if (deduplicate) {

      // Sort and deduplicate.
      titleID = "browser_action_sort_deduplicate_label";
      shortcutDescriptionID = "browser_action_shortcut_sort_deduplicate_label";
    } else {

      // Sort.
      titleID = "browser_action_sort_label";
      shortcutDescriptionID = "browser_action_shortcut_sort_label";
    }
  } else if (deduplicate) {

    // Deduplicate.
    titleID = "browser_action_deduplicate_label";
    shortcutDescriptionID = "browser_action_deduplicate_label";
  }

  // Set browser action title.
  browser.browserAction.setTitle({
    title: browser.i18n.getMessage(titleID)
  });

  // Set browser action shortcut description.
  browser.commands.update({
    name: "_execute_browser_action",
    description: browser.i18n.getMessage(shortcutDescriptionID)
  });
}

/*
 * Sorts, deduplicates, and removes low-priority (or blank) tabs.
 *
 * @param windowId      window ID
 * @param sort          sort tabs
 * @param deduplicate   deduplicate tabs
 */
function processTabs(windowId, sort, deduplicate) {

  let tabPropsArray;

  const gettingTabs = browser.tabs.query({
    pinned: false,
    windowId,
  }).then(unpinnedTabs => {

    // Check if window is already being sorted.
    if (processingWindows.has(windowId))
      return;

    processingWindows.add(windowId);

    // Initialise various caches for the window.
    hostnameTokenCache.set(windowId, new Map());
    pathnameTokenCache.set(windowId, new Map());

    tabPropsArray = unpinnedTabs.map(unpinnedTab => new TabProps(unpinnedTab));

    // Get sorting order for tabs.
    tabPropsArray.sort(compareTabs);

    if (!sort)
      return;

    // Get first tab index.
    const {index} = unpinnedTabs[0];

    // Move tabs into place.
    return browser.tabs.move(
      tabPropsArray.map(tabProps => tabProps.id), { index });
  }).then(() => {

    // Filter duplicate and discardable tabs.
    const unwantedTabs = tabPropsArray.filter(tabProps =>
      tabProps.status === "complete" &&
        (tabProps.isDiscardable || deduplicate && tabProps.isDuplicate));

    // Check for discardable tabs.
    const hasDiscardableTabs = tabPropsArray.some(tabProps =>
      tabProps.status === "complete" && tabProps.isDiscardable);

    if (hasDiscardableTabs) {

      // Create a new tab to remain after culling.
      return browser.tabs.create({
        active: false,
        windowId: windowId
      }).then(browser.tabs.remove(unwantedTabs.map(tab => tab.id)));
    } else {
      return browser.tabs.remove(unwantedTabs.map(tab => tab.id));
    }
  }).then(removedTabs => {

    // Clear the window's caches.
    hostnameTokenCache.get(windowId).clear();
    pathnameTokenCache.get(windowId).clear();

    // Allow the window's tabs to be sorted again.
    processingWindows.delete(windowId);
  });
}

/*
 * Compares tabs based on certain criteria.
 *
 * @param propsA        first tab properties for comparison
 * @param propsB        second tab properties for comparison
 * @return              comparison numeric result
 */
function compareTabs(propsA, propsB) {

  // Map the string preference value to a number.
  const sortMode = SORT_MODES.get(PREFS.pref_tabs_sort_by_parts);
  let result;

  if ((result = propsB.hasAboutScheme - propsA.hasAboutScheme) !== 0)
    return result;

  if (propsA.hasAboutScheme) { // Both tab URLs have "about:" schemes.

    // Compare pathnames.
    return propsA.pathname.localeCompare(propsB.pathname);
  }

  if (sortMode === 3) { // title-host-path sorting. Compare titles.
    if ((result = propsA.title.localeCompare(propsB.title)) !== 0)
      return result;
  }

  // Compare hostnames.
  if ((result =
    compareTokens(propsA.lowerDomainTokens, propsB.lowerDomainTokens)) !== 0)
    return result;

  // Compare TLDs.
  if ((result = compareTokens(propsA.tldTokens, propsB.tldTokens)) !== 0)
    return result;

  // Compare pathlessness.
  if ((result = propsA.hasPathname - propsB.hasPathname) !== 0)
    return result;

  if (sortMode === 1) { // host-title-path sorting. Compare titles.
    if ((result = propsA.title.localeCompare(propsB.title)) !== 0)
      return result;
  }

  // Compare pathnames.
  result = compareTokens(propsA.pathnameTokens, propsB.pathnameTokens);
  if (result !== 0)
    return result;

  // Compare query strings.
  if (PREFS.pref_tabs_sort_by_query_string) {
    if ((result = propsA.queryString.localeCompare(propsB.queryString)) !== 0)
      return result;
  }

  // Compare hashes (fragments).
  if ((result = propsA.hash.localeCompare(propsB.hash)) !== 0)
    return result;

  if (sortMode === 2) { // host-path-title. Compare titles.
    if ((result = propsA.title.localeCompare(propsB.title)) !== 0)
      return result;
  }

  // The two tabs are considered duplicate at this point.
  // If one of them is active, mark the other as the duplicate.
  if (propsA.isActive || !propsB.isActive && propsA.index < propsB.index) {
    propsB.isDuplicate = true;
  } else {
    propsA.isDuplicate = true;
  }

  return 0;
}

/*
 * Compares a list of tokens.
 *
 * Compare a token from one array to its corresponding token in the other array
 * until they sort differently or an array runs out of tokens to compare.
 *
 * @param tokensA       first array of tokens for comparison
 * @param tokensB       second array of tokens for comparison
 * @return              comparison numeric result
 */
function compareTokens(tokensA, tokensB) {

  const tokensLengthA = tokensA.length;
  const tokensLengthB = tokensB.length;
  const shortestLength = Math.min(tokensLengthA, tokensLengthB);
  let result = 0;

  for (let index = 0; index < shortestLength; ++index) {
    if ((result = tokensA[index].localeCompare(tokensB[index])) !== 0)
      break;
  }

  return result === 0 ? Math.sign(tokensLengthA - tokensLengthB) : result;
}

/*
 * Roughly split hostname into top-level and lower-level domains.
 *
 * Will set/get cache entries by hostname and window ID.
 *
 * @param hostname      URL hostname
 * @param windowId      ID of associated window
 * @return              [top-level domain tokens, lower-level domain tokens]
 */
function splitHostname(hostname, windowId) {

  const tokensMap = hostnameTokenCache.get(windowId);
  let hostnameTokens = tokensMap.get(hostname);

  if (hostnameTokens)
    return hostnameTokens;

  const tokens = hostname.split('.').reverse();
  const tokensLength = tokens.length;
  const splitIndex = (tokensLength > 2 && tokens[1].length <= 3) ? 2 : 1;
  hostnameTokens = [tokens.slice(0, splitIndex), tokens.slice(splitIndex)];
  tokensMap.set(hostname, hostnameTokens);

  return hostnameTokens;
}

/*
 * Split pathname into tokens.
 *
 * Will set/get cache entries by pathname and window ID.
 *
 * @param hostname      URL pathname
 * @param windowId      ID of associated window
 * @return              [pathname tokens]
 */
function splitPathname(pathname, windowId) {

  const tokensMap = pathnameTokenCache.get(windowId);
  let pathnameTokens = tokensMap.get(pathname);

  if (pathnameTokens)
    return pathnameTokens;

  pathnameTokens = pathname.split('/');

  tokensMap.set(pathname, pathnameTokens);

  return pathnameTokens;
}
<|endoftext|>


<commit_before>(function(window, document, querystring) {
  // TODO: Make fault tolerant.
  var screenshotServerURL = decodeURIComponent(/screenshotServerURL=([^&]*)/.exec(querystring)[1]);
  var screenshotClientID = decodeURIComponent(/screenshotClientID=([^&]*)/.exec(querystring)[1]);

  // Don't redefine an existing function.
  if (window.takeScreenshot) { return; }

  window.takeScreenshot = function(asynchronous) {
    // TODO: Collect metadata.
    // TODO: Ensure that this escapes correctly.
    var metadata = encodeURIComponent(JSON.stringify({
      scrollHeight: document.body.scrollHeight,
      outerHeight: window.outerHeight,
      innerHeight: window.innerHeight
    }));

    var endpoint = screenshotServerURL + '?screenshotClientID=' + screenshotClientID + '&metadata=' + metadata;
    var xhr = new XMLHttpRequest();

    if (!asynchronous) {
      xhr.open("POST", endpoint, false);
      xhr.send();
      // TODO: Fail gracefully.
      return true;
    } else {
      return new Promise(function(resolve, reject) {
        xhr.open("POST", endpoint, true);
        xhr.addEventListener("load", resolve, false);
        xhr.addEventListener("error", reject, false);
        xhr.addEventListener("abort", reject, false);
        xhr.send();
      });
    }
  }
})(window, document, window.location.search);
<commit_msg>sending POST to /takescreenshot<commit_after>(function(window, document, querystring) {
  // TODO: Make fault tolerant.
  var screenshotServerURL = decodeURIComponent(/screenshotServerURL=([^&]*)/.exec(querystring)[1]);
  var screenshotClientID = decodeURIComponent(/screenshotClientID=([^&]*)/.exec(querystring)[1]);

  // Don't redefine an existing function.
  if (window.takeScreenshot) { return; }

  window.takeScreenshot = function(asynchronous) {
    // TODO: Collect metadata.
    // TODO: Ensure that this escapes correctly.
    var metadata = encodeURIComponent(JSON.stringify({
      scrollHeight: document.body.scrollHeight,
      outerHeight: window.outerHeight,
      innerHeight: window.innerHeight
    }));

    var endpoint = screenshotServerURL + 'takeScreenshot?screenshotClientID=' + screenshotClientID + '&metadata=' + metadata;
    var xhr = new XMLHttpRequest();

    if (!asynchronous) {
      xhr.open("POST", endpoint, false);
      xhr.send();
      // TODO: Fail gracefully.
      return true;
    } else {
      return new Promise(function(resolve, reject) {
        xhr.open("POST", endpoint, true);
        xhr.addEventListener("load", resolve, false);
        xhr.addEventListener("error", reject, false);
        xhr.addEventListener("abort", reject, false);
        xhr.send();
      });
    }
  }
})(window, document, window.location.search);
<|endoftext|>


<commit_before>define(["js/data/RestDataSource", "js/data/Model", "js/data/Collection", "underscore"], function (RestDataSource, Model, Collection, _) {

    var SprdDataSource = RestDataSource.inherit('sprd.data.SprdDataSource', {
        defaults: {
            apiKey: null,
            secret: null,
            session: null
        },

        _getConfigurationForResource: function (resource) {

            var parentContext = resource.$context;
            var configuration = this.$dataSourceConfiguration;

            if (parentContext && parentContext.$contextModel) {
                configuration = configuration.getConfigurationForModelClass(parentContext.$contextModel.factory);
            }

            if (resource instanceof Model) {
                return configuration.getConfigurationForModelClass(resource.factory);
            } else if (resource instanceof Collection) {
                return configuration.getConfigurationForModelClass(resource.$modelFactory);
            }

            return null;
        },

        getQueryParameters: function (method, resource) {
            var obj = {};

            if (this.$.locale) {
                obj.locale = this.$.locale;
            }

            var params = _.defaults(obj, this.callBase());

            var configuration,
                apiKey = this.$.apiKey,
                needsSignature;

            // get the configuration only if needed
            configuration = this._getConfigurationForResource(resource);

            if (!configuration) {
                throw new Error("Configuration for resource not found");
            }

            var signatureRequired = configuration.$.signatureRequired,
                apiKeyRequired = configuration.$.apiKeyRequired,
                sessionRequired = configuration.$.sessionRequired;

            needsSignature = signatureRequired || sessionRequired;

            if (this.$.session && sessionRequired) {
                params = _.defaults(params, {
                    // TODO: change session id to token for translation service
                    sessionId: this.$.session.$.id
                });
            }

            if (needsSignature) {
                var secret = this.$.secret;
                var time = Date.now();
                var url = this._buildUriForResource(resource, this.$.endPoint);

                var data = method + " " + url + " " + time + " " + secret;

                params = _.defaults(params, {
                    sig: SprdDataSource.SHA1(data),
                    time: time,
                    apiKey: apiKey
                });
            }

            if (apiKeyRequired || signatureRequired || sessionRequired) {
                params = _.defaults(params, {
                    apiKey: apiKey
                });
            }

            return params;
        }
    });

    SprdDataSource.SHA1 = function (msg) {

        function rotateLeft(n, s) {
            return ( n << s ) | (n >>> (32 - s));
        }

        function cvtHex(val) {
            var str = "";
            var i;
            var v;

            for (i = 7; i >= 0; i--) {
                v = (val >>> (i * 4)) & 0x0f;
                str += v.toString(16);
            }
            return str;
        }

        function utf8Encode(string) {
            string = string.replace(/\r\n/g, "\n");
            var utfText = "";

            for (var n = 0; n < string.length; n++) {

                var c = string.charCodeAt(n);

                if (c < 128) {
                    utfText += String.fromCharCode(c);
                }
                else if ((c > 127) && (c < 2048)) {
                    utfText += String.fromCharCode((c >> 6) | 192);
                    utfText += String.fromCharCode((c & 63) | 128);
                }
                else {
                    utfText += String.fromCharCode((c >> 12) | 224);
                    utfText += String.fromCharCode(((c >> 6) & 63) | 128);
                    utfText += String.fromCharCode((c & 63) | 128);
                }

            }

            return utfText;
        }

        var blockStart;
        var i, j;
        var W = new Array(80);
        var H0 = 0x67452301;
        var H1 = 0xEFCDAB89;
        var H2 = 0x98BADCFE;
        var H3 = 0x10325476;
        var H4 = 0xC3D2E1F0;
        var A, B, C, D, E;
        var temp;

        msg = utf8Encode(msg);

        var msg_len = msg.length;

        var word_array = [];
        for (i = 0; i < msg_len - 3; i += 4) {
            j = msg.charCodeAt(i) << 24 | msg.charCodeAt(i + 1) << 16 |
                msg.charCodeAt(i + 2) << 8 | msg.charCodeAt(i + 3);
            word_array.push(j);
        }

        switch (msg_len % 4) {
            case 0:
                i = 0x080000000;
                break;
            case 1:
                i = msg.charCodeAt(msg_len - 1) << 24 | 0x0800000;
                break;

            case 2:
                i = msg.charCodeAt(msg_len - 2) << 24 | msg.charCodeAt(msg_len - 1) << 16 | 0x08000;
                break;

            case 3:
                i = msg.charCodeAt(msg_len - 3) << 24 | msg.charCodeAt(msg_len - 2) << 16 | msg.charCodeAt(msg_len - 1) << 8 | 0x80;
                break;
        }

        word_array.push(i);

        while ((word_array.length % 16) != 14) {
            word_array.push(0);
        }

        word_array.push(msg_len >>> 29);
        word_array.push((msg_len << 3) & 0x0ffffffff);

        for (blockStart = 0; blockStart < word_array.length; blockStart += 16) {

            for (i = 0; i < 16; i++) {
                W[i] = word_array[blockStart + i];
            }

            for (i = 16; i <= 79; i++) {
                W[i] = rotateLeft(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
            }

            A = H0;
            B = H1;
            C = H2;
            D = H3;
            E = H4;

            for (i = 0; i <= 19; i++) {
                temp = (rotateLeft(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
                E = D;
                D = C;
                C = rotateLeft(B, 30);
                B = A;
                A = temp;
            }

            for (i = 20; i <= 39; i++) {
                temp = (rotateLeft(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
                E = D;
                D = C;
                C = rotateLeft(B, 30);
                B = A;
                A = temp;
            }

            for (i = 40; i <= 59; i++) {
                temp = (rotateLeft(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
                E = D;
                D = C;
                C = rotateLeft(B, 30);
                B = A;
                A = temp;
            }

            for (i = 60; i <= 79; i++) {
                temp = (rotateLeft(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
                E = D;
                D = C;
                C = rotateLeft(B, 30);
                B = A;
                A = temp;
            }

            H0 = (H0 + A) & 0x0ffffffff;
            H1 = (H1 + B) & 0x0ffffffff;
            H2 = (H2 + C) & 0x0ffffffff;
            H3 = (H3 + D) & 0x0ffffffff;
            H4 = (H4 + E) & 0x0ffffffff;

        }

        temp = cvtHex(H0) + cvtHex(H1) + cvtHex(H2) + cvtHex(H3) + cvtHex(H4);
        return temp.toLowerCase();
    };

    return SprdDataSource;
});<commit_msg>signature is now only sent when session id is available and required<commit_after>define(["js/data/RestDataSource", "js/data/Model", "js/data/Collection", "underscore"], function (RestDataSource, Model, Collection, _) {

    var SprdDataSource = RestDataSource.inherit('sprd.data.SprdDataSource', {
        defaults: {
            apiKey: null,
            secret: null,
            session: null
        },

        _getConfigurationForResource: function (resource) {

            var parentContext = resource.$context;
            var configuration = this.$dataSourceConfiguration;

            if (parentContext && parentContext.$contextModel) {
                configuration = configuration.getConfigurationForModelClass(parentContext.$contextModel.factory);
            }

            if (resource instanceof Model) {
                return configuration.getConfigurationForModelClass(resource.factory);
            } else if (resource instanceof Collection) {
                return configuration.getConfigurationForModelClass(resource.$modelFactory);
            }

            return null;
        },

        getQueryParameters: function (method, resource) {
            var obj = {};

            if (this.$.locale) {
                obj.locale = this.$.locale;
            }

            var params = _.defaults(obj, this.callBase());

            var configuration,
                apiKey = this.$.apiKey,
                needsSignature;

            // get the configuration only if needed
            configuration = this._getConfigurationForResource(resource);

            if (!configuration) {
                throw new Error("Configuration for resource not found");
            }

            var signatureRequired = configuration.$.signatureRequired,
                apiKeyRequired = configuration.$.apiKeyRequired,
                sessionRequired = configuration.$.sessionRequired;

            var sessionId = this.get('session.id');

            needsSignature = signatureRequired || (sessionRequired && sessionId);

            if (sessionId && sessionRequired) {
                params = _.defaults(params, {
                    // TODO: change session id to token for translation service
                    sessionId: sessionId
                });
            }

            if (needsSignature) {
                var secret = this.$.secret;
                var time = Date.now();
                var url = this._buildUriForResource(resource, this.$.endPoint);

                var data = method + " " + url + " " + time + " " + secret;

                params = _.defaults(params, {
                    sig: SprdDataSource.SHA1(data),
                    time: time,
                    apiKey: apiKey
                });
            }

            if (apiKeyRequired || signatureRequired || sessionRequired) {
                params = _.defaults(params, {
                    apiKey: apiKey
                });
            }

            return params;
        }
    });

    SprdDataSource.SHA1 = function (msg) {

        function rotateLeft(n, s) {
            return ( n << s ) | (n >>> (32 - s));
        }

        function cvtHex(val) {
            var str = "";
            var i;
            var v;

            for (i = 7; i >= 0; i--) {
                v = (val >>> (i * 4)) & 0x0f;
                str += v.toString(16);
            }
            return str;
        }

        function utf8Encode(string) {
            string = string.replace(/\r\n/g, "\n");
            var utfText = "";

            for (var n = 0; n < string.length; n++) {

                var c = string.charCodeAt(n);

                if (c < 128) {
                    utfText += String.fromCharCode(c);
                }
                else if ((c > 127) && (c < 2048)) {
                    utfText += String.fromCharCode((c >> 6) | 192);
                    utfText += String.fromCharCode((c & 63) | 128);
                }
                else {
                    utfText += String.fromCharCode((c >> 12) | 224);
                    utfText += String.fromCharCode(((c >> 6) & 63) | 128);
                    utfText += String.fromCharCode((c & 63) | 128);
                }

            }

            return utfText;
        }

        var blockStart;
        var i, j;
        var W = new Array(80);
        var H0 = 0x67452301;
        var H1 = 0xEFCDAB89;
        var H2 = 0x98BADCFE;
        var H3 = 0x10325476;
        var H4 = 0xC3D2E1F0;
        var A, B, C, D, E;
        var temp;

        msg = utf8Encode(msg);

        var msg_len = msg.length;

        var word_array = [];
        for (i = 0; i < msg_len - 3; i += 4) {
            j = msg.charCodeAt(i) << 24 | msg.charCodeAt(i + 1) << 16 |
                msg.charCodeAt(i + 2) << 8 | msg.charCodeAt(i + 3);
            word_array.push(j);
        }

        switch (msg_len % 4) {
            case 0:
                i = 0x080000000;
                break;
            case 1:
                i = msg.charCodeAt(msg_len - 1) << 24 | 0x0800000;
                break;

            case 2:
                i = msg.charCodeAt(msg_len - 2) << 24 | msg.charCodeAt(msg_len - 1) << 16 | 0x08000;
                break;

            case 3:
                i = msg.charCodeAt(msg_len - 3) << 24 | msg.charCodeAt(msg_len - 2) << 16 | msg.charCodeAt(msg_len - 1) << 8 | 0x80;
                break;
        }

        word_array.push(i);

        while ((word_array.length % 16) != 14) {
            word_array.push(0);
        }

        word_array.push(msg_len >>> 29);
        word_array.push((msg_len << 3) & 0x0ffffffff);

        for (blockStart = 0; blockStart < word_array.length; blockStart += 16) {

            for (i = 0; i < 16; i++) {
                W[i] = word_array[blockStart + i];
            }

            for (i = 16; i <= 79; i++) {
                W[i] = rotateLeft(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
            }

            A = H0;
            B = H1;
            C = H2;
            D = H3;
            E = H4;

            for (i = 0; i <= 19; i++) {
                temp = (rotateLeft(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
                E = D;
                D = C;
                C = rotateLeft(B, 30);
                B = A;
                A = temp;
            }

            for (i = 20; i <= 39; i++) {
                temp = (rotateLeft(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
                E = D;
                D = C;
                C = rotateLeft(B, 30);
                B = A;
                A = temp;
            }

            for (i = 40; i <= 59; i++) {
                temp = (rotateLeft(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
                E = D;
                D = C;
                C = rotateLeft(B, 30);
                B = A;
                A = temp;
            }

            for (i = 60; i <= 79; i++) {
                temp = (rotateLeft(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
                E = D;
                D = C;
                C = rotateLeft(B, 30);
                B = A;
                A = temp;
            }

            H0 = (H0 + A) & 0x0ffffffff;
            H1 = (H1 + B) & 0x0ffffffff;
            H2 = (H2 + C) & 0x0ffffffff;
            H3 = (H3 + D) & 0x0ffffffff;
            H4 = (H4 + E) & 0x0ffffffff;

        }

        temp = cvtHex(H0) + cvtHex(H1) + cvtHex(H2) + cvtHex(H3) + cvtHex(H4);
        return temp.toLowerCase();
    };

    return SprdDataSource;
});<|endoftext|>


<commit_before>(function() {
    var eventHandlers = {
        interpretKeyEvents: function(event) {
            var mapping = event.shiftKey ? Flame.MODIFIED_KEY_BINDINGS : Flame.KEY_BINDINGS;
            var eventName = mapping[event.keyCode];
            if (eventName && this[eventName]) {
                var handler = this[eventName];
                if (Ember.typeOf(handler) === 'function') {
                    return handler.call(this, event, this);
                }
            }
            return false;
        },

        handleKeyEvent: function(event, view) {
            var emberEvent = null;
            switch (event.type) {
                case "keydown": emberEvent = 'keyDown'; break;
                case "keypress": emberEvent = 'keyPress'; break;
            }
            var handler = emberEvent ? this.get(emberEvent) : null;
            if (window.FlameInspector && emberEvent) FlameInspector.logEvent(event, emberEvent, this);
            if (handler) {
                // Note that in jQuery, the contract is that event handler should return
                // true to allow default handling, false to prevent it. But in Ember, event handlers return true if they handled the event,
                // false if they didn't, so we want to invert that return value here.
                return !handler.call(Flame.keyResponderStack.current(), event, Flame.keyResponderStack.current());
            } else if (emberEvent === "keyDown" && this.interpretKeyEvents(event)) { // Try to hand down the event to a more specific key event handler
                return false;
            } else if (this.get('parentView')) {
                return this.get('parentView').handleKeyEvent(event, view);
            }
        }
    };

    Ember.View.reopen(eventHandlers);
    Ember.TextSupport.reopen(eventHandlers);
}());

Flame.KEY_BINDINGS = {
    8: 'deleteBackward',
    9: 'insertTab',
    13: 'insertNewline',
    27: 'cancel',
    32: 'insertSpace',
    37: 'moveLeft',
    38: 'moveUp',
    39: 'moveRight',
    40: 'moveDown',
    46: 'deleteForward'
};

Flame.MODIFIED_KEY_BINDINGS = {
    8: 'deleteForward',
    9: 'insertBacktab',
    37: 'moveLeftAndModifySelection',
    38: 'moveUpAndModifySelection',
    39: 'moveRightAndModifySelection',
    40: 'moveDownAndModifySelection'
};

// See Flame.TextFieldView for details on what this is needed for
Flame.ALLOW_BROWSER_DEFAULT_HANDLING = {};  // Just a marker value

Ember.mixin(Flame, {
    mouseResponderView: undefined, // Which view handled the last mouseDown event?

    /*
      Holds a stack of key responder views. With this we can neatly handle restoring the previous key responder
      when some modal UI element is closed. There's a few simple rules that governs the usage of the stack:
       - mouse click does .replace (this should also be used for programmatically taking focus when not a modal element)
       - opening a modal UI element does .push
       - closing a modal element does .pop

      Also noteworthy is that a view will be signaled that it loses the key focus only when it's popped off the
      stack, not when something is pushed on top. The idea is that when a modal UI element is opened, we know
      that the previously focused view will re-gain the focus as soon as the modal element is closed. So if the
      previously focused view was e.g. in the middle of some edit operation, it shouldn't cancel that operation.
    */
    keyResponderStack: Ember.Object.create({
        _stack: [],

        // Observer-friendly version of getting current
        currentKeyResponder: function() {
            return this.current();
        }.property().volatile(),

        current: function() {
            var length = this._stack.get('length');
            if (length > 0) return this._stack.objectAt(length - 1);
            else return undefined;
        },

        push: function(view) {
            if (!Ember.none(view)) {
                if (view.willBecomeKeyResponder) view.willBecomeKeyResponder();
                //console.log('View %s became key responder', Ember.guidFor(view));
                if (view.set && !view.isDestroyed) view.set('isFocused', true);
                this._stack.push(view);
                if (view.didBecomeKeyResponder) view.didBecomeKeyResponder();
                this.propertyDidChange('currentKeyResponder');
            }
            return view;
        },

        pop: function() {
            if (this._stack.get('length') > 0) {
                var current = this.current();
                if (current && current.willLoseKeyResponder) current.willLoseKeyResponder();  // Call before popping, could make a difference
                var view = this._stack.pop();
                //console.log('View %s will lose key responder', Ember.guidFor(view));
                if (view.set && !view.isDestroyed) view.set('isFocused', false);
                if (view.didLoseKeyResponder) view.didLoseKeyResponder();
                this.propertyDidChange('currentKeyResponder');
                return view;
            }
            else return undefined;
        },

        replace: function(view) {
            if (this.current() !== view) {
                this.pop();
                return this.push(view);
            }
        }
    })
});

// Set up a handler on the document for key events.
Ember.$(document).on('keydown.flame keypress.flame', null, function(event, triggeringManager) {
    if (Flame.keyResponderStack.current() !== undefined && Flame.keyResponderStack.current().get('isVisible')) {
        return Flame.keyResponderStack.current().handleKeyEvent(event, Flame.keyResponderStack.current());
    }
    return true;
});

// This logic is needed so that the view that handled mouseDown will receive mouseMoves and the eventual mouseUp, even if the
// pointer no longer is on top of that view. Without this, you get inconsistencies with buttons and all controls that handle
// mouse click events. The ember event dispatcher always first looks up 'eventManager' property on the view that's
// receiving an event, and lets that handle the event, if defined. So this should be mixed in to all the Flame views.
Flame.EventManager = {
    // Set to true in your view if you want to accept key responder status (which is needed for handling key events)
    acceptsKeyResponder: false,

    /*
      Sets this view as the target of key events. Call this if you need to make this happen programmatically.
      This gets also called on mouseDown if the view handles that, returns true and doesn't have property 'acceptsKeyResponder'
      set to false. If mouseDown returned true but 'acceptsKeyResponder' is false, this call is propagated to the parent view.

      If called with no parameters or with replace = true, the current key responder is first popped off the stack and this
      view is then pushed. See comments for Flame.keyResponderStack above for more insight.
    */
    becomeKeyResponder: function(replace) {
        if (this.get('acceptsKeyResponder') !== false && !this.get('isDisabled')) {
            if (replace === undefined || replace === true) {
                Flame.keyResponderStack.replace(this);
            } else {
                Flame.keyResponderStack.push(this);
            }
        } else {
            var parent = this.get('parentView');
            if (parent && parent.becomeKeyResponder) return parent.becomeKeyResponder(replace);
        }
    },

    /*
      Resign key responder status by popping the head off the stack. The head might or might not be this view,
      depending on whether user clicked anything since this view became the key responder. The new key responder
      will be the next view in the stack, if any.
    */
    resignKeyResponder: function() {
        Flame.keyResponderStack.pop();
    },

    eventManager: {
        mouseDown: function(event, view) {
            view.becomeKeyResponder();  // Becoming a key responder is independent of mouseDown handling
            Flame.set('mouseResponderView', undefined);
            var handlingView = this._dispatch('mouseDown', event, view);
            if (handlingView) {
                Flame.set('mouseResponderView', handlingView);
            }
            return !handlingView;
        },

        mouseUp: function(event, view) {
            if (Flame.get('mouseResponderView') !== undefined) {
                view = Flame.get('mouseResponderView');
                Flame.set('mouseResponderView', undefined);
            }
            return !this._dispatch('mouseUp', event, view);
        },

        mouseMove: function(event, view) {
            if (Flame.get('mouseResponderView') !== undefined) {
                view = Flame.get('mouseResponderView');
            }
            return !this._dispatch('mouseMove', event, view);
        },

        doubleClick: function(event, view) {
            if (Flame.get('mouseResponderView') !== undefined) {
                view = Flame.get('mouseResponderView');
            }
            return !this._dispatch('doubleClick', event, view);
        },

        keyDown: function(event) {
            if (Flame.keyResponderStack.current() !== undefined && Flame.keyResponderStack.current().get('isVisible')) {
                return Flame.keyResponderStack.current().handleKeyEvent(event, Flame.keyResponderStack.current());
            }
            return true;
        },

        keyPress: function(event) {
            if (Flame.keyResponderStack.current() !== undefined && Flame.keyResponderStack.current().get('isVisible')) {
                return Flame.keyResponderStack.current().handleKeyEvent(event, Flame.keyResponderStack.current());
            }
            return true;
        },

        // For the passed in view, calls the method with the name of the event, if defined. If that method
        // returns true, returns the view. If the method returns false, recurses on the parent view. If no
        // view handles the event, returns false.
        _dispatch: function(eventName, event, view) {
            if (window.FlameInspector) FlameInspector.logEvent(event, eventName, view);
            var handler = view.get(eventName);
            if (handler) {
                var result = handler.call(view, event, view);
                if (result === Flame.ALLOW_BROWSER_DEFAULT_HANDLING) return false;
                else if (result) return view;
            }
            var parentView = view.get('parentView');
            if (parentView) return this._dispatch(eventName, event, parentView);
            else return false;
        }
    }
};
<commit_msg>Fix Ember.object.create API change<commit_after>(function() {
    var eventHandlers = {
        interpretKeyEvents: function(event) {
            var mapping = event.shiftKey ? Flame.MODIFIED_KEY_BINDINGS : Flame.KEY_BINDINGS;
            var eventName = mapping[event.keyCode];
            if (eventName && this[eventName]) {
                var handler = this[eventName];
                if (Ember.typeOf(handler) === 'function') {
                    return handler.call(this, event, this);
                }
            }
            return false;
        },

        handleKeyEvent: function(event, view) {
            var emberEvent = null;
            switch (event.type) {
                case "keydown": emberEvent = 'keyDown'; break;
                case "keypress": emberEvent = 'keyPress'; break;
            }
            var handler = emberEvent ? this.get(emberEvent) : null;
            if (window.FlameInspector && emberEvent) FlameInspector.logEvent(event, emberEvent, this);
            if (handler) {
                // Note that in jQuery, the contract is that event handler should return
                // true to allow default handling, false to prevent it. But in Ember, event handlers return true if they handled the event,
                // false if they didn't, so we want to invert that return value here.
                return !handler.call(Flame.keyResponderStack.current(), event, Flame.keyResponderStack.current());
            } else if (emberEvent === "keyDown" && this.interpretKeyEvents(event)) { // Try to hand down the event to a more specific key event handler
                return false;
            } else if (this.get('parentView')) {
                return this.get('parentView').handleKeyEvent(event, view);
            }
        }
    };

    Ember.View.reopen(eventHandlers);
    Ember.TextSupport.reopen(eventHandlers);
}());

Flame.KEY_BINDINGS = {
    8: 'deleteBackward',
    9: 'insertTab',
    13: 'insertNewline',
    27: 'cancel',
    32: 'insertSpace',
    37: 'moveLeft',
    38: 'moveUp',
    39: 'moveRight',
    40: 'moveDown',
    46: 'deleteForward'
};

Flame.MODIFIED_KEY_BINDINGS = {
    8: 'deleteForward',
    9: 'insertBacktab',
    37: 'moveLeftAndModifySelection',
    38: 'moveUpAndModifySelection',
    39: 'moveRightAndModifySelection',
    40: 'moveDownAndModifySelection'
};

// See Flame.TextFieldView for details on what this is needed for
Flame.ALLOW_BROWSER_DEFAULT_HANDLING = {};  // Just a marker value

Ember.mixin(Flame, {
    mouseResponderView: undefined, // Which view handled the last mouseDown event?

    /*
      Holds a stack of key responder views. With this we can neatly handle restoring the previous key responder
      when some modal UI element is closed. There's a few simple rules that governs the usage of the stack:
       - mouse click does .replace (this should also be used for programmatically taking focus when not a modal element)
       - opening a modal UI element does .push
       - closing a modal element does .pop

      Also noteworthy is that a view will be signaled that it loses the key focus only when it's popped off the
      stack, not when something is pushed on top. The idea is that when a modal UI element is opened, we know
      that the previously focused view will re-gain the focus as soon as the modal element is closed. So if the
      previously focused view was e.g. in the middle of some edit operation, it shouldn't cancel that operation.
    */
    keyResponderStack: Ember.Object.extend({
        _stack: [],

        // Observer-friendly version of getting current
        currentKeyResponder: function() {
            return this.current();
        }.property().volatile(),

        current: function() {
            var length = this._stack.get('length');
            if (length > 0) return this._stack.objectAt(length - 1);
            else return undefined;
        },

        push: function(view) {
            if (!Ember.none(view)) {
                if (view.willBecomeKeyResponder) view.willBecomeKeyResponder();
                //console.log('View %s became key responder', Ember.guidFor(view));
                if (view.set && !view.isDestroyed) view.set('isFocused', true);
                this._stack.push(view);
                if (view.didBecomeKeyResponder) view.didBecomeKeyResponder();
                this.propertyDidChange('currentKeyResponder');
            }
            return view;
        },

        pop: function() {
            if (this._stack.get('length') > 0) {
                var current = this.current();
                if (current && current.willLoseKeyResponder) current.willLoseKeyResponder();  // Call before popping, could make a difference
                var view = this._stack.pop();
                //console.log('View %s will lose key responder', Ember.guidFor(view));
                if (view.set && !view.isDestroyed) view.set('isFocused', false);
                if (view.didLoseKeyResponder) view.didLoseKeyResponder();
                this.propertyDidChange('currentKeyResponder');
                return view;
            }
            else return undefined;
        },

        replace: function(view) {
            if (this.current() !== view) {
                this.pop();
                return this.push(view);
            }
        }
    }).create()
});

// Set up a handler on the document for key events.
Ember.$(document).on('keydown.flame keypress.flame', null, function(event, triggeringManager) {
    if (Flame.keyResponderStack.current() !== undefined && Flame.keyResponderStack.current().get('isVisible')) {
        return Flame.keyResponderStack.current().handleKeyEvent(event, Flame.keyResponderStack.current());
    }
    return true;
});

// This logic is needed so that the view that handled mouseDown will receive mouseMoves and the eventual mouseUp, even if the
// pointer no longer is on top of that view. Without this, you get inconsistencies with buttons and all controls that handle
// mouse click events. The ember event dispatcher always first looks up 'eventManager' property on the view that's
// receiving an event, and lets that handle the event, if defined. So this should be mixed in to all the Flame views.
Flame.EventManager = {
    // Set to true in your view if you want to accept key responder status (which is needed for handling key events)
    acceptsKeyResponder: false,

    /*
      Sets this view as the target of key events. Call this if you need to make this happen programmatically.
      This gets also called on mouseDown if the view handles that, returns true and doesn't have property 'acceptsKeyResponder'
      set to false. If mouseDown returned true but 'acceptsKeyResponder' is false, this call is propagated to the parent view.

      If called with no parameters or with replace = true, the current key responder is first popped off the stack and this
      view is then pushed. See comments for Flame.keyResponderStack above for more insight.
    */
    becomeKeyResponder: function(replace) {
        if (this.get('acceptsKeyResponder') !== false && !this.get('isDisabled')) {
            if (replace === undefined || replace === true) {
                Flame.keyResponderStack.replace(this);
            } else {
                Flame.keyResponderStack.push(this);
            }
        } else {
            var parent = this.get('parentView');
            if (parent && parent.becomeKeyResponder) return parent.becomeKeyResponder(replace);
        }
    },

    /*
      Resign key responder status by popping the head off the stack. The head might or might not be this view,
      depending on whether user clicked anything since this view became the key responder. The new key responder
      will be the next view in the stack, if any.
    */
    resignKeyResponder: function() {
        Flame.keyResponderStack.pop();
    },

    eventManager: {
        mouseDown: function(event, view) {
            view.becomeKeyResponder();  // Becoming a key responder is independent of mouseDown handling
            Flame.set('mouseResponderView', undefined);
            var handlingView = this._dispatch('mouseDown', event, view);
            if (handlingView) {
                Flame.set('mouseResponderView', handlingView);
            }
            return !handlingView;
        },

        mouseUp: function(event, view) {
            if (Flame.get('mouseResponderView') !== undefined) {
                view = Flame.get('mouseResponderView');
                Flame.set('mouseResponderView', undefined);
            }
            return !this._dispatch('mouseUp', event, view);
        },

        mouseMove: function(event, view) {
            if (Flame.get('mouseResponderView') !== undefined) {
                view = Flame.get('mouseResponderView');
            }
            return !this._dispatch('mouseMove', event, view);
        },

        doubleClick: function(event, view) {
            if (Flame.get('mouseResponderView') !== undefined) {
                view = Flame.get('mouseResponderView');
            }
            return !this._dispatch('doubleClick', event, view);
        },

        keyDown: function(event) {
            if (Flame.keyResponderStack.current() !== undefined && Flame.keyResponderStack.current().get('isVisible')) {
                return Flame.keyResponderStack.current().handleKeyEvent(event, Flame.keyResponderStack.current());
            }
            return true;
        },

        keyPress: function(event) {
            if (Flame.keyResponderStack.current() !== undefined && Flame.keyResponderStack.current().get('isVisible')) {
                return Flame.keyResponderStack.current().handleKeyEvent(event, Flame.keyResponderStack.current());
            }
            return true;
        },

        // For the passed in view, calls the method with the name of the event, if defined. If that method
        // returns true, returns the view. If the method returns false, recurses on the parent view. If no
        // view handles the event, returns false.
        _dispatch: function(eventName, event, view) {
            if (window.FlameInspector) FlameInspector.logEvent(event, eventName, view);
            var handler = view.get(eventName);
            if (handler) {
                var result = handler.call(view, event, view);
                if (result === Flame.ALLOW_BROWSER_DEFAULT_HANDLING) return false;
                else if (result) return view;
            }
            var parentView = view.get('parentView');
            if (parentView) return this._dispatch(eventName, event, parentView);
            else return false;
        }
    }
};
<|endoftext|>