<commit_before>define(['js/core/EventDispatcher','js/lib/parser','js/core/Binding', 'underscore'], function(EventDispatcher,Parser, Binding, _){

    function findTransformFunction(path, scope) {
        var pathElement = path[0];
        if (pathElement.type == Binding.TYPE_FNC) {
            scope = scope.getScopeForFncName(pathElement.name);
        } else {
            scope = scope.getScopeForKey(pathElement.name);
        }

        var nScope = scope;
        while (nScope && path.length > 0) {
            pathElement = path.shift();
            if (pathElement.type == Binding.TYPE_FNC) {
                return nScope[pathElement.name];
            } else if (pathElement.type == Binding.TYPE_VAR) {
                nScope = nScope.get(pathElement.name);
            }
        }

        return false;
    }

    var bindingCache = {},
        idCounter = 0,
        cacheCounter = 0;

    function pathToString(path){
        if(path instanceof Array){
            var el = [];
            for(var i = 0; i < path.length; i++){
                if(path[i].type === Binding.TYPE_VAR){
                    el.push(path[i].name);
                } else if(path[i].type === Binding.TYPE_FNC){
                    el.push(path[i].name + "(" + (idCounter++) + ")");
                }
            }

            return el.join(".");
        } else {
            return path;
        }
    }

    return EventDispatcher.inherit('js.core.BindingCreator',{

        /***
         *
         * @param bindingDef
         * @param targetScope
         * @param {String|Function} attrKey key where to bind, or callback function
         * @param context
         * @return {*}
         */
        create: function(bindingDef, targetScope, attrKey, context){
            var path = bindingDef.path;
            var pathElement = path[0];

            var scope;
            var searchScope = targetScope;
            if (pathElement.type != Binding.TYPE_FNC && attrKey == pathElement.name) {
                searchScope = searchScope.$parentScope;
            }

            if (pathElement.type == Binding.TYPE_FNC) {
                scope = searchScope.getScopeForFncName(pathElement.name);
            } else {
                scope = searchScope.getScopeForKey(pathElement.name);
            }

            if(scope){
                if (bindingDef.type !== "static") {
                    var cb;
                    if (_.isFunction(attrKey)) {
                        cb = attrKey;
                    }

                    var twoWay = (bindingDef.type == Binding.TYPE_TWOWAY),
                        cacheId;

                    if(!cb && !twoWay){
                        cacheId = pathToString(bindingDef.path) + scope.$cid;
                        if(bindingCache[cacheId]){
                            console.log(cacheCounter++);
                            bindingCache[cacheId].addTarget(targetScope,attrKey);
                            return bindingCache[cacheId];
                        }
                    }

                    var options = {
                        scope: scope,
                        path: path,
                        parent: bindingDef.parent,
                        target: targetScope,
                        twoWay: twoWay,
                        context: context,
                        bindingCreator: this
                    };

                    if (twoWay) {
                        if (bindingDef.transform) {
                            var transformFnc = findTransformFunction(bindingDef.transform, searchScope);
                            if (transformFnc) {
                                options.transform = transformFnc;
                            }
                        }

                        if (bindingDef.transformBack) {
                            var transformBackFnc = findTransformFunction(bindingDef.transformBack, searchScope);
                            if (transformBackFnc) {
                                options.transformBack = transformBackFnc;
                            }
                        }
                    }

                    if (cb) {
                        options['callback'] = cb;
                    } else {
                        options['targetKey'] = attrKey;
                    }

                    var binding = new Binding(options);
                    if(!twoWay && !(cb)){
                        binding.bind('destroy', function(){
                            delete bindingCache[cacheId];
                        });
                        bindingCache[cacheId] = binding
                    }

                    return binding;

                } else {
                    return scope.get(bindingDef.path);
                }
            }else{
                throw new Error("Couldn't find scope for " + pathElement.name);
            }

        },

        /***
         *
         * @param text
         * @param scope
         * @param attrKey
         * @param [bindingDefinitions]
         * @return {*}
         */
        evaluate: function (text, scope, attrKey, bindingDefinitions) {
            if (!_.isString(text)) {
                return text;
            }

            bindingDefinitions = bindingDefinitions || this.parse(text);

            var binding,
                bindings = [];

            for (var i = 0; i < bindingDefinitions.length; i++) {
                var bindingDef = bindingDefinitions[i];
                if (bindingDef.length) {
                    bindingDefinitions[i] = bindingDef;
                } else {
                    try {
                        binding = this.create(bindingDef, scope, attrKey, bindingDefinitions);
                    } catch (e) {
                        throw new Error("Create binding for '" + text + "'. " + e.message);
                    }

                    if (binding instanceof Binding) {
                        bindings.push(binding);
                        scope.$bindings[attrKey] = scope.$bindings[attrKey] || [];
                        scope.$bindings[attrKey].push(binding);
                    }
                    bindingDefinitions[i] = binding;
                }

            }

            if (bindings.length > 0) {
                return bindings[0].getContextValue();
            } else if (bindingDefinitions.length > 0) {
                if (bindingDefinitions.length === 1) {
                    return bindingDefinitions[0];
                }
                return Binding.contextToString(bindingDefinitions);
            } else {
                return text;
            }

        },

        parse: function(text) {
            if (_.isString(text)) {
                return Parser.parse(text, "text");
            }
        },

        parsePath: function(text){
            return Parser.parse(text, "path");
        },
        containsBindingDefinition: function(bindingDefinitions) {

            if (bindingDefinitions) {
                for (var i = 0; i < bindingDefinitions.length; i++) {
                    if (bindingDefinitions[i] instanceof Object) {
                        return true;
                    }
                }
            }

            return false;
        }
    });

});<commit_msg>removed console.log<commit_after>define(['js/core/EventDispatcher','js/lib/parser','js/core/Binding', 'underscore'], function(EventDispatcher,Parser, Binding, _){

    function findTransformFunction(path, scope) {
        var pathElement = path[0];
        if (pathElement.type == Binding.TYPE_FNC) {
            scope = scope.getScopeForFncName(pathElement.name);
        } else {
            scope = scope.getScopeForKey(pathElement.name);
        }

        var nScope = scope;
        while (nScope && path.length > 0) {
            pathElement = path.shift();
            if (pathElement.type == Binding.TYPE_FNC) {
                return nScope[pathElement.name];
            } else if (pathElement.type == Binding.TYPE_VAR) {
                nScope = nScope.get(pathElement.name);
            }
        }

        return false;
    }

    var bindingCache = {},
        idCounter = 0,
        cacheCounter = 0;

    function pathToString(path){
        if(path instanceof Array){
            var el = [];
            for(var i = 0; i < path.length; i++){
                if(path[i].type === Binding.TYPE_VAR){
                    el.push(path[i].name);
                } else if(path[i].type === Binding.TYPE_FNC){
                    el.push(path[i].name + "(" + (idCounter++) + ")");
                }
            }

            return el.join(".");
        } else {
            return path;
        }
    }

    return EventDispatcher.inherit('js.core.BindingCreator',{

        /***
         *
         * @param bindingDef
         * @param targetScope
         * @param {String|Function} attrKey key where to bind, or callback function
         * @param context
         * @return {*}
         */
        create: function(bindingDef, targetScope, attrKey, context){
            var path = bindingDef.path;
            var pathElement = path[0];

            var scope;
            var searchScope = targetScope;
            if (pathElement.type != Binding.TYPE_FNC && attrKey == pathElement.name) {
                searchScope = searchScope.$parentScope;
            }

            if (pathElement.type == Binding.TYPE_FNC) {
                scope = searchScope.getScopeForFncName(pathElement.name);
            } else {
                scope = searchScope.getScopeForKey(pathElement.name);
            }

            if(scope){
                if (bindingDef.type !== "static") {
                    var cb;
                    if (_.isFunction(attrKey)) {
                        cb = attrKey;
                    }

                    var twoWay = (bindingDef.type == Binding.TYPE_TWOWAY),
                        cacheId;

                    if(!cb && !twoWay){
                        cacheId = pathToString(bindingDef.path) + scope.$cid;
                        if(bindingCache[cacheId]){
                            bindingCache[cacheId].addTarget(targetScope,attrKey);
                            return bindingCache[cacheId];
                        }
                    }

                    var options = {
                        scope: scope,
                        path: path,
                        parent: bindingDef.parent,
                        target: targetScope,
                        twoWay: twoWay,
                        context: context,
                        bindingCreator: this
                    };

                    if (twoWay) {
                        if (bindingDef.transform) {
                            var transformFnc = findTransformFunction(bindingDef.transform, searchScope);
                            if (transformFnc) {
                                options.transform = transformFnc;
                            }
                        }

                        if (bindingDef.transformBack) {
                            var transformBackFnc = findTransformFunction(bindingDef.transformBack, searchScope);
                            if (transformBackFnc) {
                                options.transformBack = transformBackFnc;
                            }
                        }
                    }

                    if (cb) {
                        options['callback'] = cb;
                    } else {
                        options['targetKey'] = attrKey;
                    }

                    var binding = new Binding(options);
                    if(!twoWay && !(cb)){
                        binding.bind('destroy', function(){
                            delete bindingCache[cacheId];
                        });
                        bindingCache[cacheId] = binding
                    }

                    return binding;

                } else {
                    return scope.get(bindingDef.path);
                }
            }else{
                throw new Error("Couldn't find scope for " + pathElement.name);
            }

        },

        /***
         *
         * @param text
         * @param scope
         * @param attrKey
         * @param [bindingDefinitions]
         * @return {*}
         */
        evaluate: function (text, scope, attrKey, bindingDefinitions) {
            if (!_.isString(text)) {
                return text;
            }

            bindingDefinitions = bindingDefinitions || this.parse(text);

            var binding,
                bindings = [];

            for (var i = 0; i < bindingDefinitions.length; i++) {
                var bindingDef = bindingDefinitions[i];
                if (bindingDef.length) {
                    bindingDefinitions[i] = bindingDef;
                } else {
                    try {
                        binding = this.create(bindingDef, scope, attrKey, bindingDefinitions);
                    } catch (e) {
                        throw new Error("Create binding for '" + text + "'. " + e.message);
                    }

                    if (binding instanceof Binding) {
                        bindings.push(binding);
                        scope.$bindings[attrKey] = scope.$bindings[attrKey] || [];
                        scope.$bindings[attrKey].push(binding);
                    }
                    bindingDefinitions[i] = binding;
                }

            }

            if (bindings.length > 0) {
                return bindings[0].getContextValue();
            } else if (bindingDefinitions.length > 0) {
                if (bindingDefinitions.length === 1) {
                    return bindingDefinitions[0];
                }
                return Binding.contextToString(bindingDefinitions);
            } else {
                return text;
            }

        },

        parse: function(text) {
            if (_.isString(text)) {
                return Parser.parse(text, "text");
            }
        },

        parsePath: function(text){
            return Parser.parse(text, "path");
        },
        containsBindingDefinition: function(bindingDefinitions) {

            if (bindingDefinitions) {
                for (var i = 0; i < bindingDefinitions.length; i++) {
                    if (bindingDefinitions[i] instanceof Object) {
                        return true;
                    }
                }
            }

            return false;
        }
    });

});<|endoftext|>


<commit_before>/* jshint esversion:6 */

const MGDL_UNITS = 'mg/dL';
const MMOLL_UNITS = 'mmol/L';

module.exports = {
  MGDL_PER_MMOLL: 18.01559,
  MGDL_UNITS: 'mg/dL',
  MMOLL_UNITS: 'mmol/L',
  DEFAULT_BG_BOUNDS: {
  	[MGDL_UNITS]:{
  		veryLow: 54,
  		targetLower: 70,
  		targetUpper: 180,
  		veryHigh:250,
  	},
  	[MMOLL_UNITS]: {
  		veryLow: 3.0,
     	targetLower: 3.9,
      	targetUpper: 10.0,
      	veryHigh: 13.9,
  	},
  },
  BG_CLAMP_THRESHOLD: {
  	[MGDL_UNITS]: 600,
  	[MMOLL_UNITS]: 600/MGDL_PER_MMOLL,
  },
};
<commit_msg>Fix separate BG units<commit_after>/* jshint esversion:6 */

const MGDL_UNITS = 'mg/dL';
const MMOLL_UNITS = 'mmol/L';
const MGDL_PER_MMOLL = 18.01559;

module.exports = {
  MGDL_PER_MMOLL: 18.01559,
  MGDL_UNITS: 'mg/dL',
  MMOLL_UNITS: 'mmol/L',
  DEFAULT_BG_BOUNDS: {
  	[MGDL_UNITS]:{
  		veryLow: 54,
  		targetLower: 70,
  		targetUpper: 180,
  		veryHigh:250,
  	},
  	[MMOLL_UNITS]: {
  		veryLow: 3.0,
     	targetLower: 3.9,
      	targetUpper: 10.0,
      	veryHigh: 13.9,
  	},
  },
  BG_CLAMP_THRESHOLD: {
  	[MGDL_UNITS]: 600,
  	[MMOLL_UNITS]: 600/MGDL_PER_MMOLL,
  },
};
<|endoftext|>


<commit_before>import { List, Repeat } from 'immutable';
import { Modifier, CharacterMetadata, BlockMapBuilder, EditorState, ContentBlock, Entity, genKey } from 'draft-js';

export default function (editorState, selection, type, data, entityType = undefined, text = ' ') {
  const currentContentState = editorState.getCurrentContent();
  const currentSelectionState = selection;

  // in case text is selected it is removed and then the block is appended
  const afterRemovalContentState = Modifier.removeRange(
      currentContentState,
      currentSelectionState,
      'backward'
  );

  // deciding on the postion to split the text
  const targetSelection = afterRemovalContentState.getSelectionAfter();
  const blockKeyForTarget = targetSelection.get('focusKey');
  const block = currentContentState.getBlockForKey(blockKeyForTarget);
  let insertionTargetSelection;
  let insertionTargetBlock;

  // In case there are no characters or entity or the selection is at the start it
  // is safe to insert the block in the current block.
  // Otherwise a new block is created (the block is always its own block)
  const isEmptyBlock = block.getLength() === 0 && block.getEntityAt(0) === null;
  const selectedFromStart = currentSelectionState.getStartOffset() === 0;
  if (isEmptyBlock || selectedFromStart) {
    insertionTargetSelection = targetSelection;
    insertionTargetBlock = afterRemovalContentState;
  } else {
    // the only way to insert a new seems to be by splitting an existing in to two
    insertionTargetBlock = Modifier.splitBlock(afterRemovalContentState, targetSelection);

    // the position to insert our blocks
    insertionTargetSelection = insertionTargetBlock.getSelectionAfter();
  }

  // TODO not sure why we need it …
  const newContentStateAfterSplit = Modifier.setBlockType(insertionTargetBlock, insertionTargetSelection, type);

  // creating a new ContentBlock including the entity with data
  // Entity will be created with a specific type, if defined, else will fall back to the ContentBlock type
  const entityKey = Entity.create(entityType || type, 'IMMUTABLE', { ...data });
  const charData = CharacterMetadata.create({ entity: entityKey });

  const fragmentArray = [
    new ContentBlock({
      key: genKey(),
      type,
      text,
      characterList: List(Repeat(charData, text.length || 1)), // eslint-disable-line new-cap
    }),

    // new contentblock so we can continue wrting right away after inserting the block
    new ContentBlock({
      key: genKey(),
      type: 'unstyled',
      text: '',
      characterList: List(), // eslint-disable-line new-cap
    }),
  ];

  // create fragment containing the two content blocks
  const fragment = BlockMapBuilder.createFromArray(fragmentArray);

  // replace the contentblock we reserved for our insert
  const contentStateWithBlock = Modifier.replaceWithFragment(
      newContentStateAfterSplit,
      insertionTargetSelection,
      fragment
  );

  // update editor state with our new state including the block
  const newState = EditorState.push(
      editorState,
      contentStateWithBlock,
      `insert-${type}`
  );
  return EditorState.forceSelection(newState, contentStateWithBlock.getSelectionAfter());
}
<commit_msg>Removed unneeded undefined default arg<commit_after>import { List, Repeat } from 'immutable';
import { Modifier, CharacterMetadata, BlockMapBuilder, EditorState, ContentBlock, Entity, genKey } from 'draft-js';

export default function (editorState, selection, type, data, entityType, text = ' ') {
  const currentContentState = editorState.getCurrentContent();
  const currentSelectionState = selection;

  // in case text is selected it is removed and then the block is appended
  const afterRemovalContentState = Modifier.removeRange(
      currentContentState,
      currentSelectionState,
      'backward'
  );

  // deciding on the postion to split the text
  const targetSelection = afterRemovalContentState.getSelectionAfter();
  const blockKeyForTarget = targetSelection.get('focusKey');
  const block = currentContentState.getBlockForKey(blockKeyForTarget);
  let insertionTargetSelection;
  let insertionTargetBlock;

  // In case there are no characters or entity or the selection is at the start it
  // is safe to insert the block in the current block.
  // Otherwise a new block is created (the block is always its own block)
  const isEmptyBlock = block.getLength() === 0 && block.getEntityAt(0) === null;
  const selectedFromStart = currentSelectionState.getStartOffset() === 0;
  if (isEmptyBlock || selectedFromStart) {
    insertionTargetSelection = targetSelection;
    insertionTargetBlock = afterRemovalContentState;
  } else {
    // the only way to insert a new seems to be by splitting an existing in to two
    insertionTargetBlock = Modifier.splitBlock(afterRemovalContentState, targetSelection);

    // the position to insert our blocks
    insertionTargetSelection = insertionTargetBlock.getSelectionAfter();
  }

  // TODO not sure why we need it …
  const newContentStateAfterSplit = Modifier.setBlockType(insertionTargetBlock, insertionTargetSelection, type);

  // creating a new ContentBlock including the entity with data
  // Entity will be created with a specific type, if defined, else will fall back to the ContentBlock type
  const entityKey = Entity.create(entityType || type, 'IMMUTABLE', { ...data });
  const charData = CharacterMetadata.create({ entity: entityKey });

  const fragmentArray = [
    new ContentBlock({
      key: genKey(),
      type,
      text,
      characterList: List(Repeat(charData, text.length || 1)), // eslint-disable-line new-cap
    }),

    // new contentblock so we can continue wrting right away after inserting the block
    new ContentBlock({
      key: genKey(),
      type: 'unstyled',
      text: '',
      characterList: List(), // eslint-disable-line new-cap
    }),
  ];

  // create fragment containing the two content blocks
  const fragment = BlockMapBuilder.createFromArray(fragmentArray);

  // replace the contentblock we reserved for our insert
  const contentStateWithBlock = Modifier.replaceWithFragment(
      newContentStateAfterSplit,
      insertionTargetSelection,
      fragment
  );

  // update editor state with our new state including the block
  const newState = EditorState.push(
      editorState,
      contentStateWithBlock,
      `insert-${type}`
  );
  return EditorState.forceSelection(newState, contentStateWithBlock.getSelectionAfter());
}
<|endoftext|>


<commit_before>// Spreadsheet内にメニューを表示
function onOpen() {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var menuEntries = [
                       {name: "Export CSV", functionName: "exportSheetCsv"},
                       {name: "Export JSON", functionName: "exportSheetJson"}
                       ];
    ss.addMenu("Export Menu", menuEntries);
}

// Export CSV
function exportSheetCsv(e) {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var sheet = ss.getActiveSheet();
    var cellsData = sheet.getDataRange().getValues();

    var csvData = "";

    for(var i = 0; i < cellsData.length; i++){
        for(var j = 0; j < 4; j++){
            csvData += cellsData[i][j];
            csvData += ","
        }
        csvData += "\n";
    }

    // 時間取得
    var objDate=new Date();
    var Year = objDate.getFullYear();
    var Month = objDate.getMonth()+1;
    var Week = objDate.getDay();
    var Day = objDate.getDate();
    var Hour = objDate.getHours();
    var Minute = objDate.getMinutes();
    var Second = objDate.getSeconds();

    // "google-drive-kakeibo"フォルダ内にCSVファイルを生成
    // もし別のフォルダ名にしている場合は対象フォルダ名に書き換えること
    var folders = DriveApp.getFolders();
    while (folders.hasNext()) {
        var folder = folders.next();
        if(folder.getName() === "google-drive-kakeibo"){
            var file = folder.createFile("PaymentList_CSV_"+Year+Month+Day+"_"+Hour+Minute+Second+ ".csv", csvData);
        }
    }
}

// Export JSON
function exportSheetJson(e) {

    // 対象シート取得
    var book = SpreadsheetApp.getActiveSpreadsheet();
    var sheet = book.getSheetByName("PaymentList_form");

    // タイトルが格納されているセル - 1行目に格納されているものとする
    var firstRange = sheet.getRange(1, 1, 1, 4);

    // タイトル取得
    var firstRowValues = firstRange.getValues();
    var titleColumns = firstRowValues[0];

    // 値の読み取りを開始するセルを選択 - 2行目から値が入っているものとしている
    var valuesRowIndex = 2;

    // 値取得処理
    var lastRow = sheet.getLastRow();
    var rowValues = [];
    for(var rowIndex=valuesRowIndex; rowIndex<=lastRow; rowIndex++) {
        var rowNum = 1;

        // 取得するのはタイムスタンプから支払金額まで
        var range = sheet.getRange(rowIndex, 1, 1, 4);
        var values = range.getValues();
        rowValues.push(values[0]);
    }

    // create json
    var jsonArray = [];
    for(var i=0; i<rowValues.length; i++) {
        var line = rowValues[i];
        var json = new Object();
        for(var j=0; j<titleColumns.length; j++) {
            json[titleColumns[j]] = line[j];
        }
        jsonArray.push(json);
    }
    // json格納
    var json = jsonArray;

    // 時間取得
    var objDate=new Date();
    var Year = objDate.getFullYear();
    var Month = objDate.getMonth()+1;
    var Week = objDate.getDay();
    var Day = objDate.getDate();
    var Hour = objDate.getHours();
    var Minute = objDate.getMinutes();
    var Second = objDate.getSeconds();

    // "google-drive-kakeibo"フォルダ内にjsonファイルを生成
    // もし別のフォルダ名にしている場合は対象フォルダ名に書き換えること
    var folders = DriveApp.getFolders();
    while (folders.hasNext()) {
        var folder = folders.next();
        if(folder.getName() === "google-drive-kakeibo"){
            var file = folder.createFile("PaymentList_JSON_"+Year+Month+Day+"_"+Hour+Minute+Second+ ".json", JSON.stringify(json));
        }
    }
}
<commit_msg>ファイル名修正<commit_after><|endoftext|>


<commit_before>/**
 * Module that actually manages the musicpad
 */
angular.module('musicpad.pad', ['btford.socket-io', 'angular-audio-player', 'ui.bootstrap.dropdownToggle', 'monospaced.qrcode'])

/**
 * Setup route for this module
 */
.config(['$routeProvider', 'socketProvider', function ($routeProvider, socketProvider) {
  socketProvider.ioSocket = io.connect(null, {
    transports: ['websocket', 'xhr-polling'],
    'connect timeout': 3000,
    'max reconnection attempts': 5
  });

  $routeProvider.when('/:uniqueID', {
    controller: 'PadController',
    templateUrl: 'pad/pad.tpl.html',
    resolve: {
      'checkID': ['$q', '$route', function ($q, $route) {
        var paramCheck = $q.defer();
        /**
         * Regex or complex validation on uniqueID parameter, it could even be async!
         */
        // $timeout(function(){ Not needed for now!
        if ($route.current.params.uniqueID.length < 8) {
          paramCheck.reject('uniqueID');
        } else {
          paramCheck.resolve();
        }
        // },0);

        return paramCheck.promise;
      }]
    },
    // Not useful now, but this is good stuff!
    // redirectTo: function (params, current, search) {
    //   if ( params.uniqueID.length < 10 ) {
    //     return '/';
    //   }
    // },
    redirectMap: { 'uniqueID': '/' }
  });

}])

.directive('searchValidation', function () {
  return {
    require: 'ngModel',
    link: function (scope, elm, attrs, ctrl) {
      // Thanks to ridgerunner: http://stackoverflow.com/a/13034050/1071793
      var scRegex = /https?:\/\/(?:www.)?soundcloud.com\/([A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)*(?!\/sets(?:\/|$))(?:\/[A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)*){1,2}\/?)/,
      // Thanks to Chris Nolet: http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url#comment11747164_8260383
          ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/,
          elmButton = angular.element(elm[0].parentNode.querySelector('button'));

      ctrl.$parsers.unshift(function (value) {
        var shortURL = null;
        // Checks if the url provided is a sane youtube/soundcloud url
        if ((shortURL = value.match(scRegex)) !== null ) {
          ctrl.$setValidity(attrs.name, true); // useless ? :(
          scope.searchBoxType = 'sc';
          elmButton.addClass('btn-success'); elm.addClass('success');
          shortURL = shortURL.pop();
          elm.val(shortURL);
          return shortURL;
        } else if ((shortURL = value.match(ytRegex)) !== null) {
          ctrl.$setValidity(attrs.name, true); // useless ? :(
          scope.searchBoxType = 'yt';
          elmButton.addClass('btn-success'); elm.addClass('success');
          shortURL = shortURL.pop();
          elm.val(shortURL);
          return shortURL;
        } else {
          ctrl.$setValidity(attrs.name, false); // useless ? :(
          scope.searchBoxType = null;
          elmButton.removeClass('btn-success'); elm.removeClass('success');
        }
      });

      // Bind click on button, to clear btn-success class
      elmButton.bind('click', function () {
        elmButton.removeClass('btn-success');
        elm.removeClass('success');
      });
    }
  };
})

/**
 * And of course we define a controller for our route.
 */
.controller('PadController', [
  '$scope',
  '$routeParams',
  '$log',
  'titleService',
  'socket',
  function ($scope, $routeParams, $log, titleService, socket) {
    var servicePrefixes = {
      'yt': 'ytsux',
      'sc': ''
    };
    titleService.setTitle($routeParams.uniqueID);

    // NOTE: in case socket goes down, it makes it join the correct musicPad again.
    // pretty robust implementation :o
    socket.on('connect', function () {
      socket.emit('joinPad', $routeParams.uniqueID);
    });

    socket.on('ready', function () {
      $scope.padConnected = true;
      socket.forward('response', $scope); // setup socket news redirection to $scope
    });
    socket.on('disconnect', function () {
      $scope.padConnected = false;
      socket.removeListener('response');
    });

    $scope.addSong = function () {
      $log.info($scope.searchBox);
      socket.emit('request', { id: $scope.searchBox, type: $scope.searchBoxType });

      // request sent! GOOOO MUSICPAD!! we can clear the values for our next request :-)
      $scope.searchBox = null;
      $scope.searchBoxType = null;
    };

    $scope.uniqueID = $routeParams.uniqueID;

    /* Dummy infos.
    $scope.mainPlaylist = {};
    $scope.mainPlaylist['some-artist/some-track'] = {
      status: 'starting',
      title: 'Some Artist - Some Track!',
      hq: true,
      progress: 80
    };*/

    $scope.orderedPlaylist = [];
    $scope.audioPlaylist = [];
    $scope.playingNow = null;
    $scope.loadingSong = false;

    $scope.$on('socket:response', function (event, responseObj) {
      var status = responseObj.status,
          audioElements = [];
      $scope.mainPlaylist = $scope.mainPlaylist || {};

      if (status === 'starting') {
        responseObj.progress = 0;
        $scope.mainPlaylist[responseObj.id] = responseObj;
        $scope.orderedPlaylist.push($scope.mainPlaylist[responseObj.id]); // enqueue in order!
        $log.info($scope.mainPlaylist[responseObj.id]);
      } else if (status === 'progress') {
        angular.extend($scope.mainPlaylist[responseObj.id], responseObj);
      } else if (status === 'complete') {
        responseObj.progress = 100;
        audioElements.push({ src: '/assets/' + responseObj.service + '/' + servicePrefixes[responseObj.service] + responseObj.id + '.mp3', type: 'audio/mpeg' });
        audioElements.push({ src: '/assets/' + responseObj.service + '/' + servicePrefixes[responseObj.service] + responseObj.id + '.ogg', type: 'audio/ogg' });
        responseObj.audioElements = audioElements;

        // In case the song is cached, you ONLY receive 'complete' event.
        // So let's handle it!
        if (!$scope.mainPlaylist[responseObj.id]) {
          $scope.mainPlaylist[responseObj.id] = responseObj;
          $scope.orderedPlaylist.push($scope.mainPlaylist[responseObj.id]);
          $scope.audioPlaylist.push(audioElements);
          return;
        }

        angular.extend($scope.mainPlaylist[responseObj.id], responseObj);
        $scope.audioPlaylist.push(audioElements);
        $log.info($scope.mainPlaylist[responseObj.id]);
      }
    });

    // On pause we don't play anything ;)
    $scope.$on('audioplayer:pause', function (event) {
      $scope.playingNow = null;
    });
    $scope.$on('audioplayer:play', function (event, playlistIndex) {
      $scope.loadingSong = false;
      $scope.playingNow = $scope.orderedPlaylist[playlistIndex];
    });
    $scope.$on('audioplayer:load', function (event, autoplayNext) {
      $scope.loadingSong = autoplayNext;
    });

    $scope.playPause = function (index) {
      if ($scope.orderedPlaylist[index].status === 'complete') {
        $scope.audio.playPause(index);
      }
    };

  }
]);
<commit_msg>change accordingly to my angular-socket-io fork<commit_after>/**
 * Module that actually manages the musicpad
 */
angular.module('musicpad.pad', ['btford.socket-io', 'angular-audio-player', 'ui.bootstrap.dropdownToggle', 'monospaced.qrcode'])

/**
 * Setup route for this module
 */
.config(['$routeProvider', 'socketProvider', function ($routeProvider, socketProvider) {
  socketProvider.socketOptions = {
    transports: ['websocket', 'xhr-polling'],
    'connect timeout': 3000,
    'max reconnection attempts': 5
  };

  $routeProvider.when('/:uniqueID', {
    controller: 'PadController',
    templateUrl: 'pad/pad.tpl.html',
    resolve: {
      'checkID': ['$q', '$route', function ($q, $route) {
        var paramCheck = $q.defer();
        /**
         * Regex or complex validation on uniqueID parameter, it could even be async!
         */
        // $timeout(function(){ Not needed for now!
        if ($route.current.params.uniqueID.length < 8) {
          paramCheck.reject('uniqueID');
        } else {
          paramCheck.resolve();
        }
        // },0);

        return paramCheck.promise;
      }]
    },
    // Not useful now, but this is good stuff!
    // redirectTo: function (params, current, search) {
    //   if ( params.uniqueID.length < 10 ) {
    //     return '/';
    //   }
    // },
    redirectMap: { 'uniqueID': '/' }
  });

}])

.directive('searchValidation', function () {
  return {
    require: 'ngModel',
    link: function (scope, elm, attrs, ctrl) {
      // Thanks to ridgerunner: http://stackoverflow.com/a/13034050/1071793
      var scRegex = /https?:\/\/(?:www.)?soundcloud.com\/([A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)*(?!\/sets(?:\/|$))(?:\/[A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)*){1,2}\/?)/,
      // Thanks to Chris Nolet: http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url#comment11747164_8260383
          ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/,
          elmButton = angular.element(elm[0].parentNode.querySelector('button'));

      ctrl.$parsers.unshift(function (value) {
        var shortURL = null;
        // Checks if the url provided is a sane youtube/soundcloud url
        if ((shortURL = value.match(scRegex)) !== null ) {
          ctrl.$setValidity(attrs.name, true); // useless ? :(
          scope.searchBoxType = 'sc';
          elmButton.addClass('btn-success'); elm.addClass('success');
          shortURL = shortURL.pop();
          elm.val(shortURL);
          return shortURL;
        } else if ((shortURL = value.match(ytRegex)) !== null) {
          ctrl.$setValidity(attrs.name, true); // useless ? :(
          scope.searchBoxType = 'yt';
          elmButton.addClass('btn-success'); elm.addClass('success');
          shortURL = shortURL.pop();
          elm.val(shortURL);
          return shortURL;
        } else {
          ctrl.$setValidity(attrs.name, false); // useless ? :(
          scope.searchBoxType = null;
          elmButton.removeClass('btn-success'); elm.removeClass('success');
        }
      });

      // Bind click on button, to clear btn-success class
      elmButton.bind('click', function () {
        elmButton.removeClass('btn-success');
        elm.removeClass('success');
      });
    }
  };
})

/**
 * And of course we define a controller for our route.
 */
.controller('PadController', [
  '$scope',
  '$routeParams',
  '$log',
  'titleService',
  'socket',
  function ($scope, $routeParams, $log, titleService, socket) {
    var servicePrefixes = {
      'yt': 'ytsux',
      'sc': ''
    };
    titleService.setTitle($routeParams.uniqueID);

    // NOTE: in case socket goes down, it makes it join the correct musicPad again.
    // pretty robust implementation :o
    socket.on('connect', function () {
      socket.emit('joinPad', $routeParams.uniqueID);
    });

    socket.on('ready', function () {
      $scope.padConnected = true;
      socket.forward('response', $scope); // setup socket news redirection to $scope
    });
    socket.on('disconnect', function () {
      $scope.padConnected = false;
      socket.removeListener('response');
    });

    $scope.addSong = function () {
      $log.info($scope.searchBox);
      socket.emit('request', { id: $scope.searchBox, type: $scope.searchBoxType });

      // request sent! GOOOO MUSICPAD!! we can clear the values for our next request :-)
      $scope.searchBox = null;
      $scope.searchBoxType = null;
    };

    $scope.uniqueID = $routeParams.uniqueID;

    /* Dummy infos.
    $scope.mainPlaylist = {};
    $scope.mainPlaylist['some-artist/some-track'] = {
      status: 'starting',
      title: 'Some Artist - Some Track!',
      hq: true,
      progress: 80
    };*/

    $scope.orderedPlaylist = [];
    $scope.audioPlaylist = [];
    $scope.playingNow = null;
    $scope.loadingSong = false;

    $scope.$on('socket:response', function (event, responseObj) {
      var status = responseObj.status,
          audioElements = [];
      $scope.mainPlaylist = $scope.mainPlaylist || {};

      if (status === 'starting') {
        responseObj.progress = 0;
        $scope.mainPlaylist[responseObj.id] = responseObj;
        $scope.orderedPlaylist.push($scope.mainPlaylist[responseObj.id]); // enqueue in order!
        $log.info($scope.mainPlaylist[responseObj.id]);
      } else if (status === 'progress') {
        angular.extend($scope.mainPlaylist[responseObj.id], responseObj);
      } else if (status === 'complete') {
        responseObj.progress = 100;
        audioElements.push({ src: '/assets/' + responseObj.service + '/' + servicePrefixes[responseObj.service] + responseObj.id + '.mp3', type: 'audio/mpeg' });
        audioElements.push({ src: '/assets/' + responseObj.service + '/' + servicePrefixes[responseObj.service] + responseObj.id + '.ogg', type: 'audio/ogg' });
        responseObj.audioElements = audioElements;

        // In case the song is cached, you ONLY receive 'complete' event.
        // So let's handle it!
        if (!$scope.mainPlaylist[responseObj.id]) {
          $scope.mainPlaylist[responseObj.id] = responseObj;
          $scope.orderedPlaylist.push($scope.mainPlaylist[responseObj.id]);
          $scope.audioPlaylist.push(audioElements);
          return;
        }

        angular.extend($scope.mainPlaylist[responseObj.id], responseObj);
        $scope.audioPlaylist.push(audioElements);
        $log.info($scope.mainPlaylist[responseObj.id]);
      }
    });

    // On pause we don't play anything ;)
    $scope.$on('audioplayer:pause', function (event) {
      $scope.playingNow = null;
    });
    $scope.$on('audioplayer:play', function (event, playlistIndex) {
      $scope.loadingSong = false;
      $scope.playingNow = $scope.orderedPlaylist[playlistIndex];
    });
    $scope.$on('audioplayer:load', function (event, autoplayNext) {
      $scope.loadingSong = autoplayNext;
    });

    $scope.playPause = function (index) {
      if ($scope.orderedPlaylist[index].status === 'complete') {
        $scope.audio.playPause(index);
      }
    };

  }
]);
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const mkdirp = require('mkdirp');
const datafire = require('datafire');

const iterateIntegs = require('./iterate-integrations');

const MAX_DESCRIPTION_LENGTH = 120;

const maybeMkdirp = (dir) => {
  try {
    mkdirp.sync(dir);
  } catch (e) {
    if (e.code !== 'EEXIST') throw e;
  }
}

const truncateDescription = (desc) => {
  if (!desc) return;
  desc = desc || '';
  desc = desc.replace(/<(?:.|\n)*?>/gm, '');
  let newLine = desc.indexOf('\n');
  if (newLine > -1) {
    desc = desc.substring(0, newLine);
  }
  if (desc.length > MAX_DESCRIPTION_LENGTH) {
    desc = desc.substring(0, MAX_DESCRIPTION_LENGTH) + '...';
  }
  return desc;
}

const BASE_DIR = __dirname + '/../integrations/';
const OUT_DIR = __dirname + '/../json/';

let list = {};
iterateIntegs((dir, name, integ) => {
    console.log('adding', name);
    let package = require(path.join(dir, 'package.json'));
    let openapiFile = path.join(dir, 'openapi.json');
    let openapi = {info: {}};
    if (fs.existsSync(openapiFile)) {
      openapi = JSON.parse(fs.readFileSync(openapiFile, 'utf8'));
    }
    if (list[name]) throw new Error("Duplicate name " + name);
    list[name] = {
      id: name,
      title: integ.title,
      description: integ.description,
      security: integ.security,
      logo: openapi.info['x-logo'],
      tags: (openapi.info['x-apisguru-categories'] || []).map(t => t.replace(/_/g, ' ')),
    };
    if (name.indexOf('google_') === 0) list[name].tags.push('google');
    if (name.indexOf('amazonaws_') === 0) list[name].tags.push('aws');
    if (name.indexOf('azure_') === 0) list[name].tags.push('azure');
    let details = Object.assign({}, list[name]);
    list[name].description = truncateDescription(list[name].description);
    list[name].actionCount = integ.allActions.length;
    list[name].latestVersion = package.version;

    details.actions = integ.allActions.map(action => {
      details.definitions = details.definitions || action.inputSchema.definitions;
      let actionDetails = {
        id: action.id.split('/')[1],
        title: action.title,
        description: action.description,
        inputSchema: Object.assign({definitions: null}, action.inputSchema),
        outputSchema: Object.assign({definitions: null}, action.outputSchema),
      }
      if (action.security && action.security[name]) {
        actionDetails.security = {};
        actionDetails.security[name] = {integration: name};
      }
      delete actionDetails.inputSchema.definitions;
      delete actionDetails.outputSchema.definitions;
      return actionDetails;
    })
    let integDir = path.join(OUT_DIR, name, package.version);
    maybeMkdirp(integDir);
    fs.writeFileSync(path.join(integDir, 'index.json'), JSON.stringify(details, null, 2));
})

fs.writeFileSync(OUT_DIR + 'list.json', JSON.stringify(list, null, 2));

<commit_msg>add args.name to generate-json script<commit_after>const fs = require('fs');
const path = require('path');
const mkdirp = require('mkdirp');
const datafire = require('datafire');
const args = require('yargs').argv;

const iterateIntegs = require('./iterate-integrations');

const MAX_DESCRIPTION_LENGTH = 120;

const maybeMkdirp = (dir) => {
  try {
    mkdirp.sync(dir);
  } catch (e) {
    if (e.code !== 'EEXIST') throw e;
  }
}

const truncateDescription = (desc) => {
  if (!desc) return;
  desc = desc || '';
  desc = desc.replace(/<(?:.|\n)*?>/gm, '');
  let newLine = desc.indexOf('\n');
  if (newLine > -1) {
    desc = desc.substring(0, newLine);
  }
  if (desc.length > MAX_DESCRIPTION_LENGTH) {
    desc = desc.substring(0, MAX_DESCRIPTION_LENGTH) + '...';
  }
  return desc;
}

const BASE_DIR = __dirname + '/../integrations/';
const OUT_DIR = __dirname + '/../json/';

let list = {};
if (args.name) list = require(OUT_DIR + 'list.json');
iterateIntegs((dir, name, integ) => {
    console.log('adding', name);
    let package = require(path.join(dir, 'package.json'));
    let openapiFile = path.join(dir, 'openapi.json');
    let openapi = {info: {}};
    if (fs.existsSync(openapiFile)) {
      openapi = JSON.parse(fs.readFileSync(openapiFile, 'utf8'));
    }
    if (list[name] && !args.name) throw new Error("Duplicate name " + name);
    list[name] = {
      id: name,
      title: integ.title,
      description: integ.description,
      security: integ.security,
      logo: openapi.info['x-logo'],
      tags: (openapi.info['x-apisguru-categories'] || []).map(t => t.replace(/_/g, ' ')),
    };
    if (name.indexOf('google_') === 0) list[name].tags.push('google');
    if (name.indexOf('amazonaws_') === 0) list[name].tags.push('aws');
    if (name.indexOf('azure_') === 0) list[name].tags.push('azure');
    let details = Object.assign({}, list[name]);
    list[name].description = truncateDescription(list[name].description);
    list[name].actionCount = integ.allActions.length;
    list[name].latestVersion = package.version;

    details.actions = integ.allActions.map(action => {
      details.definitions = details.definitions || action.inputSchema.definitions;
      let actionDetails = {
        id: action.id.split('/')[1],
        title: action.title,
        description: action.description,
        inputSchema: Object.assign({definitions: null}, action.inputSchema),
        outputSchema: Object.assign({definitions: null}, action.outputSchema),
      }
      if (action.security && action.security[name]) {
        actionDetails.security = {};
        actionDetails.security[name] = {integration: name};
      }
      delete actionDetails.inputSchema.definitions;
      delete actionDetails.outputSchema.definitions;
      return actionDetails;
    })
    let integDir = path.join(OUT_DIR, name, package.version);
    maybeMkdirp(integDir);
    fs.writeFileSync(path.join(integDir, 'index.json'), JSON.stringify(details, null, 2));
}, name => !args.name || name === args.name)

fs.writeFileSync(OUT_DIR + 'list.json', JSON.stringify(list, null, 2));

<|endoftext|>


<commit_before>
const RG = require('./rg');

RG.Factory = require('./factory');
RG.Factory.Battle = require('./factory.battle');
const OW = require('./overworld.map');

const debug = require('debug')('bitn:GameMaster');

/* GameMaster objects reacts to various events caused by player and other
 * actors, and shapes the game world based on them. For example,
 * GameMaster can:
 *   - start battles on the area player entered
 *   - spawn special events and actors
 *   - spawn special items etc.
 */
const GameMaster = function(game) {
    this.player = null;
    this.game = game;
    this.fact = new RG.Factory.Battle(game);

    // Lookup table for battles by level ID
    this.battles = {};

    this.battlesDone = {};

    this.setPool = pool => {this.pool = pool;};
    this.setGame = game => {this.game = game;};

    this.setPlayer = player => {
        this.player = player;
    };
    this.setWorld = world => {this.world = world;};

    this.hasNotify = true;
    this.notify = (evtName, args) => {
        if (evtName === RG.EVT_LEVEL_CHANGED) {
            debug('EVT_LEVEL_CHANGED');
            const {actor} = args;
            if (actor.isPlayer()) {
                if (!actor.has('InBattle')) {
                    this.addPlayerToBattle(args);
                }
                else {
                    this.removePlayerFromBattle(args);
                }
            }
        }
        else if (evtName === RG.EVT_TILE_CHANGED) {
            debug('EVT_TILE_CHANGED');
            // Should spawn battle etc event
            if (!this.player) {
                debug('\tBut player is NULL for the moment');
                return;
            }
            debug('\tPlayer not null. Creating battle');
            const level = this.player.getLevel();
            const id = level.getID();

            const ow = this.game.getOverWorld();
            let maxDanger = 4;
            let armySize = 20;
            const battleConf = {};
            let levelType = 'forest';
            if (ow) {
                const world = this.game.getCurrentWorld();
                const area = world.getAreas()[0];
                const xy = area.findTileXYById(id);
                // TODO use actual starting position
                const startX = 2;
                const startY = ow.getSizeY() - 1;
                const dX = Math.abs(startX - xy[0]);
                const dY = Math.abs(startY - xy[1]);
                maxDanger += dX + dY;
                armySize += 10 * dY + 5 * dX;
                console.log('armySize will be ' + armySize);
                const biome = ow.getBiome(xy[0], xy[1]);
                levelType = this.biomeToLevelType(biome);
            }
            battleConf.maxDanger = maxDanger;
            battleConf.armySize = armySize;
            battleConf.levelType = levelType;

            if (!this.battles.hasOwnProperty(id)) {
                const battle = this.fact.createBattle(level, battleConf);
                this.battles[id] = battle;
                this.game.addBattle(this.battles[id]);
            }
        }
        else if (evtName === RG.EVT_BATTLE_OVER) {
            const {battle} = args;
            debug(`EVT_BATTLE_OVER: ${battle.getName()}`);
            const id = battle.getLevel().getID();

            debug(`1. battlesDone for ${id}: ${this.battlesDone[id]}`);
            if (!this.battlesDone[id] && battle) {
                debug(`2. battlesDone for ${id}: ${this.battlesDone[id]}`);
                this.battlesDone[id] = true;
                debug(`3. battlesDone for ${id}: ${this.battlesDone[id]}`);
                this.addBadgesForActors(battle);
                this.moveActorsOutOfBattle(battle);
                const bName = battle.getName();
                RG.gameMsg(`Battle ${bName} is over!`);
            }
            else {
                const json = JSON.stringify(args);
                RG.err('Game.Master', 'notify',
                    `Args ${json} does not contain "battle"`);
            }
            debug('GameMaster registered battle over');
            // TODO delete the battle (but keep the level)
        }
    };
    RG.POOL.listenEvent(RG.EVT_LEVEL_CHANGED, this);
    RG.POOL.listenEvent(RG.EVT_TILE_CHANGED, this);
    RG.POOL.listenEvent(RG.EVT_BATTLE_OVER, this);

    /* Adds player to the battle level. */
    this.addPlayerToBattle = function(args) {
        const {actor, target, src} = args;
        const srcID = src.getID();
        if (this.battles.hasOwnProperty(srcID)) {
            const battle = this.battles[srcID];
            const battleLevel = battle.getLevel();
            if (battleLevel.getID() === target.getID()) {
                if (this.actorCanEnter(actor, battle)) {
                    // Entered a battle
                    const comp = new RG.Component.InBattle();
                    comp.setData({name: battle.getName()});
                    actor.add(comp);
                    // Get army selection object
                    const obj = this.getSelArmyObject(actor, battle);
                    actor.getBrain().setSelectionObject(obj);
                }
                else if (battle.isOver()) {
                    RG.gameMsg('Looks like the battle is already fought..');
                }
                else {
                    RG.gameMsg('You cannot join the fight anymore, deserter.');
                }
            }
        }

    };

    /* Returns true if the actor can still enter the battle as an army member.
     * */
    this.actorCanEnter = (actor, battle) => {
        if (battle.isOver()) {return false;}
        if (this.actorDesertedBattle(actor, battle)) {return false;}
        return true;
    };

    /* Removes the player from a battle. */
    this.removePlayerFromBattle = function(args) {
        const {actor, target, src} = args;
        const areaID = target.getID();
        const srcID = src.getID();
        const battle = this.battles[areaID];
        const battleLevID = battle.getLevel().getID();

        const inBattleComp = actor.get('InBattle');
        const battleData = inBattleComp.getData();

        if (srcID !== battleLevID) {
            const msg = `Level ID mismatch: ${srcID} !== ${battleLevID}`;
            RG.err('GameMaster', 'removePlayerFromBattle', msg);
        }

        // Mark player as deserter, TODO add confirm object
        if (!battle.isOver() && battleData.army) {
            const badge = new RG.Component.BattleBadge();
            badge.setData({status: 'Fled', name: battle.getName(),
                army: battleData.army});
            actor.add(badge);
            actor.remove('InBattle');
            actor.add(new RG.Component.BattleOver());
        }
    };

    /* Adds BattleBadges after a battle is over. */
    this.addBadgesForActors = battle => {
        const armies = battle.getArmies();
        armies.forEach(army => {
            const actors = army.getActors();
            const ids = actors.map(actor => actor.getID());

            actors.forEach(actor => {
                if (!this.actorDesertedBattle(actor, battle)) {
                    const badge = new RG.Component.BattleBadge();
                    const battleData = {
                        name: battle.getName(),
                        army: army.getName(),
                        allies: ids,
                        status: army.isDefeated() ? 'Lost' : 'Won'
                    };
                    badge.setData(battleData);
                    actor.add(badge);

                    actor.remove('InBattle');
                    actor.add(new RG.Component.BattleOver());
                }
            });
        });
    };

    this.actorDesertedBattle = (actor, battle) => {
        const badgeList = actor.getList('BattleBadge');
        const badge = badgeList.find(b => (
            b.getData().name === battle.getName()
        ));
        if (badge) {return true;}
        return false;
    };

    /* Moves actors out of the battle level into the parent level of the battle
     * (at the moment this is always Area.Tile level. */
    this.moveActorsOutOfBattle = battle => {
        const level = battle.getLevel();
        const conns = level.getConnections();

        if (!conns || conns.length === 0) {
            RG.err('Game.Master', 'moveActorsOutOfBattle',
                'No exit connnection in level');
        }

        const exit = conns[0];
        const targetLevel = exit.getTargetLevel();

        const armies = battle.getArmies();
        armies.forEach(army => {
            const actors = army.getActors();
            actors.forEach(actor => {
                if (!actor.isPlayer()) {
                    if (level.removeActor(actor)) {
                        debug(`Rm actor ${actor.getID()},${actor.getName()}`);
                        targetLevel.addActorToFreeCell(actor);
                    }
                    else {
                        const json = JSON.stringify(actor.toJSON());
                        RG.err('Game.Master', 'moveActorsOutOfBattle',
                            `level.removeActor failed for actor ${json}`);
                    }
                }
                else {
                    const selObj = this.getSelLeaveBattle(actor, level);
                    actor.getBrain().setSelectionObject(selObj);
                }
            });
        });

    };

    /* Returns the selection object for player to select an army. */
    this.getSelArmyObject = function(player, battle) {
        return {
            showMenu: () => true,
            getMenu: () => {
                RG.gameMsg('Please select an army to join:');
                const armies = battle.getArmies();
                const menuObj = {};
                armies.forEach((army, i) => {
                    menuObj[i] = ' Army ' + army.getName();
                });
                menuObj['Any other key'] = 'Take no side';
                return menuObj;
            },
            select: code => {
                const selection = RG.codeToIndex(code);
                const armies = battle.getArmies();
                if (selection < armies.length) {
                    const army = armies[selection];
                    return () => {
                        const battleLevel = battle.getLevel();
                        let armyActors = army.getActors();
                        const nActors = armyActors.length;
                        const pIndex = RG.RAND.getUniformInt(0, nActors);
                        const replacedActor = armyActors[pIndex];
                        const [pX, pY] = replacedActor.getXY();

                        // Remove substituted actor from army/level
                        replacedActor.get('Action').disable();
                        army.removeActor(replacedActor);
                        battleLevel.removeActor(replacedActor);

                        armyActors = army.getActors();
                        army.addActor(player);

                        player.get('InBattle').updateData({army: army.getName});
                        armyActors.forEach(actor => {
                            actor.addFriend(player);
                        });

                        armies.forEach(enemyArmy => {
                            if (enemyArmy !== army) {
                                const enemies = enemyArmy.getActors();
                                enemies.forEach(enemy => {
                                    enemy.addEnemy(player);
                                });
                            }
                        });

                        if (!battleLevel.moveActorTo(player, pX, pY)) {
                            RG.err('GameMaster', 'getSelArmyObject',
                                `Could not move player to ${pX},${pY}`);
                        }
                    };
                }
                return null;
            }
        };
    };

    this.getSelLeaveBattle = function(player, level) {
        const selObj = function() {
            this.showMenu = () => true;
            this.getMenu = () => {
                RG.gameMsg('Battle is over! Do you want to leave battle?');
                return {
                    0: 'Leave immediately',
                    1: 'Stay behind to scavenge the bodies of the dead.'
                };
            };
            this.select = code => {
                const selection = RG.codeToIndex(code);
                if (selection === 0) {
                    return () => {
                        const exit = level.getConnections()[0];
                        if (!exit.useStairs(player)) {
                            RG.err('GameMaster', 'moveActorsOutOfBattle',
                                'Cannot move player via useStairs');
                        }
                        else {
                            const name = player.getName();
                            RG.gameMsg(`${name} leaves the battlefield`);
                        }
                    };
                }
                if (selection === 1) {
                    // TODO add some dishonor for the player or some necromancy
                    // effect etc
                    return null;
                }
                return this;
            };
        };
        return new selObj();
    };

    /* Serializes the object into JSON. */
    this.toJSON = function() {
        const keys = Object.keys(this.battles);
        const battles = {};
        keys.forEach(id => {
            battles[id] = this.battles[id].toJSON();
        });
        return {
            battles,
            battlesDone: this.battlesDone
        };
    };

    this.biomeToLevelType = function(biome) {
        switch (biome) {
            case OW.BIOME.ARCTIC: return 'arctic';
            case OW.BIOME.TUNDRA: return 'arctic';
            case OW.BIOME.ALPINE: return 'mountain';
            case OW.BIOME.TAIGA: return 'forest';
            default: return 'forest';
        }
    };
};

module.exports = GameMaster;
<commit_msg>Added getBattleLevels(). Used mainly for testing.<commit_after>
const RG = require('./rg');

RG.Factory = require('./factory');
RG.Factory.Battle = require('./factory.battle');
const OW = require('./overworld.map');

const debug = require('debug')('bitn:GameMaster');

/* GameMaster objects reacts to various events caused by player and other
 * actors, and shapes the game world based on them. For example,
 * GameMaster can:
 *   - start battles on the area player entered
 *   - spawn special events and actors
 *   - spawn special items etc.
 */
const GameMaster = function(game) {
    this.player = null;
    this.game = game;
    this.fact = new RG.Factory.Battle(game);

    // Lookup table for battles by level ID
    this.battles = {};

    this.battlesDone = {};

    this.setPool = pool => {this.pool = pool;};
    this.setGame = game => {this.game = game;};

    this.setPlayer = player => {
        this.player = player;
    };
    this.setWorld = world => {this.world = world;};

    this.hasNotify = true;
    this.notify = (evtName, args) => {
        if (evtName === RG.EVT_LEVEL_CHANGED) {
            debug('EVT_LEVEL_CHANGED');
            const {actor} = args;
            if (actor.isPlayer()) {
                if (!actor.has('InBattle')) {
                    this.addPlayerToBattle(args);
                }
                else {
                    this.removePlayerFromBattle(args);
                }
            }
        }
        else if (evtName === RG.EVT_TILE_CHANGED) {
            debug('EVT_TILE_CHANGED');
            // Should spawn battle etc event
            if (!this.player) {
                debug('\tBut player is NULL for the moment');
                return;
            }
            debug('\tPlayer not null. Creating battle');
            const level = this.player.getLevel();
            const id = level.getID();

            const ow = this.game.getOverWorld();
            let maxDanger = 4;
            let armySize = 20;
            const battleConf = {};
            let levelType = 'forest';
            if (ow) {
                const world = this.game.getCurrentWorld();
                const area = world.getAreas()[0];
                const xy = area.findTileXYById(id);
                // TODO use actual starting position
                const startX = 2;
                const startY = area.getMaxY() - 1;
                const dX = Math.abs(startX - xy[0]);
                const dY = Math.abs(startY - xy[1]);
                maxDanger += dX + dY;
                armySize += 10 * dY + 5 * dX;
                console.log('armySize will be ' + armySize);

                const owPos = this.game.getPlayerOwPos();
                const biome = ow.getBiome(owPos[0], owPos[1]);
                levelType = this.biomeToLevelType(biome);
            }
            battleConf.maxDanger = maxDanger;
            battleConf.armySize = armySize;
            battleConf.levelType = levelType;

            if (!this.battles.hasOwnProperty(id)) {
                const battle = this.fact.createBattle(level, battleConf);
                this.battles[id] = battle;
                this.game.addBattle(this.battles[id]);
            }
        }
        else if (evtName === RG.EVT_BATTLE_OVER) {
            const {battle} = args;
            debug(`EVT_BATTLE_OVER: ${battle.getName()}`);
            const id = battle.getLevel().getID();

            debug(`1. battlesDone for ${id}: ${this.battlesDone[id]}`);
            if (!this.battlesDone[id] && battle) {
                debug(`2. battlesDone for ${id}: ${this.battlesDone[id]}`);
                this.battlesDone[id] = true;
                debug(`3. battlesDone for ${id}: ${this.battlesDone[id]}`);
                this.addBadgesForActors(battle);
                this.moveActorsOutOfBattle(battle);
                const bName = battle.getName();
                RG.gameMsg(`Battle ${bName} is over!`);
            }
            else {
                const json = JSON.stringify(args);
                RG.err('Game.Master', 'notify',
                    `Args ${json} does not contain "battle"`);
            }
            debug('GameMaster registered battle over');
            // TODO delete the battle (but keep the level)
        }
    };
    RG.POOL.listenEvent(RG.EVT_LEVEL_CHANGED, this);
    RG.POOL.listenEvent(RG.EVT_TILE_CHANGED, this);
    RG.POOL.listenEvent(RG.EVT_BATTLE_OVER, this);

    /* Adds player to the battle level. */
    this.addPlayerToBattle = function(args) {
        const {actor, target, src} = args;
        const srcID = src.getID();
        if (this.battles.hasOwnProperty(srcID)) {
            const battle = this.battles[srcID];
            const battleLevel = battle.getLevel();
            if (battleLevel.getID() === target.getID()) {
                if (this.actorCanEnter(actor, battle)) {
                    // Entered a battle
                    const comp = new RG.Component.InBattle();
                    comp.setData({name: battle.getName()});
                    actor.add(comp);
                    // Get army selection object
                    const obj = this.getSelArmyObject(actor, battle);
                    actor.getBrain().setSelectionObject(obj);
                }
                else if (battle.isOver()) {
                    RG.gameMsg('Looks like the battle is already fought..');
                }
                else {
                    RG.gameMsg('You cannot join the fight anymore, deserter.');
                }
            }
        }

    };

    /* Returns true if the actor can still enter the battle as an army member.
     * */
    this.actorCanEnter = (actor, battle) => {
        if (battle.isOver()) {return false;}
        if (this.actorDesertedBattle(actor, battle)) {return false;}
        return true;
    };

    /* Removes the player from a battle. */
    this.removePlayerFromBattle = function(args) {
        const {actor, target, src} = args;
        const areaID = target.getID();
        const srcID = src.getID();
        const battle = this.battles[areaID];
        const battleLevID = battle.getLevel().getID();

        const inBattleComp = actor.get('InBattle');
        const battleData = inBattleComp.getData();

        if (srcID !== battleLevID) {
            const msg = `Level ID mismatch: ${srcID} !== ${battleLevID}`;
            RG.err('GameMaster', 'removePlayerFromBattle', msg);
        }

        // Mark player as deserter, TODO add confirm object
        if (!battle.isOver() && battleData.army) {
            const badge = new RG.Component.BattleBadge();
            badge.setData({status: 'Fled', name: battle.getName(),
                army: battleData.army});
            actor.add(badge);
            actor.remove('InBattle');
            actor.add(new RG.Component.BattleOver());
        }
    };

    /* Adds BattleBadges after a battle is over. */
    this.addBadgesForActors = battle => {
        const armies = battle.getArmies();
        armies.forEach(army => {
            const actors = army.getActors();
            const ids = actors.map(actor => actor.getID());

            actors.forEach(actor => {
                if (!this.actorDesertedBattle(actor, battle)) {
                    const badge = new RG.Component.BattleBadge();
                    const battleData = {
                        name: battle.getName(),
                        army: army.getName(),
                        allies: ids,
                        status: army.isDefeated() ? 'Lost' : 'Won'
                    };
                    badge.setData(battleData);
                    actor.add(badge);

                    actor.remove('InBattle');
                    actor.add(new RG.Component.BattleOver());
                }
            });
        });
    };

    this.actorDesertedBattle = (actor, battle) => {
        const badgeList = actor.getList('BattleBadge');
        const badge = badgeList.find(b => (
            b.getData().name === battle.getName()
        ));
        if (badge) {return true;}
        return false;
    };

    /* Moves actors out of the battle level into the parent level of the battle
     * (at the moment this is always Area.Tile level. */
    this.moveActorsOutOfBattle = battle => {
        const level = battle.getLevel();
        const conns = level.getConnections();

        if (!conns || conns.length === 0) {
            RG.err('Game.Master', 'moveActorsOutOfBattle',
                'No exit connnection in level');
        }

        const exit = conns[0];
        const targetLevel = exit.getTargetLevel();

        const armies = battle.getArmies();
        armies.forEach(army => {
            const actors = army.getActors();
            actors.forEach(actor => {
                if (!actor.isPlayer()) {
                    if (level.removeActor(actor)) {
                        debug(`Rm actor ${actor.getID()},${actor.getName()}`);
                        targetLevel.addActorToFreeCell(actor);
                    }
                    else {
                        const json = JSON.stringify(actor.toJSON());
                        RG.err('Game.Master', 'moveActorsOutOfBattle',
                            `level.removeActor failed for actor ${json}`);
                    }
                }
                else {
                    const selObj = this.getSelLeaveBattle(actor, level);
                    actor.getBrain().setSelectionObject(selObj);
                }
            });
        });

    };

    /* Returns the selection object for player to select an army. */
    this.getSelArmyObject = function(player, battle) {
        return {
            showMenu: () => true,
            getMenu: () => {
                RG.gameMsg('Please select an army to join:');
                const armies = battle.getArmies();
                const menuObj = {};
                armies.forEach((army, i) => {
                    menuObj[i] = ' Army ' + army.getName();
                });
                menuObj['Any other key'] = 'Take no side';
                return menuObj;
            },
            select: code => {
                const selection = RG.codeToIndex(code);
                const armies = battle.getArmies();
                if (selection < armies.length) {
                    const army = armies[selection];
                    return () => {
                        const battleLevel = battle.getLevel();
                        let armyActors = army.getActors();
                        const nActors = armyActors.length;
                        const pIndex = RG.RAND.getUniformInt(0, nActors);
                        const replacedActor = armyActors[pIndex];
                        const [pX, pY] = replacedActor.getXY();

                        // Remove substituted actor from army/level
                        replacedActor.get('Action').disable();
                        army.removeActor(replacedActor);
                        battleLevel.removeActor(replacedActor);

                        armyActors = army.getActors();
                        army.addActor(player);

                        player.get('InBattle').updateData({army: army.getName});
                        armyActors.forEach(actor => {
                            actor.addFriend(player);
                        });

                        armies.forEach(enemyArmy => {
                            if (enemyArmy !== army) {
                                const enemies = enemyArmy.getActors();
                                enemies.forEach(enemy => {
                                    enemy.addEnemy(player);
                                });
                            }
                        });

                        if (!battleLevel.moveActorTo(player, pX, pY)) {
                            RG.err('GameMaster', 'getSelArmyObject',
                                `Could not move player to ${pX},${pY}`);
                        }
                    };
                }
                return null;
            }
        };
    };

    this.getSelLeaveBattle = function(player, level) {
        const selObj = function() {
            this.showMenu = () => true;
            this.getMenu = () => {
                RG.gameMsg('Battle is over! Do you want to leave battle?');
                return {
                    0: 'Leave immediately',
                    1: 'Stay behind to scavenge the bodies of the dead.'
                };
            };
            this.select = code => {
                const selection = RG.codeToIndex(code);
                if (selection === 0) {
                    return () => {
                        const exit = level.getConnections()[0];
                        if (!exit.useStairs(player)) {
                            RG.err('GameMaster', 'moveActorsOutOfBattle',
                                'Cannot move player via useStairs');
                        }
                        else {
                            const name = player.getName();
                            RG.gameMsg(`${name} leaves the battlefield`);
                        }
                    };
                }
                if (selection === 1) {
                    // TODO add some dishonor for the player or some necromancy
                    // effect etc
                    return null;
                }
                return this;
            };
        };
        return new selObj();
    };

    /* Serializes the object into JSON. */
    this.toJSON = function() {
        const keys = Object.keys(this.battles);
        const battles = {};
        keys.forEach(id => {
            battles[id] = this.battles[id].toJSON();
        });
        return {
            battles,
            battlesDone: this.battlesDone
        };
    };

    this.biomeToLevelType = function(biome) {
        switch (biome) {
            case OW.BIOME.ARCTIC: return 'arctic';
            case OW.BIOME.TUNDRA: return 'arctic';
            case OW.BIOME.ALPINE: return 'mountain';
            case OW.BIOME.TAIGA: return 'forest';
            default: return 'forest';
        }
    };

    this.getBattleLevels = function() {
        const levels = [];
        Object.values(this.battles).forEach(battle => {
            levels.push(battle.getLevel());
        });
        return levels;

    };
};

module.exports = GameMaster;
<|endoftext|>


<commit_before>SC.initialize({
  client_id: "711c21de667ecd3ea4e91721e5a4fae1"
});

var debug = true;
var lastSound = null;
var lastTrack = null;
var $lastIcon = null;
var trackIsPlaying = false;

function log(str) {
  if (debug)
    console.log(str);
}

var LOAD_TIMEOUT = 2200; // ms
var loadStartedTime = null;

function play(track, $listElement) {
  if (lastTrack !== track) {
    if (lastSound) {
      lastSound.stop();
      if (lastSound._timeout) {
        clearTimeout(lastSound._timeout);
      }
    }

    if ($lastIcon) {
      $lastIcon.removeClass("icon-pause icon-spin3 animate-spin");
    }
  } else { // attempting to play same track
    if ($lastIcon.hasClass("icon-block")
        || !lastSound._pausePosition || lastSound._pausePosition < 1) {
      log("Same track but broken track");

      if ($lastIcon.hasClass('icon-spin3'))
        return;
    } else {
      log("Same track");
      $lastIcon.addClass("icon-spin3 animate-spin");
      $lastIcon.spinning = true;

      var cont = false;
      if (lastSound) {
        if (lastSound._timeout) {
          clearTimeout(lastSound._timeout);
        }
        if (lastSound.playState) { // if currently playing
          var pp = lastSound.position;
          log("paused at " + pp);
          lastSound._pausePosition = pp;
          lastSound.stop();
          $lastIcon.removeClass('icon-pause icon-spin3 animate-spin');
        } else { // not current playing
          var pp = lastSound._pausePosition || 0;
          lastSound.setPosition(pp);
          lastSound.play();
          log("resumed at " + pp);
          $lastIcon.addClass('icon-pause icon-spin3 animate-spin');
          $lastIcon.spinning = true;
        }
        return;
      }
    }
  }

  var $i = $listElement.find('i');
  $i.removeClass("icon-block");
  $i.addClass("icon-spin3 animate-spin");
  $i.spinning = true;

  // bind events to remove the loading icon once music is playing
  // and icon for sop when fnished playing
  var opts = (function(){
    var e = $i;
    return {
      whileplaying: function () {
        if (!e.spinning)
          return;
        if (this.position < 1) // once actually playing
          return;
        log("Music started to play.");
        e.removeClass("icon-spin3 animate-spin");
        e.spinning = false;
        if (this._timeout)
          clearTimeout(this._timeout);
      },
      onfinish: function () {
        log(" >>> ONFINISH CALLED");
        e.removeClass("icon-pause");
      }
    };
  })();

  var uri = track.uri || track;
  SC.stream(uri, opts, function (sound) {
    window.ss = sound;

    //soundManager.stopAll();
    sound.play();
    $i.addClass("icon-pause");

    // retry playing sound if first attempt fails
    (function(){
      var mySound = sound;
      var $e = $i;
      log("setting up retry check timeout");
      mySound._timeout = setTimeout(function() {
        log("retry check timeout called");

        if (mySound.playState && mySound.position < 1) {
          // if still not playing - try again
          mySound.setPosition(0);
          mySound.play();
          log(" -- retrying to play track");

          // set another timeout that tells the user
          // the music is broken if it still doesn't work
          mySound._timeout = setTimeout(function(){
            log('  final check called');
            if (mySound.playState && mySound.position < 1) {
              $e
                .removeClass("icon-spin3 animate-spin icon-paused")
                .addClass("icon-block");
              var str = "Oh noes :( that track seems to be broken.";
              //showMessage(str, 'error');
              showNotice(str, 'error');
              mySound.stop();
              log(" !! track seems to be broken, tell user and stop trying");
            } else {
              log("    playState: " + mySound.playState);
              log("    position: " + mySound.position);
            }
          }, LOAD_TIMEOUT * 1 + 500); // extend
        } else {
          log("element OK but check failed");
          log("  position is: " + mySound.position);
        }
      }, LOAD_TIMEOUT);
    })();

    lastSound = sound;
  }); // eof SC.stream

  $lastIcon = $i;
  lastTrack = track;
}

var $list = $('#list');

var lastTracks = null;
var currentTrackIndex = 0;

function addMoreTracks(amount, animation) {
  if (!lastTracks)
    return;

  var tracks = lastTracks;

  log("current: " + currentTrackIndex);
  log("tracks.length: " + tracks.length);

  if (currentTrackIndex >= tracks.length) {
    showMessage("Didn't find any more songs! Try a new search?", "error");
    $('#more-button').removeClass().html("No more songs found!");
    return;
  }

  var limit = amount + currentTrackIndex;
  for (var i = currentTrackIndex; i < limit; i++) {
    var t = tracks[i];

    var t_title = t.title.substring(0, 48);
    if (t.title.length > 48) {
      t_title += "...";
    }

    var ani = animation || 'fadeIn';
    var $el = $(
      '
' +
        '' + t_title + '' +
      '
'
    );

    var ii = $el.find('i');
    ii.track = t;
    (function(){
      var e = $el;
      ii.on('click', function () {
        log("click: " + e.track.uri);
        play(e.track, e);
        return false;
      })
    }());

    $el.track = t;
    $list.append($el);
  }
  currentTrackIndex = limit;
}

var defaultLimit = 16;
var $text = $('#message-text');

function search(str) {
  // set spinning icon to signify loading
  showMessage(null, 'ok');
  showNotice(null); // clear the notice
  $text.removeClass().html('').addClass('icon-spin3 animate-spin');

  var query = {
    q: str,
    limit: defaultLimit
  }

  SC.get("http://api.soundcloud.com/tracks", query, function (tracks) {
    currentTrackIndex = 0;
    lastTracks = tracks;
    $('#more-button').html("More results").removeClass().addClass('icon-plus');
    var track = tracks[0];

    if (tracks.length < 1) {
      log("No tracks found!");
      $list.empty();
      $('.results-footer').css("display", "none"); // hide more button
      showMessage("Didn't find any songs! Try a new search?", "error");
    } else {
      showMessage("Here are some results.", "ok");

      // clear list for new search
      $list.empty();

      // Initial amount of tracks to show after search
      addMoreTracks(4);
      $('.results-footer').css("display", "block"); // show more button
    }
  }); // eof SC.get
}

var inputWatermarkText = "Search for any song";
var input = $('.searchbar input');

// setup input watermark
var first = true;
input.val(inputWatermarkText);
input.on('focus', function () {
  input.val("");
});
input.on('blur', function () {
  if (input.val().length < 1)
    input.val(inputWatermarkText);
});

var timeout = null;
input.on('input', function () {

  if (timeout) {
    clearTimeout(timeout);
  }

  if (input.val().length <= 0)
    return; // do nothing

  timeout = setTimeout(function() {
    if (input.val().length < 2) {
      showMessage("How about we try searching for something real?", 'ok');
      return;
    }
    search(input.val());
  }, 400);
})

// seupt more button
$('#more-button').on('click', function() {
  addMoreTracks(2);
  return false;
});

var $text = $('#message-text');
var $message = $('#message-box');
function showMessage(message, type) {
  log("showMessage called");
  $text.removeClass();
  if (message)
    $text.html(message);

  $message.removeClass();
  $message.addClass("message info-ok info-" + type);
}

var $nText = $('#notice-text');
var $nMessage = $('#notice-box');
var nTimeout = null;
// show temporary notice message
function showNotice (message, type) {
  log("showNotice called");

  $nText.removeClass();
  if (message) {
    $nText.html(message);
  } else {
    if (nTimeout) {
      clearTimeout(nTimeout);
    }
    $nMessage.addClass('fadeOut animated');
    return false;
  }

  $nMessage.removeClass();
  $nMessage.addClass("message bounce animated info-ok info-" + type);

  $nMessage.css('display', 'block');

  if (nTimeout)
    clearTimeout(nTimeout);

  nTimeout = setTimeout(function(){
    //$nMessage.css('display', 'none');
    $nMessage.addClass('fadeOut animated');
  }, LOAD_TIMEOUT * 1.8);
}

log("app loaded");
// default debug search result
search("melody circus");

document.body.addEventListener('touchstart', function () {
  log("in touchstart evt");
  document.body.removeEventListener('touchstart', arguments.callee);
}, false);
<commit_msg>clearing obsolete timeouts on cancel load<commit_after>SC.initialize({
  client_id: "711c21de667ecd3ea4e91721e5a4fae1"
});

var debug = true;
var lastSound = null;
var lastTrack = null;
var $lastIcon = null;
var trackIsPlaying = false;

function log(str) {
  if (debug)
    console.log(str);
}

var LOAD_TIMEOUT = 2200; // ms
var loadStartedTime = null;

function play(track, $listElement) {
  if (lastTrack !== track) {
    if (lastSound) {
      lastSound.stop();
      if (lastSound._timeout) {
        clearTimeout(lastSound._timeout);
      }
    }

    if ($lastIcon) {
      $lastIcon.removeClass("icon-pause icon-spin3 animate-spin");
    }
  } else { // attempting to play same track
    if ($lastIcon.hasClass("icon-block")
        || !lastSound._pausePosition || lastSound._pausePosition < 1) {
      log("Same track but broken track");

      if ($lastIcon.hasClass('icon-spin3')) {
        lastSound.stop();
        $lastIcon.removeClass("icon-pause icon-spin3 animate-spin icon-block");
        if (lastSound._timeout) {
          clearTimeout(lastSound._timeout);
        }
        return;
      }
    } else {
      log("Same track");
      $lastIcon.addClass("icon-spin3 animate-spin");
      $lastIcon.spinning = true;

      var cont = false;
      if (lastSound) {
        if (lastSound._timeout) {
          clearTimeout(lastSound._timeout);
        }
        if (lastSound.playState) { // if currently playing
          var pp = lastSound.position;
          log("paused at " + pp);
          lastSound._pausePosition = pp;
          lastSound.stop();
          $lastIcon.removeClass('icon-pause icon-spin3 animate-spin');
        } else { // not current playing
          var pp = lastSound._pausePosition || 0;
          lastSound.setPosition(pp);
          lastSound.play();
          log("resumed at " + pp);
          $lastIcon.addClass('icon-pause icon-spin3 animate-spin');
          $lastIcon.spinning = true;
        }
        return;
      }
    }
  }

  var $i = $listElement.find('i');
  $i.removeClass("icon-block");
  $i.addClass("icon-spin3 animate-spin");
  $i.spinning = true;

  // bind events to remove the loading icon once music is playing
  // and icon for sop when fnished playing
  var opts = (function(){
    var e = $i;
    return {
      whileplaying: function () {
        if (!e.spinning)
          return;
        if (this.position < 1) // once actually playing
          return;
        log("Music started to play.");
        e.removeClass("icon-spin3 animate-spin");
        e.spinning = false;
        if (this._timeout)
          clearTimeout(this._timeout);
      },
      onfinish: function () {
        log(" >>> ONFINISH CALLED");
        e.removeClass("icon-pause");
      }
    };
  })();

  var uri = track.uri || track;
  SC.stream(uri, opts, function (sound) {
    window.ss = sound;

    //soundManager.stopAll();
    sound.play();
    $i.addClass("icon-pause");

    // retry playing sound if first attempt fails
    (function(){
      var mySound = sound;
      var $e = $i;
      log("setting up retry check timeout");
      mySound._timeout = setTimeout(function() {
        log("retry check timeout called");

        if (mySound.playState && mySound.position < 1) {
          // if still not playing - try again
          mySound.setPosition(0);
          mySound.play();
          log(" -- retrying to play track");

          // set another timeout that tells the user
          // the music is broken if it still doesn't work
          mySound._timeout = setTimeout(function(){
            log('  final check called');
            if (mySound.playState && mySound.position < 1) {
              $e
                .removeClass("icon-spin3 animate-spin icon-paused")
                .addClass("icon-block");
              var str = "Oh noes :( that track seems to be broken.";
              //showMessage(str, 'error');
              showNotice(str, 'error');
              mySound.stop();
              log(" !! track seems to be broken, tell user and stop trying");
            } else {
              log("    playState: " + mySound.playState);
              log("    position: " + mySound.position);
            }
          }, LOAD_TIMEOUT * 1 + 500); // extend
        } else {
          log("element OK but check failed");
          log("  position is: " + mySound.position);
        }
      }, LOAD_TIMEOUT);
    })();

    lastSound = sound;
  }); // eof SC.stream

  $lastIcon = $i;
  lastTrack = track;
}

var $list = $('#list');

var lastTracks = null;
var currentTrackIndex = 0;

function addMoreTracks(amount, animation) {
  if (!lastTracks)
    return;

  var tracks = lastTracks;

  log("current: " + currentTrackIndex);
  log("tracks.length: " + tracks.length);

  if (currentTrackIndex >= tracks.length) {
    showMessage("Didn't find any more songs! Try a new search?", "error");
    $('#more-button').removeClass().html("No more songs found!");
    return;
  }

  var limit = amount + currentTrackIndex;
  for (var i = currentTrackIndex; i < limit; i++) {
    var t = tracks[i];

    var t_title = t.title.substring(0, 48);
    if (t.title.length > 48) {
      t_title += "...";
    }

    var ani = animation || 'fadeIn';
    var $el = $(
      '
' +
        '' + t_title + '' +
      '
'
    );

    var ii = $el.find('i');
    ii.track = t;
    (function(){
      var e = $el;
      ii.on('click', function () {
        log("click: " + e.track.uri);
        play(e.track, e);
        return false;
      })
    }());

    $el.track = t;
    $list.append($el);
  }
  currentTrackIndex = limit;
}

var defaultLimit = 16;
var $text = $('#message-text');

function search(str) {
  // set spinning icon to signify loading
  showMessage(null, 'ok');
  showNotice(null); // clear the notice
  $text.removeClass().html('').addClass('icon-spin3 animate-spin');

  var query = {
    q: str,
    limit: defaultLimit
  }

  SC.get("http://api.soundcloud.com/tracks", query, function (tracks) {
    currentTrackIndex = 0;
    lastTracks = tracks;
    $('#more-button').html("More results").removeClass().addClass('icon-plus');
    var track = tracks[0];

    if (tracks.length < 1) {
      log("No tracks found!");
      $list.empty();
      $('.results-footer').css("display", "none"); // hide more button
      showMessage("Didn't find any songs! Try a new search?", "error");
    } else {
      showMessage("Here are some results.", "ok");

      // clear list for new search
      $list.empty();

      // Initial amount of tracks to show after search
      addMoreTracks(4);
      $('.results-footer').css("display", "block"); // show more button
    }
  }); // eof SC.get
}

var inputWatermarkText = "Search for any song";
var input = $('.searchbar input');

// setup input watermark
var first = true;
input.val(inputWatermarkText);
input.on('focus', function () {
  input.val("");
});
input.on('blur', function () {
  if (input.val().length < 1)
    input.val(inputWatermarkText);
});

var timeout = null;
input.on('input', function () {

  if (timeout) {
    clearTimeout(timeout);
  }

  if (input.val().length <= 0)
    return; // do nothing

  timeout = setTimeout(function() {
    if (input.val().length < 2) {
      showMessage("How about we try searching for something real?", 'ok');
      return;
    }
    search(input.val());
  }, 400);
})

// seupt more button
$('#more-button').on('click', function() {
  addMoreTracks(2);
  return false;
});

var $text = $('#message-text');
var $message = $('#message-box');
function showMessage(message, type) {
  log("showMessage called");
  $text.removeClass();
  if (message)
    $text.html(message);

  $message.removeClass();
  $message.addClass("message info-ok info-" + type);
}

var $nText = $('#notice-text');
var $nMessage = $('#notice-box');
var nTimeout = null;
// show temporary notice message
function showNotice (message, type) {
  log("showNotice called");

  $nText.removeClass();
  if (message) {
    $nText.html(message);
  } else {
    if (nTimeout) {
      clearTimeout(nTimeout);
    }
    $nMessage.addClass('fadeOut animated');
    return false;
  }

  $nMessage.removeClass();
  $nMessage.addClass("message bounce animated info-ok info-" + type);

  $nMessage.css('display', 'block');

  if (nTimeout)
    clearTimeout(nTimeout);

  nTimeout = setTimeout(function(){
    //$nMessage.css('display', 'none');
    $nMessage.addClass('fadeOut animated');
  }, LOAD_TIMEOUT * 1.8);
}

log("app loaded");
// default debug search result
search("melody circus");

document.body.addEventListener('touchstart', function () {
  log("in touchstart evt");
  document.body.removeEventListener('touchstart', arguments.callee);
}, false);
<|endoftext|>


<commit_before>require.config({

  baseUrl: '/client/src/js',

  paths: {

    jasmine: '/client/test/lib/jasmine-1.3.1/jasmine',
    jasmineHtml: '/client/test/lib/jasmine-1.3.1/jasmine-html',
    jasmineJunitXml: '/client/test/lib/jasmine-reporters/jasmine.junit_reporter',

    tests: '/client/test/lib/tests'

  },

  shim: {

    'jasmine': {
      exports: 'jasmine'
    },

    'jasmineHtml': {
      deps: ['jasmine'],
      exports: 'jasmine.HtmlReporter'
    },

    'jasmineJunitXml': {
      deps: ['jasmine'],
      exports: 'jasmine.JUnitXmlReporter'
    }

  }

});<commit_msg>Syntax update.<commit_after>require.config({
  baseUrl: '/client/src/js',
  paths: {
    jasmine: '/client/test/lib/jasmine-1.3.1/jasmine',
    jasmineHtml: '/client/test/lib/jasmine-1.3.1/jasmine-html',
    jasmineJunitXml: '/client/test/lib/jasmine-reporters/jasmine.junit_reporter',
    tests: '/client/test/lib/tests'
  },
  shim: {
    'jasmine': {
      exports: 'jasmine'
    },
    'jasmineHtml': {
      deps: ['jasmine'],
      exports: 'jasmine.HtmlReporter'
    },
    'jasmineJunitXml': {
      deps: ['jasmine'],
      exports: 'jasmine.JUnitXmlReporter'
    }
  }
});<|endoftext|>