<commit_before>'use strict';
var MACROUTILS = require( 'osg/Utils' );
var Geometry = require( 'osg/Geometry' );
var Notify = require( 'osg/Notify' );
var Matrix = require( 'osg/Matrix' );
var StateSet = require( 'osg/StateSet' );
var MorphGeometry = require( 'osgAnimation/MorphGeometry' );
var UpdateRigGeometry = require( 'osgAnimation/UpdateRigGeometry' );
var RigTransformHardware = require( 'osgAnimation/RigTransformHardware' );

// RigGeometry is a Geometry deformed by bones
// To connect bones to RigGeometry it requires:
//     - a map of bones with index / weight eg also called VertexInfluenceMap
// {
//     bone0: { index: [],  // vertex index
//              weight: []  // weight for this index
//            },
//     bone2: { index: [],
//              weight: []
//            }
// }

var RigGeometry = function () {

    Geometry.call( this );

    this.setUpdateCallback( new UpdateRigGeometry() );

    //this._geometry = undefined;
    this._root = undefined;
    this._boneNameID = {};

    this._matrixFromSkeletonToGeometry = Matrix.create();
    this._invMatrixFromSkeletonToGeometry = Matrix.create();

    this._rigTransformImplementation = new RigTransformHardware();

    // RigGeometry have a special stateset that will be pushed at the very end of the culling
    // this stateSet only represents animation (and shouldn't contain any rendering attributes)
    // It's a way to make every RigGeometry unique (in term of stateSet stack)
    this._stateSetAnimation = new StateSet();

    // invalid/no bound by default
    this.boundingSphereComputed = true;
    this._boundingBoxComputed = true;

    this._needToComputeMatrix = true;

};

RigGeometry.prototype = MACROUTILS.objectLibraryClass( MACROUTILS.objectInherit( Geometry.prototype, {

    getStateSetAnimation: function () {
        return this._stateSetAnimation;
    },

    getSkeleton: function () {
        return this._root;
    },

    setSkeleton: function ( root ) {
        this._root = root;
    },

    setNeedToComputeMatrix: function ( needToComputeMatrix ) {
        this._needToComputeMatrix = needToComputeMatrix;
    },

    getNeedToComputeMatrix: function () {
        return this._needToComputeMatrix;
    },

    computeMatrixFromRootSkeleton: function () {

        if ( !this._root ) {
            Notify.warn( 'Warning ' + this.className() + '.computeMatrixFromRootSkeleton if you have this message it means you miss to call buildTransformer( root ), or your RigGeometry (' + this.getName() + ') is not attached to a Skeleton subgraph' );
            return;
        }

        var mtxList = this.getParents()[ 0 ].getWorldMatrices( this._root );
        var invNotRoot = Matrix.create();

        Matrix.inverse( this._root.getMatrix(), invNotRoot );
        Matrix.mult( invNotRoot, mtxList[ 0 ], this._matrixFromSkeletonToGeometry );
        Matrix.inverse( this._matrixFromSkeletonToGeometry, this._invMatrixFromSkeletonToGeometry );

        this._needToComputeMatrix = false;
    },

    getMatrixFromSkeletonToGeometry: function () {
        return this._matrixFromSkeletonToGeometry;
    },

    getInvMatrixFromSkeletonToGeometry: function () {
        return this._invMatrixFromSkeletonToGeometry;
    },

    getSourceGeometry: function () {
        return this._geometry;
    },

    setSourceGeometry: function ( geometry ) {
        this._geometry = geometry;
    },

    mergeChildrenVertexAttributeList: function () {

        if ( this._geometry instanceof MorphGeometry )
            this._geometry.mergeChildrenVertexAttributeList();

        var sourceGeometryVertexAttributeList = this._geometry.getVertexAttributeList();

        Geometry.appendVertexAttributeToList( sourceGeometryVertexAttributeList, this.getVertexAttributeList() );

    },

    mergeChildrenData: function () {

        // move to the rig the vertex attributes, the primitives and the stateset

        this.mergeChildrenVertexAttributeList();
        var primitiveSetList = this._geometry.getPrimitiveSetList();

        this.getPrimitiveSetList().length = 0;
        for ( var i = 0, il = primitiveSetList.length; i < il; i++ )
            this.getPrimitiveSetList()[ i ] = primitiveSetList[ i ];

        if ( this.getStateSet() )
            console.error( 'A stateset in the rig is already present : ' + this.getStateSet() );
        this.setStateSet( this._geometry.getStateSet() );
    },

    update: function () {
        this._rigTransformImplementation.update( this );
    }

} ), 'osgAnimation', 'RigGeometry' );

MACROUTILS.setTypeID( RigGeometry );

module.exports = RigGeometry;
<commit_msg>Compute the bounding box for the non influenced vertices of a rig geometry<commit_after>'use strict';
var MACROUTILS = require( 'osg/Utils' );
var Geometry = require( 'osg/Geometry' );
var Notify = require( 'osg/Notify' );
var Matrix = require( 'osg/Matrix' );
var StateSet = require( 'osg/StateSet' );
var MorphGeometry = require( 'osgAnimation/MorphGeometry' );
var UpdateRigGeometry = require( 'osgAnimation/UpdateRigGeometry' );
var RigTransformHardware = require( 'osgAnimation/RigTransformHardware' );

// RigGeometry is a Geometry deformed by bones
// To connect bones to RigGeometry it requires:
//     - a map of bones with index / weight eg also called VertexInfluenceMap
// {
//     bone0: { index: [],  // vertex index
//              weight: []  // weight for this index
//            },
//     bone2: { index: [],
//              weight: []
//            }
// }

var RigGeometry = function () {

    Geometry.call( this );

    this.setUpdateCallback( new UpdateRigGeometry() );

    //this._geometry = undefined;
    this._root = undefined;
    this._boneNameID = {};

    this._matrixFromSkeletonToGeometry = Matrix.create();
    this._invMatrixFromSkeletonToGeometry = Matrix.create();

    this._rigTransformImplementation = new RigTransformHardware();

    // RigGeometry have a special stateset that will be pushed at the very end of the culling
    // this stateSet only represents animation (and shouldn't contain any rendering attributes)
    // It's a way to make every RigGeometry unique (in term of stateSet stack)
    this._stateSetAnimation = new StateSet();

    this._needToComputeMatrix = true;

};

RigGeometry.prototype = MACROUTILS.objectLibraryClass( MACROUTILS.objectInherit( Geometry.prototype, {

    getStateSetAnimation: function () {
        return this._stateSetAnimation;
    },

    getSkeleton: function () {
        return this._root;
    },

    setSkeleton: function ( root ) {
        this._root = root;
    },

    setNeedToComputeMatrix: function ( needToComputeMatrix ) {
        this._needToComputeMatrix = needToComputeMatrix;
    },

    getNeedToComputeMatrix: function () {
        return this._needToComputeMatrix;
    },

    computeBoundingBox: function ( boundingBox ) {

        var vertexArray = this.getVertexAttributeList().Vertex;
        var weightsArray = this.getVertexAttributeList().Weights;
        // mainly copy paste of geometry computeBoundingBox code, except we only
        // take into account the non-influenced vertices

        // we do that only for the non-influenced vertices because the rigged ones
        // can't be statically computed (full moving bbox of rigs should be computed externally
        // through bones or cpu rigged colision mesh, etc)
        // bbox is important for culling (near/far)

        if ( vertexArray && weightsArray && vertexArray.getElements() && vertexArray.getItemSize() > 2 ) {

            var weights = weightsArray.getElements();
            var vertexes = vertexArray.getElements();
            var itemSize = vertexArray.getItemSize();

            var min = boundingBox.getMin();
            var max = boundingBox.getMax();

            var minx = min[ 0 ];
            var miny = min[ 1 ];
            var minz = min[ 2 ];
            var maxx = max[ 0 ];
            var maxy = max[ 1 ];
            var maxz = max[ 2 ];

            for ( var idx = 0, idb = 0, l = vertexes.length; idx < l; idx += itemSize, idb += 4 ) {

                if ( weights[ idx ] !== 0.0 || weights[ idx + 1 ] !== 0.0 || weights[ idx + 2 ] !== 0.0 || weights[ idx + 3 ] !== 0.0 )
                    continue;

                var v1 = vertexes[ idx ];
                var v2 = vertexes[ idx + 1 ];
                var v3 = vertexes[ idx + 2 ];
                if ( v1 < minx ) minx = v1;
                if ( v1 > maxx ) maxx = v1;
                if ( v2 < miny ) miny = v2;
                if ( v2 > maxy ) maxy = v2;
                if ( v3 < minz ) minz = v3;
                if ( v3 > maxz ) maxz = v3;
            }

            min[ 0 ] = minx;
            min[ 1 ] = miny;
            min[ 2 ] = minz;
            max[ 0 ] = maxx;
            max[ 1 ] = maxy;
            max[ 2 ] = maxz;
        }

        return boundingBox;
    },

    computeMatrixFromRootSkeleton: function () {

        if ( !this._root ) {
            Notify.warn( 'Warning ' + this.className() + '.computeMatrixFromRootSkeleton if you have this message it means you miss to call buildTransformer( root ), or your RigGeometry (' + this.getName() + ') is not attached to a Skeleton subgraph' );
            return;
        }

        var mtxList = this.getParents()[ 0 ].getWorldMatrices( this._root );
        var invNotRoot = Matrix.create();

        Matrix.inverse( this._root.getMatrix(), invNotRoot );
        Matrix.mult( invNotRoot, mtxList[ 0 ], this._matrixFromSkeletonToGeometry );
        Matrix.inverse( this._matrixFromSkeletonToGeometry, this._invMatrixFromSkeletonToGeometry );

        this._needToComputeMatrix = false;
    },

    getMatrixFromSkeletonToGeometry: function () {
        return this._matrixFromSkeletonToGeometry;
    },

    getInvMatrixFromSkeletonToGeometry: function () {
        return this._invMatrixFromSkeletonToGeometry;
    },

    getSourceGeometry: function () {
        return this._geometry;
    },

    setSourceGeometry: function ( geometry ) {
        this._geometry = geometry;
    },

    mergeChildrenVertexAttributeList: function () {

        if ( this._geometry instanceof MorphGeometry )
            this._geometry.mergeChildrenVertexAttributeList();

        var sourceGeometryVertexAttributeList = this._geometry.getVertexAttributeList();

        Geometry.appendVertexAttributeToList( sourceGeometryVertexAttributeList, this.getVertexAttributeList() );

    },

    mergeChildrenData: function () {

        // move to the rig the vertex attributes, the primitives and the stateset

        this.mergeChildrenVertexAttributeList();
        var primitiveSetList = this._geometry.getPrimitiveSetList();

        this.getPrimitiveSetList().length = 0;
        for ( var i = 0, il = primitiveSetList.length; i < il; i++ )
            this.getPrimitiveSetList()[ i ] = primitiveSetList[ i ];

        if ( this.getStateSet() )
            console.error( 'A stateset in the rig is already present : ' + this.getStateSet() );
        this.setStateSet( this._geometry.getStateSet() );
    },

    update: function () {
        this._rigTransformImplementation.update( this );
    }

} ), 'osgAnimation', 'RigGeometry' );

MACROUTILS.setTypeID( RigGeometry );

module.exports = RigGeometry;
<|endoftext|>


<commit_before>'use strict';

describe('search', function() {
  var createTokens = function() {
    return {
      '01' : {
        id: '01',
        string: 'Arma'
      },
      '02': {
        id: '02',
        string: 'virum'
      },
      '03': {
        id: '03',
        string: 'virum'
      }
    };
  };

  var mockConfigurator = {
    configurationFor: function(name) {
      return {};
    },

    getRetrievers: function(name) {
      return {};
    }
  };

  beforeEach(module('arethusa.core', function($provide) {
    $provide.value('configurator', mockConfigurator);
  }));

  beforeEach(module('arethusa.search'));

  var search;
  var state;
  beforeEach(inject(function(_search_, _state_) {
    search = _search_;
    state = _state_;
    state.tokens = createTokens();
    search.init();
  }));

  describe('this.collectTokenStrings', function() {
    it('collects all strings of tokens as keys with their ids as values in an array', function() {
      var res = {'Arma': ['01'], 'virum': ['02', '03']};
      expect(search.collectTokenStrings()).toEqual(res);
    });
  });

  describe('this.pluginsWithSearch', function() {
    it('returns an array of plugins that have a search function', function() {
      var pluginA = { canSearch: true };
      var pluginB = { canSearch: false };
      var pluginC = { canSearch: true };
      var plugins = { "a": pluginA, "b": pluginB, "c": pluginC };
      expect(search.pluginsWithSearch(plugins)).toEqual([ pluginA, pluginC ]);
    });
  });
});
<commit_msg>Add spec for findByRegex()<commit_after>'use strict';

describe('search', function() {
  var createTokens = function() {
    return {
      '01' : {
        id: '01',
        string: 'Arma'
      },
      '02': {
        id: '02',
        string: 'virum'
      },
      '03': {
        id: '03',
        string: 'virum'
      }
    };
  };

  var mockConfigurator = {
    configurationFor: function(name) {
      return {};
    },

    getRetrievers: function(name) {
      return {};
    }
  };

  beforeEach(module('arethusa.core', function($provide) {
    $provide.value('configurator', mockConfigurator);
  }));

  beforeEach(module('arethusa.search'));

  var search;
  var state;
  beforeEach(inject(function(_search_, _state_) {
    search = _search_;
    state = _state_;
    state.tokens = createTokens();
    search.init();
  }));

  describe('this.findByRegex', function() {
    describe('finds tokens by a regex and returns their id in an array', function() {
      it('with simple regex', function() {
        var arg = 'um';
        expect(search.findByRegex(arg)).toEqual(['02', '03']);
      });
    });

    describe('with meta characters', function() {
      it('with .', function() {
        var arg = 'r.m';
        expect(search.findByRegex(arg)).toEqual(['02', '03']);
      });

      it('...', function() {
        // more tests to follow
      });
    });
  });

  describe('this.collectTokenStrings', function() {
    it('collects all strings of tokens as keys with their ids as values in an array', function() {
      var res = {'Arma': ['01'], 'virum': ['02', '03']};
      expect(search.collectTokenStrings()).toEqual(res);
    });
  });

  describe('this.pluginsWithSearch', function() {
    it('returns an array of plugins that have a search function', function() {
      var pluginA = { canSearch: true };
      var pluginB = { canSearch: false };
      var pluginC = { canSearch: true };
      var plugins = { "a": pluginA, "b": pluginB, "c": pluginC };
      expect(search.pluginsWithSearch(plugins)).toEqual([ pluginA, pluginC ]);
    });
  });
});
<|endoftext|>


<commit_before>/* jshint evil: true */

(function() {

var lastSlotNumber = null;

var recentUnits = JSON.parse(localStorage.getItem('recentUnits')) || [ ];
var instructionsShown = JSON.parse(localStorage.getItem('instructionsShown'));

/* * * * * Dialog generation * * * * */

var createDialog = function() {
    BootstrapDialog.show({
        title: 'Pick a Unit',
        animate: false,
        message: function(dialog) {
            var content = $(
                    '
' +
                        '<input type="text" id="picker" placeholder="type to filter">' +
                        '

' +
                        '
' +
                            'Recently picked' +
                            '<divd id="recentUnits">
' +
                        '
' +
                    '
');
            content.find('input').keyup(Utils.debounce('picker',function() {
                populateList(generateSearchParameters(this.value),$('#pickerUnits'));
            }));
            populateRecent(content.find('#recentUnits'));
            return content;
        },
        buttons: [{
            label: 'Cancel',
            action: function(dialog) { dialog.close(); }
        }],
        onshown: function(dialog) {
            $('#picker').focus();
        }
    });
};

var closeDialog = function() {
    $.each(BootstrapDialog.dialogs,function(id,dialog) {
        dialog.close();
    });
};

var pick = function(event,slotNumber) {
    createDialog(slotNumber);
};

var showInstructions = function() {
    Utils.info([
        'You can use operators to refine searches.',
        'Supported string operators:',
        '- "type:X": shows only units of type X (STR, DEX, QCK, INT or PSY)',
        '- "class:X": shows only units of class X (fighter, slasher, striker, shooter, ...)',
        'Supported mathematical operators:',
        '- "hp>X": shows only units with max HP > X',
        '- "atk>X": shows only units with max ATK > X',
        '- "cost>X": shows only units with a cost higher than X',
        '- "stars>X": shows only units with more than X stars',
        'Mathematical operators also work with "<", ">=", "<=" and "=".',
        'Example query:',
        'type:str class:fighter hp>1200 atk>1300 cost>20'
    ].join('
'));
    localStorage.setItem('instructionsShown','true');
    instructionsShown = true;
};

/* * * * * List generation * * * * */

var generateSearchParameters = function(query) {
    if (!query || query.trim().length < 3) return null;
    var result = { name: [ ] };
    tokens = query.trim().replace(/\s+/g,' ').split(' ').filter(function(x) { return x.length > 0; });
    tokens.forEach(function(x) {
        var temp = x.match(/^((type|class):(\w+)|(hp|atk|stars|cost|growth)(>|<|>=|<=|=)(\d+))$/), func;
        if (!temp) {
            result.name.push(x);
        } else if (temp[4] !== undefined) {
            func = new Function('x','return x ' + temp[5].replace(/^=$/,'==') + ' ' + temp[6] + ';');
            result[temp[4]] = func;
        } else {
            func = new Function('x','return /' + temp[3] + '/i.test(x);');
            result[temp[2]] = func;
        }
    });
    if (result.name.length === 0) delete result.name;
    else result.name = new Function('x','return /' + result.name.join(' ') + '/i.test(x);');
    return result;
};

var populateList = function(parameters,target) {
    var result = units;
    $(target).empty();
    if (parameters === null) return;
    Object.keys(parameters).forEach(function(key) {
        var unitKey = key.replace(/^(hp|atk)$/,function(x) { return 'max' + x.toUpperCase(); });
        result = result.filter(function(x) {
            return parameters[key](x[unitKey]);
        });
    });
    result.forEach(function(unit) {
        $(target).append(Utils.createThumbnail(unit.number,false,onThumbnailClick));
    });
};

/* * * * * Events * * * * */

var onThumbnailClick = function() {
    var unitNumber = parseInt(this.getAttribute('unitID'),10);
    updateRecent(unitNumber);
    $(document).trigger('unitPicked',[ lastSlotNumber, unitNumber ]);
    closeDialog();
};

var onUnitClick = function(e) {
    if (e.which != 1 || e.ctrlKey) return;
    var target = $(e.target);
    if (!target.hasClass('unitPortrait') && !target.hasClass('empty')) return;
    if (Utils.isClickOnOrb(e,e.target)) return;
    lastSlotNumber = $(this).index();
    createDialog();
    if (!instructionsShown) showInstructions();
};

/* * * * * Recent list generation * * * * */

var updateRecent = function(unitNumber) {
    var n = recentUnits.indexOf(unitNumber);
    if (n < 0) recentUnits.unshift(unitNumber);
    else recentUnits = recentUnits.splice(n,1).concat(recentUnits);
    recentUnits = recentUnits.slice(0,14);
    localStorage.setItem('recentUnits',JSON.stringify(recentUnits));
};

var populateRecent = function(target) {
    recentUnits.forEach(function(x) {
        var thumbnail = Utils.createThumbnail(x,true,onThumbnailClick);
        target.append(thumbnail);
    });
};

/* * * * * Events * * * * */

$(function() {

    $('.unit').click(onUnitClick);

});

})();
<commit_msg>fixed picker not opening on placeholder<commit_after>/* jshint evil: true */

(function() {

var lastSlotNumber = null;

var recentUnits = JSON.parse(localStorage.getItem('recentUnits')) || [ ];
var instructionsShown = JSON.parse(localStorage.getItem('instructionsShown'));

/* * * * * Dialog generation * * * * */

var createDialog = function() {
    BootstrapDialog.show({
        title: 'Pick a Unit',
        animate: false,
        message: function(dialog) {
            var content = $(
                    '
' +
                        '<input type="text" id="picker" placeholder="type to filter">' +
                        '

' +
                        '
' +
                            'Recently picked' +
                            '<divd id="recentUnits">
' +
                        '
' +
                    '
');
            content.find('input').keyup(Utils.debounce('picker',function() {
                populateList(generateSearchParameters(this.value),$('#pickerUnits'));
            }));
            populateRecent(content.find('#recentUnits'));
            return content;
        },
        buttons: [{
            label: 'Cancel',
            action: function(dialog) { dialog.close(); }
        }],
        onshown: function(dialog) {
            $('#picker').focus();
        }
    });
};

var closeDialog = function() {
    $.each(BootstrapDialog.dialogs,function(id,dialog) {
        dialog.close();
    });
};

var pick = function(event,slotNumber) {
    createDialog(slotNumber);
};

var showInstructions = function() {
    Utils.info([
        'You can use operators to refine searches.',
        'Supported string operators:',
        '- "type:X": shows only units of type X (STR, DEX, QCK, INT or PSY)',
        '- "class:X": shows only units of class X (fighter, slasher, striker, shooter, ...)',
        'Supported mathematical operators:',
        '- "hp>X": shows only units with max HP > X',
        '- "atk>X": shows only units with max ATK > X',
        '- "cost>X": shows only units with a cost higher than X',
        '- "stars>X": shows only units with more than X stars',
        'Mathematical operators also work with "<", ">=", "<=" and "=".',
        'Example query:',
        'type:str class:fighter hp>1200 atk>1300 cost>20'
    ].join('
'));
    localStorage.setItem('instructionsShown','true');
    instructionsShown = true;
};

/* * * * * List generation * * * * */

var generateSearchParameters = function(query) {
    if (!query || query.trim().length < 3) return null;
    var result = { name: [ ] };
    tokens = query.trim().replace(/\s+/g,' ').split(' ').filter(function(x) { return x.length > 0; });
    tokens.forEach(function(x) {
        var temp = x.match(/^((type|class):(\w+)|(hp|atk|stars|cost|growth|rcv)(>|<|>=|<=|=)(\d+))$/), func;
        if (!temp) {
            result.name.push(x);
        } else if (temp[4] !== undefined) {
            func = new Function('x','return x ' + temp[5].replace(/^=$/,'==') + ' ' + temp[6] + ';');
            result[temp[4]] = func;
        } else {
            func = new Function('x','return /' + temp[3] + '/i.test(x);');
            result[temp[2]] = func;
        }
    });
    if (result.name.length === 0) delete result.name;
    else result.name = new Function('x','return /' + result.name.join(' ') + '/i.test(x);');
    return result;
};

var populateList = function(parameters,target) {
    var result = units;
    $(target).empty();
    if (parameters === null) return;
    Object.keys(parameters).forEach(function(key) {
        var unitKey = key.replace(/^(hp|atk|rcv)$/,function(x) { return 'max' + x.toUpperCase(); });
        result = result.filter(function(x) {
            return parameters[key](x[unitKey]);
        });
    });
    result.forEach(function(unit) {
        $(target).append(Utils.createThumbnail(unit.number,false,onThumbnailClick));
    });
};

/* * * * * Events * * * * */

var onThumbnailClick = function() {
    var unitNumber = parseInt(this.getAttribute('unitID'),10);
    updateRecent(unitNumber);
    $(document).trigger('unitPicked',[ lastSlotNumber, unitNumber ]);
    closeDialog();
};

var onUnitClick = function(e) {
    if (e.which != 1 || e.ctrlKey) return;
    if (!/unitPortrait|empty|unitPlaceholder/.test(e.target.className)) return;
    if (Utils.isClickOnOrb(e,e.target)) return;
    lastSlotNumber = $(this).index();
    createDialog();
    if (!instructionsShown) showInstructions();
};

/* * * * * Recent list generation * * * * */

var updateRecent = function(unitNumber) {
    var n = recentUnits.indexOf(unitNumber);
    if (n < 0) recentUnits.unshift(unitNumber);
    else recentUnits = recentUnits.splice(n,1).concat(recentUnits);
    recentUnits = recentUnits.slice(0,14);
    localStorage.setItem('recentUnits',JSON.stringify(recentUnits));
};

var populateRecent = function(target) {
    recentUnits.forEach(function(x) {
        var thumbnail = Utils.createThumbnail(x,true,onThumbnailClick);
        target.append(thumbnail);
    });
};

/* * * * * Events * * * * */

$(function() {

    $('.unit').click(onUnitClick);

});

})();
<|endoftext|>


<commit_before>/*
Copyright 2006-2007, Open Source Applications Foundation

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

//Wait a specified number of milliseconds
windmill.controller.waits.sleep = function (paramObj, obj) {
  windmill.waiting = true;
  //if a number of milliseconds werent provided in a int format
  if ((paramObj.milliseconds == "") || (parseInt(paramObj.milliseconds, 10) == NaN)){
    paramObj.milliseconds = 5000;
  }

  done = function(){
    windmill.waiting = false;
    windmill.controller.continueLoop();
    //we passed the id in the parms object of the action in the ide
    var aid = paramObj.aid;
    delete paramObj.aid;
    //set the result in the ide
    windmill.xhr.setWaitBgAndReport(aid,true,obj);
  }
  setTimeout('done()', paramObj.milliseconds);
  return true;
};

windmill.controller.waits.forJSTrue = function (paramObj, obj, pageLoad) {
  _this = this;

  //we passed the id in the parms object of the action in the ide
  var aid = paramObj.aid;
  delete paramObj.aid;
  var count = 0;
  var p = paramObj || {};
  var timeout = 8000;
  var isJsTest = (p.orig == 'js');
  var testCondition = p.test;

  // If we get the weird string "NaN" (yes, the actual
  // string, "NaN" :)) value from the IDE, or some other
  // unusable string , just use the default value of 20 seconds
  if (p.timeout) {
    if (parseInt(p.timeout, 10) != NaN){
      timeout = p.timeout;
    }
  }

  var lookup = function () {
    if (count >= timeout) {
      if (isJsTest) {
        windmill.jsTest.runTestItemArray();
        windmill.jsTest.waiting = false;
        windmill.jsTest.handleErr('waits.forElement timed out after ' + timeout + ' seconds.');
      }
      else {
        if (pageLoad){
          windmill.loaded();
          windmill.controller.continueLoop();
        }
        else{ windmill.controller.continueLoop(); }
      }
      windmill.xhr.setWaitBgAndReport(aid,false,obj);
      return false;
    }
    count += 2500;

    // Get a result
    var result;
    if (typeof testCondition == 'string') {
      result = eval(testCondition);
    }
    else if (typeof testCondition == 'function') {
      result = testCondition();
    }
    else {
      throw new Error('waits.forTrue test condition must be a string or function.');
    }
    result = !!result; // Make sure we've got a Boolean

    if (!result){ var x = setTimeout(lookup, 1500); }
    else {
        c = function () {
          //If this method is being called by the js test framework
          if (isJsTest) {
            windmill.jsTest.waiting = false;
            windmill.jsTest.runTestItemArray();
          }
          else{
             if (pageLoad){ windmill.loaded(); }
             else{ windmill.controller.continueLoop(); }
          }

           //set the result in the ide
            windmill.xhr.setWaitBgAndReport(aid,true,obj);
        }
      setTimeout(c, 1000);
    }
  }

  lookup();

  //waits are going to wait, so I return true
  //Optimally it would return false if it times out, so when it does return false
  //the calling code will jump back up and process the ui accordingly
  return true;

};

//wait for an element to show up on the page
//if it doesn't after a provided timeout, defaults to 20 seconds
windmill.controller.waits.forElement = function (paramObj,obj) {
    var p = paramObj || {};
    var f = function () {
      try { return windmill.controller._lookupDispatch(p); }
      catch(err){}
    };
    p.test = f;
    return windmill.controller.waits.forJSTrue(p, obj);
};

//wait for an element to show up on the page
//if it doesn't after a provided timeout, defaults to 20 seconds
windmill.controller.waits.forNotElement = function (paramObj,obj) {
    var p = paramObj || {};
    var f = function () {
      try{
        var node = windmill.controller._lookupDispatch(p);
        return !node;
      }
      catch(err){}
    };
    p.test = f;
    return windmill.controller.waits.forJSTrue(p, obj);
};

//This is more of an internal function used by wait and click events
//To know when to try and reattach the listeners
//But if users wanted this manually they could use it
windmill.controller.waits.forPageLoad = function (paramObj,obj) {

  var p = paramObj || {};
  var sl = function(){
    var f = function () {
      try {
        var v = opener.document.domain;
      }catch(err){
        document.domain = windmill.docDomain;
      }
      try {
        var d = opener.document.body;
      }catch(err){ d = null;}

      if (d != null){
        return true;
      }

      return false;
    };
    p.test = f;

    return windmill.controller.waits.forJSTrue(p, obj, true);
  }
  setTimeout(sl, 3000);
  //we can't access the body, so now wait for the loading
  //setTimeout(sl, 0);
}

//wait for an element to show up on the page
//if it doesn't after a provided timeout, defaults to 20 seconds
windmill.controller.waits._forNotTitleAttach = function (paramObj,obj) {
    var p = paramObj || {};
    var f = function () {
      try{
        if (opener.document.title != param_object.title){
          var d = opener.document.body;
          return true;
        }
        return false;
      }
      catch(err){ return false; }
    };
    p.test = f;
    p.timeout = 60000;
    return windmill.controller.waits.forJSTrue(p, obj, true);
};

// //Turn the loop back on when the page in the testingApp window is loaded
// //this is an internal wait used only for the first load of the page
// //a more generic one will be added if there is a need
// windmill.controller.waits._forNotTitleAttach = function (param_object) {
//   _this = this;
//
//   var timeout = 80000;
//   var count = 0;
//   var p = param_object;
//
//   if (p.timeout){
//     timeout = p.timeout;
//   }
//   this.lookup = function(){
//     if (count >= timeout){
//       windmill.controller.continueLoop();
//       return false;
//     }
//     try {
//       if (windmill.testWindow.document.title == p.title){
//        var n = false;
//       }
//       else { var n = true };
//     }
//     catch(err){
//       n = false;
//     }
//     count += 2500;
//
//     this.check(n);
//   }
//
//   this.check = function(n){
//
//     if (!n){
//       var x = setTimeout(function () { _this.lookup(); }, 1000);
//     }
//     else{
//
//       try {
//         if (typeof(windmill.testWindow.onload.listenReg) == 'undefined'){
//           windmill.loaded();
//         }
//       }
//       catch(err){ this.lookup() }
//         fleegix.event.suppressHandlerErrors(windmill.testWindow, 'onload');
//         fleegix.event.unlisten(windmill.testWindow, 'onload', windmill, 'loaded');
//         fleegix.event.listen(windmill.testWindow, 'onload', windmill, 'loaded');
//         _this.lookup();
//
//       return true;
//     }
//   }
//
//   this.lookup();
//
//   //if windmill.timeout goes by and the tests haven't been started
//   //We go ahead and start them, longer waits can happen by changing windmill.timeout
//   ct = function(){
//    //windmill.controller.continueLoop();
//    windmill.controller.waits.forPageLoad({timeout:"50000"});
//  }
//    windmill.loadTimeoutId = setTimeout('ct()', windmill.timeout);
//
//   return true;
// }
<commit_msg>Checking in some debugging output for the safari blank page state<commit_after>/*
Copyright 2006-2007, Open Source Applications Foundation

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

//Wait a specified number of milliseconds
windmill.controller.waits.sleep = function (paramObj, obj) {
  windmill.waiting = true;
  //if a number of milliseconds werent provided in a int format
  if ((paramObj.milliseconds == "") || (parseInt(paramObj.milliseconds, 10) == NaN)){
    paramObj.milliseconds = 5000;
  }

  done = function(){
    windmill.waiting = false;
    windmill.controller.continueLoop();
    //we passed the id in the parms object of the action in the ide
    var aid = paramObj.aid;
    delete paramObj.aid;
    //set the result in the ide
    windmill.xhr.setWaitBgAndReport(aid,true,obj);
  }
  setTimeout('done()', paramObj.milliseconds);
  return true;
};

windmill.controller.waits.forJSTrue = function (paramObj, obj, pageLoad) {
  _this = this;

  //we passed the id in the parms object of the action in the ide
  var aid = paramObj.aid;
  delete paramObj.aid;
  var count = 0;
  var p = paramObj || {};
  var timeout = 8000;
  var isJsTest = (p.orig == 'js');
  var testCondition = p.test;

  // If we get the weird string "NaN" (yes, the actual
  // string, "NaN" :)) value from the IDE, or some other
  // unusable string , just use the default value of 20 seconds
  if (p.timeout) {
    if (parseInt(p.timeout, 10) != NaN){
      timeout = p.timeout;
    }
  }

  var lookup = function () {
    if (count >= timeout) {
      if (isJsTest) {
        windmill.jsTest.runTestItemArray();
        windmill.jsTest.waiting = false;
        windmill.jsTest.handleErr('waits.forElement timed out after ' + timeout + ' seconds.');
      }
      else {
        if (pageLoad){
          windmill.loaded();
          windmill.controller.continueLoop();
        }
        else{ windmill.controller.continueLoop(); }
      }
      windmill.xhr.setWaitBgAndReport(aid,false,obj);
      return false;
    }
    count += 2500;

    // Get a result
    var result;
    if (typeof testCondition == 'string') {
      result = eval(testCondition);
    }
    else if (typeof testCondition == 'function') {
      result = testCondition();
    }
    else {
      throw new Error('waits.forTrue test condition must be a string or function.');
    }
    result = !!result; // Make sure we've got a Boolean

    if (!result){ var x = setTimeout(lookup, 1500); }
    else {
        c = function () {
          //If this method is being called by the js test framework
          if (isJsTest) {
            windmill.jsTest.waiting = false;
            windmill.jsTest.runTestItemArray();
          }
          else{
             if (pageLoad){ windmill.loaded(); }
             else{ windmill.controller.continueLoop(); }
          }

           //set the result in the ide
            windmill.xhr.setWaitBgAndReport(aid,true,obj);
        }
      setTimeout(c, 1000);
    }
  }

  lookup();

  //waits are going to wait, so I return true
  //Optimally it would return false if it times out, so when it does return false
  //the calling code will jump back up and process the ui accordingly
  return true;

};

//wait for an element to show up on the page
//if it doesn't after a provided timeout, defaults to 20 seconds
windmill.controller.waits.forElement = function (paramObj,obj) {
    var p = paramObj || {};
    var f = function () {
      try { return windmill.controller._lookupDispatch(p); }
      catch(err){}
    };
    p.test = f;
    return windmill.controller.waits.forJSTrue(p, obj);
};

//wait for an element to show up on the page
//if it doesn't after a provided timeout, defaults to 20 seconds
windmill.controller.waits.forNotElement = function (paramObj,obj) {
    var p = paramObj || {};
    var f = function () {
      try{
        var node = windmill.controller._lookupDispatch(p);
        return !node;
      }
      catch(err){}
    };
    p.test = f;
    return windmill.controller.waits.forJSTrue(p, obj);
};

//This is more of an internal function used by wait and click events
//To know when to try and reattach the listeners
//But if users wanted this manually they could use it
windmill.controller.waits.forPageLoad = function (paramObj,obj) {

  var p = paramObj || {};
  var sl = function(){
    var f = function () {
      try {
        var v = opener.document.domain;
      }catch(err){
        document.domain = windmill.docDomain;
      }
      try {
        var d = opener.document.body;
      }catch(err){ d = null;}

      if (d != null){
        return true;
      }

      //Fix for strange safari issue where a blank page is loaded
      //window object exists but document is null
      if (windmill.browser.isSafari){
        if ((typeof _w == "object") && (_w.document == null)){
          windmill.ui.results.writeResult('Writing debugging info for the safari blank page state<br>');
          windmill.ui.results.writeResult(typeof _w);
          windmill.ui.results.writeResult(typeof _w.document);
          windmill.ui.results.writeResult(_w.location.href);
        }
      }
      return false;
    };
    p.test = f;

    return windmill.controller.waits.forJSTrue(p, obj, true);
  }
  setTimeout(sl, 3000);
  //we can't access the body, so now wait for the loading
  //setTimeout(sl, 0);
}

//wait for an element to show up on the page
//if it doesn't after a provided timeout, defaults to 20 seconds
windmill.controller.waits._forNotTitleAttach = function (paramObj,obj) {
    var p = paramObj || {};
    var f = function () {
      try{
        if (opener.document.title != param_object.title){
          var d = opener.document.body;
          return true;
        }
        return false;
      }
      catch(err){ return false; }
    };
    p.test = f;
    p.timeout = 60000;
    return windmill.controller.waits.forJSTrue(p, obj, true);
};

// //Turn the loop back on when the page in the testingApp window is loaded
// //this is an internal wait used only for the first load of the page
// //a more generic one will be added if there is a need
// windmill.controller.waits._forNotTitleAttach = function (param_object) {
//   _this = this;
//
//   var timeout = 80000;
//   var count = 0;
//   var p = param_object;
//
//   if (p.timeout){
//     timeout = p.timeout;
//   }
//   this.lookup = function(){
//     if (count >= timeout){
//       windmill.controller.continueLoop();
//       return false;
//     }
//     try {
//       if (windmill.testWindow.document.title == p.title){
//        var n = false;
//       }
//       else { var n = true };
//     }
//     catch(err){
//       n = false;
//     }
//     count += 2500;
//
//     this.check(n);
//   }
//
//   this.check = function(n){
//
//     if (!n){
//       var x = setTimeout(function () { _this.lookup(); }, 1000);
//     }
//     else{
//
//       try {
//         if (typeof(windmill.testWindow.onload.listenReg) == 'undefined'){
//           windmill.loaded();
//         }
//       }
//       catch(err){ this.lookup() }
//         fleegix.event.suppressHandlerErrors(windmill.testWindow, 'onload');
//         fleegix.event.unlisten(windmill.testWindow, 'onload', windmill, 'loaded');
//         fleegix.event.listen(windmill.testWindow, 'onload', windmill, 'loaded');
//         _this.lookup();
//
//       return true;
//     }
//   }
//
//   this.lookup();
//
//   //if windmill.timeout goes by and the tests haven't been started
//   //We go ahead and start them, longer waits can happen by changing windmill.timeout
//   ct = function(){
//    //windmill.controller.continueLoop();
//    windmill.controller.waits.forPageLoad({timeout:"50000"});
//  }
//    windmill.loadTimeoutId = setTimeout('ct()', windmill.timeout);
//
//   return true;
// }
<|endoftext|>


<commit_before>'use strict';

const rethinkDB = require('rethinkdb');
const program = require('commander');

const log = require('./../server/log');

export class CoralUserGenerator {
  constructor (conn, number, startLocationId) {
    this.conn = conn;
    this.name = 'coral';
    this.number = number;

    this.user = {x: 0, y: 0, direction: 'up', login: undefined, location: startLocationId};
  }

  generate () {
    rethinkDB
      .tableCreate('userPosition')
      .run(this.conn, (err) => {
        if (err) throw err;

        let login;

        for (let i = 0; i < this.number; i++) {
          this.user.login = `${this.name}${i}`;

          log.info(`Create test user ${this.user.login}`);

          rethinkDB
            .table('userPosition')
            .insert(this.user)
            .run(this.conn, (err) => {
              if (err) throw err;
            });
        }
      });
  }
}

<commit_msg>add online field<commit_after>'use strict';

const rethinkDB = require('rethinkdb');
const program = require('commander');

const log = require('./../server/log');

export class CoralUserGenerator {
  constructor (conn, number, startLocationId) {
    this.conn = conn;
    this.name = 'coral';
    this.number = number;

    this.user = {
      x: 0,
      y: 0,
      direction: 'up',
      login: undefined,
      location: startLocationId,
      online: false};
  }

  generate () {
    rethinkDB
      .tableCreate('userPosition')
      .run(this.conn, (err) => {
        if (err) throw err;

        let login;

        for (let i = 0; i < this.number; i++) {
          this.user.login = `${this.name}${i}`;

          log.info(`Create test user ${this.user.login}`);

          rethinkDB
            .table('userPosition')
            .insert(this.user)
            .run(this.conn, (err) => {
              if (err) throw err;
            });
        }
      });
  }
}

<|endoftext|>


<commit_before>// ---------------------------------------------
// @utils
// ---------------------------------------------

var Module = (function () {

    // Private Methods
    // ---------------------------------------------

    // Public Methods
    // ---------------------------------------------

    function init() {

        $.fn.ajax_submit_form = function (url, options) {

            if (options === undefined) {
                options = {};
            }

            var data = $(this).serialize();

            var ajax_options = {
                type: "POST",
                url: url,
                data: data,
                dataType: 'json'
            };

            var jqXHR = $.ajax($.extend(ajax_options, options));

            if (options.done_fun !== undefined) {
                jqXHR.done(options.done_fun);
            }

            if (options.fail_fun !== undefined) {
                jqXHR.fail(options.fail_fun);
            }
        };

        $.fn.exists = function () {
            return !!this.length;
        };

        $.fn.display_block = function () {

            this.removeClass('u--hide').addClass('u--show');
        };

        $.fn.display_none = function () {

            this.removeClass('u--show').addClass('u--hide');
        };

        $.fn.handle_content_height = function ($content, $header, $footer, options) {

            var params = options || {};

            var window_h = $(window).height();
            var partials_h = ($header.innerHeight() + $footer.innerHeight());
            var offset_h = window_h - partials_h;

            if (partials_h <= window_h) {
                $content.css({
                    'height': offset_h + 'px'
                });
            }
        };

        $.fn.remove_class_except = function (val) {
            return this.each(function (index, el) {
                var keep = val.split(" "),  // list we'd like to keep
                    reAdd = [],          // ones that should be re-added if found
                    $el = $(el);       // element we're working on
                // look for which we re-add (based on them already existing)
                for (var c = 0; c < keep.length; c++) {
                    if ($el.hasClass(keep[c])) reAdd.push(keep[c]);
                }

                // drop all, and only add those confirmed as existing
                $el
                    .removeClass()               // remove existing classes
                    .addClass(reAdd.join(' '));  // re-add the confirmed ones
            });
        };

        $.fn.fill_nav = function (li, dest) {
            $(li).each(function () {
                var cp = $(this).clone();
                cp.remove_class_except('s--active').removeAttr('id');
                cp.appendTo($(dest));
            });
        };

        document.querySelector('.m--navbar__toggle')
            .addEventListener('click', function () {
                this.classList.toggle('active');
            });

        function load_scripts(array, callback) {
            var loader = function (src, handler) {
                var script = document.createElement("script");
                script.src = src;
                script.onload = script.onreadystatechange = function () {
                    script.onreadystatechange = script.onload = null;
                    handler();
                };
                var head = document.getElementsByTagName("head")[0];
                (head || document.body).appendChild(script);
            };
            (function () {
                if (array.length !== 0) {
                    loader(('https:' === location.protocol ? 'https:' : 'http:') + array.shift(), arguments.callee);
                } else {
                    callback && callback();
                }
            })();
        }
    }

    // Module API
    // ---------------------------------------------

    return {
        init: init
    };

})();

// Module Export
// ---------------------------------------------

module.exports = Module;<commit_msg>added loadscript<commit_after>// ---------------------------------------------
// @utils
// ---------------------------------------------

var Module = (function () {

    // Private Methods
    // ---------------------------------------------

    // Public Methods
    // ---------------------------------------------

    function init() {

        $.fn.ajax_submit_form = function (url, options) {

            if (options === undefined) {
                options = {};
            }

            var data = $(this).serialize();

            var ajax_options = {
                type: "POST",
                url: url,
                data: data,
                dataType: 'json'
            };

            var jqXHR = $.ajax($.extend(ajax_options, options));

            if (options.done_fun !== undefined) {
                jqXHR.done(options.done_fun);
            }

            if (options.fail_fun !== undefined) {
                jqXHR.fail(options.fail_fun);
            }
        };

        $.fn.exists = function () {
            return !!this.length;
        };

        $.fn.display_block = function () {

            this.removeClass('u--hide').addClass('u--show');
        };

        $.fn.display_none = function () {

            this.removeClass('u--show').addClass('u--hide');
        };

        $.fn.handle_content_height = function ($content, $header, $footer, options) {

            var params = options || {};

            var window_h = $(window).height();
            var partials_h = ($header.innerHeight() + $footer.innerHeight());
            var offset_h = window_h - partials_h;

            if (partials_h <= window_h) {
                $content.css({
                    'height': offset_h + 'px'
                });
            }
        };

        $.fn.remove_class_except = function (val) {
            return this.each(function (index, el) {
                var keep = val.split(" "),  // list we'd like to keep
                    reAdd = [],          // ones that should be re-added if found
                    $el = $(el);       // element we're working on
                // look for which we re-add (based on them already existing)
                for (var c = 0; c < keep.length; c++) {
                    if ($el.hasClass(keep[c])) reAdd.push(keep[c]);
                }

                // drop all, and only add those confirmed as existing
                $el
                    .removeClass()               // remove existing classes
                    .addClass(reAdd.join(' '));  // re-add the confirmed ones
            });
        };

        $.fn.fill_nav = function (li, dest) {
            $(li).each(function () {
                var cp = $(this).clone();
                cp.remove_class_except('s--active').removeAttr('id');
                cp.appendTo($(dest));
            });
        };

        document.querySelector('.m--navbar__toggle')
            .addEventListener('click', function () {
                this.classList.toggle('active');
            });

        window.load_scripts = function (array, callback) {
            var loader = function (src, handler) {
                var script = document.createElement("script");
                script.src = src;
                script.onload = script.onreadystatechange = function () {
                    script.onreadystatechange = script.onload = null;
                    handler();
                };
                var head = document.getElementsByTagName("head")[0];
                (head || document.body).appendChild(script);
            };
            (function () {
                if (array.length !== 0) {
                    loader(('https:' === location.protocol ? 'https:' : 'http:') + array.shift(), arguments.callee);
                } else {
                    callback && callback();
                }
            })();
        }
    }

    // Module API
    // ---------------------------------------------

    return {
        init: init
    };

})();

// Module Export
// ---------------------------------------------

module.exports = Module;<|endoftext|>


<commit_before>var exec = require('cordova/exec');

var downloadStatus = {
    NONE: 0,
    INPROGRESS: 1,
    DONE: 2,
    FAILED: 3
}

var playlistPrefix = "playlist-";

var isInit = false;
var tracks = [];
var playlistIdLookup = null;
var getIdsPromise = null;

// Configure local forage.
audioPlugin.localForage.config({
    name: 'cordovaAudioPlaylists'
});

exports.initAudio = function(success, error) {
    isInit = true;

    return execPromise(success, error, "CordovaPluginAudioPlaylist", "initAudio", []);
};

exports.isInit = function() {
    return isInit;
}

exports.clearPlaylist = function(success, error) {
    tracks = [];
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "clearPlaylist", []);
}

exports.getCurrentTrack = function(success, error) {
    return new Promise(function(resolve, reject) {
        execPromise(success, error, "CordovaPluginAudioPlaylist", "getPlayIndex", []).then((index) => {
            resolve(tracks[index]);
        }).catch((err) => {
            reject(reject);
        });
    });
}

exports.getTracks = function() {
    return tracks;
}

exports.isLastTrack = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "isLastTrack", []);
}

exports.addItem = function(arg0, success, error) {
    if (arg0 instanceof Object && arg0.hasOwnProperty("id") && arg0.hasOwnProperty("url")) {
        tracks.push(arg0);

        return audioPlugin.localForage.getItem("track-url-" + arg0.id).then(function(result) {
            if (null !== result) {
                arg0.url = result;
            }
        }).catch(function(err) {
            return Promise.reject(err);
        }).then(function() {
            return execPromise(success, error, "CordovaPluginAudioPlaylist", "addItem", [arg0]);
        })
    } else {
        throw "Track not a valid object";
    }
};

exports.addManyItems = function(arg0, success, error) {
    if (Array.isArray(arg0)) {
        return Promise.all(arg0.map(
            function(track) {
                tracks.push(track);

                return audioPlugin.localForage.getItem("track-url-" + track.id).then(function(result) {
                    if (null !== result) {
                        track.url = result;
                    }
                })
            })
        ).then(function(successResult) {

        }).catch(function(err) {
            return Promise.reject(err);
        }).then(function() {
            // Finally.
            return execPromise(success, error, "CordovaPluginAudioPlaylist", "addManyItems", [arg0]);
        });
    } else {
        throw "Add Many Items must be an array."
    }
};

exports.play = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "play", []);
};

exports.pause = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "pause", []);
};

exports.toggle = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "toggle", []);
}

exports.next = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "next", []);
}

exports.previous = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "previous", []);
}

exports.stop = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "stop", []);
}

exports.loop = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "loop", []);
}

exports.watch = function(successCallback, error) {
    return exec(successCallback, error, "CordovaPluginAudioPlaylist", "watch", [])
}

exports.isPlaylistSaved = function(playlistId) {
    return new Promise(function(resolve, reject) {
        getPlaylistLookupAsync().then(function(success) {
            resolve(playlistIdLookup.hasOwnProperty(playlistId));
        }).catch(function(err) {
            reject(err);
        });
    });
}

exports.savePlaylistOffline = function(inputPlaylist) {
    var playlist =  {
        id: "",
        title: "",
        albumPicture: "",
        songs: null,
        downloadStatus: downloadStatus.NONE
    }

    playlist = extend(playlist, inputPlaylist, true);
    playlist.songs.forEach(function(song) {
        song.downloadStatus = downloadStatus.NONE;
    });

    return audioPlugin.localForage.setItem(playlistPrefix + playlist.id, playlist).then(function() {
        if (!isEmpty(playlistIdLookup)) {
            playlistIdLookup[playlist.id] = true;
        }

        downloadPlaylist(playlist);
    });
}

exports.getPlaylistOffline = function(playlistId) {
    return audioPlugin.localForage.getItem(playlistPrefix + playlistId);
}

exports.removePlaylistOffline = function(playlistId) {
    // TODO: Actually delete stored tracks.
    return audioPlugin.localForage.removeItem(playlistPrefix + playlistId).then(function() {
        if (!isEmpty(playlistIdLookup)) {
            delete playlistIdLookup[playlistId];
        }
    });
}

exports.syncPlaylistOffline = function(playlistFromServer) {
    return downloadPlaylist(playlist);
}

exports.resumeDownload = function(playlistId) {
    return audioPlugin.localForage.getItem(playlistPrefix + playlistId).then((playlist) => {
        if (!isEmpty(playlist)) {
            downloadPlaylist(playlist);
        }
    })
};

exports.downloadTrack = function(track) {
    return downloadTrack(track);
}

function downloadPlaylist(playlist) {
    return new Promise(function(resolve, reject) {
        audioPlugin.localForage.getItem(playlistPrefix + playlist.id).then(function(result) {
            if (result !== null) {
                playlist = syncPlaylists(playlist, result);
            }

            Promise.all(
                playlist.songs.map(function(song, index) {
                    switch (song.downloadStatus) {
                        case downloadStatus.NONE:
                        case downloadStatus.FAILED:
                            return downloadTrack(song).then(function(trackResult) {
                                return audioPlugin.localForage.setItem("track-url-" + song.id, trackResult).then(function(storeSongResult) {
                                    playlist.songs[index].url = trackResult;
                                    playlist.songs[index].downloadStatus = downloadStatus.DONE;
                                });
                            }).catch(function(trackErr) {
                                playlist.songs[index].downloadStatus = downloadStatus.FAILED;
                            });
                        default:
                            return Promise.resolve();
                    }
                })
            ).then(function(allSuccess) {
                playlist.downloadStatus = downloadStatus.DONE;
                audioPlugin.localForage.setItem(playlistPrefix + playlist.id, playlist).then(function() {
                    resolve(playlist);
                }).catch(function(saveErr) {
                    reject(saveErr);
                });
            }).catch(function(allErr) {
                reject(allErr);
            });
        });
    });
}

function downloadTrack(track) {
    return new Promise(function(resolve, reject) {
        window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function (dirEntry) {
            var fileTransfer = new FileTransfer();
            var uri = track.url;

            // Parameters passed to getFile create a new file or return the file if it already exists.
            dirEntry.getFile(track.id + '.mp3', { create: true, exclusive: false }, function (fileEntry) {
                fileTransfer.download(
                    uri,
                    fileEntry.nativeURL,
                    function(entry) {
                        resolve(entry.nativeURL);
                    },
                    function(error) {
                        reject(error);
                    },
                    false,
                    {
                        headers: {}
                    }
                );
            }, function(onErrorCreateFile) {
                reject(onErrorCreateFile);
            });
        }, function(onErrorFs) {
            reject(onErrorFs);
        });
    });
}

/**
 * Syncs an incoming playlist from server with saved playlist on device.
 *
 * @param {any} inputPlaylist
 * @param {any} savedPlaylist
 * @returns
 */
function syncPlaylists(inputPlaylist, savedPlaylist) {
    var mergedSongList = syncSongLists(inputPlaylist.songs, savedPlaylist.songs);

    var output = extend(savedPlaylist, inputPlaylist, true);
    output.songs = mergedSongList;

    return output;
}

/**
 * Syncs song lists to ensure already downloaded songs not downloaded again.
 *
 * @param {any} inputSongs
 * @param {any} savedSongs
 * @returns
 */
function syncSongLists(inputSongs, savedSongs) {
    var lookup = {};

    // Create lookup object for faster matching.
    savedSongs.forEach(function(song) {
        lookup[song.id] = song;
    });

    inputSongs.forEach(function(song, index) {
        if (lookup.hasOwnProperty(song.id)) {
            var savedMatch = lookup[song.id];
            song = extend(song, savedMatch, false);
        }
    });

    return inputSongs;
}

function getPlaylistLookupAsync() {
    if (isEmpty(playlistIdLookup)) {
        if (isEmpty(getIdsPromise)) {
            getIdsPromise = new Promise(function(resolve, reject) {
                audioPlugin.localForage.keys().then(function(keys) {
                    playlistIdLookup = {};

                    keys.forEach(function(key) {
                        if (key.lastIndexOf(playlistPrefix, 0) === 0) {
                            playlistIdLookup[key.substring(playlistPrefix.length)] = true;
                        }
                    });

                    resolve(playlistIdLookup);
                }).catch(function(err) {
                    reject(err);
                });
            });
        }

        return getIdsPromise;
    } else {
        return Promise.resolve(playlistIdLookup);
    }
}

function execPromise(success, error, pluginName, method, args) {
    return new Promise(function (resolve, reject) {
        exec(function (result) {
                resolve(result);
                if (typeof success === "function") {
                    success(result);
                }
            },
            function (reason) {
                reject(reason);
                if (typeof error === "function") {
                    error(reason);
                }
            },
            pluginName,
            method,
            args);
    });
}

function extend(obj, src, overwrite) {
    Object.keys(src).forEach(function(key) {
        if (obj.hasOwnProperty(key)) {
            if (isEmpty(obj[key]) || overwrite === true) {
                obj[key] = src[key];
            }
        } else {
            obj[key] = src[key];
        }
    });
    return obj;
}

function isEmpty(e) {
  switch (e) {
    case "":
    case 0:
    case "0":
    case null:
    case false:
    case typeof this === "undefined":
      return true;
    default:
      return false;
  }
}<commit_msg>bug fix<commit_after>var exec = require('cordova/exec');

var downloadStatus = {
    NONE: 0,
    INPROGRESS: 1,
    DONE: 2,
    FAILED: 3
}

var playlistPrefix = "playlist-";

var isInit = false;
var localForageInit = false;
var tracks = [];
var playlistIdLookup = null;
var getIdsPromise = null;

exports.initAudio = function(success, error) {
    if (!localForageInit) {
        configureLocalForage();
    }

    isInit = true;

    return execPromise(success, error, "CordovaPluginAudioPlaylist", "initAudio", []);
};

exports.isInit = function() {
    return isInit;
}

exports.clearPlaylist = function(success, error) {
    tracks = [];
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "clearPlaylist", []);
}

exports.getCurrentTrack = function(success, error) {
    return new Promise(function(resolve, reject) {
        execPromise(success, error, "CordovaPluginAudioPlaylist", "getPlayIndex", []).then((index) => {
            resolve(tracks[index]);
        }).catch((err) => {
            reject(reject);
        });
    });
}

exports.getTracks = function() {
    return tracks;
}

exports.isLastTrack = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "isLastTrack", []);
}

exports.addItem = function(arg0, success, error) {
    if (arg0 instanceof Object && arg0.hasOwnProperty("id") && arg0.hasOwnProperty("url")) {
        tracks.push(arg0);

        return audioPlugin.localForage.getItem("track-url-" + arg0.id).then(function(result) {
            if (null !== result) {
                arg0.url = result;
            }
        }).catch(function(err) {
            return Promise.reject(err);
        }).then(function() {
            return execPromise(success, error, "CordovaPluginAudioPlaylist", "addItem", [arg0]);
        })
    } else {
        throw "Track not a valid object";
    }
};

exports.addManyItems = function(arg0, success, error) {
    if (Array.isArray(arg0)) {
        return Promise.all(arg0.map(
            function(track) {
                tracks.push(track);

                return audioPlugin.localForage.getItem("track-url-" + track.id).then(function(result) {
                    if (null !== result) {
                        track.url = result;
                    }
                })
            })
        ).then(function(successResult) {

        }).catch(function(err) {
            return Promise.reject(err);
        }).then(function() {
            // Finally.
            return execPromise(success, error, "CordovaPluginAudioPlaylist", "addManyItems", [arg0]);
        });
    } else {
        throw "Add Many Items must be an array."
    }
};

exports.play = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "play", []);
};

exports.pause = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "pause", []);
};

exports.toggle = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "toggle", []);
}

exports.next = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "next", []);
}

exports.previous = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "previous", []);
}

exports.stop = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "stop", []);
}

exports.loop = function(success, error) {
    return execPromise(success, error, "CordovaPluginAudioPlaylist", "loop", []);
}

exports.watch = function(successCallback, error) {
    return exec(successCallback, error, "CordovaPluginAudioPlaylist", "watch", [])
}

exports.isPlaylistSaved = function(playlistId) {
    if (!localForageInit) {
        configureLocalForage();
    }

    return new Promise(function(resolve, reject) {
        getPlaylistLookupAsync().then(function(success) {
            resolve(playlistIdLookup.hasOwnProperty(playlistId));
        }).catch(function(err) {
            reject(err);
        });
    });
}

exports.savePlaylistOffline = function(inputPlaylist) {
    if (!localForageInit) {
        configureLocalForage();
    }

    var playlist =  {
        id: "",
        title: "",
        albumPicture: "",
        songs: null,
        downloadStatus: downloadStatus.NONE
    }

    playlist = extend(playlist, inputPlaylist, true);
    playlist.songs.forEach(function(song) {
        song.downloadStatus = downloadStatus.NONE;
    });

    return audioPlugin.localForage.setItem(playlistPrefix + playlist.id, playlist).then(function() {
        if (!isEmpty(playlistIdLookup)) {
            playlistIdLookup[playlist.id] = true;
        }

        downloadPlaylist(playlist);
    });
}

exports.getPlaylistOffline = function(playlistId) {
    if (!localForageInit) {
        configureLocalForage();
    }

    return audioPlugin.localForage.getItem(playlistPrefix + playlistId);
}

exports.removePlaylistOffline = function(playlistId) {
    if (!localForageInit) {
        configureLocalForage();
    }

    // TODO: Actually delete stored tracks.
    return audioPlugin.localForage.removeItem(playlistPrefix + playlistId).then(function() {
        if (!isEmpty(playlistIdLookup)) {
            delete playlistIdLookup[playlistId];
        }
    });
}

exports.syncPlaylistOffline = function(playlistFromServer) {
    if (!localForageInit) {
        configureLocalForage();
    }

    return downloadPlaylist(playlist);
}

exports.resumeDownload = function(playlistId) {
    if (!localForageInit) {
        configureLocalForage();
    }

    return audioPlugin.localForage.getItem(playlistPrefix + playlistId).then((playlist) => {
        if (!isEmpty(playlist)) {
            downloadPlaylist(playlist);
        }
    })
};

exports.downloadTrack = function(track) {
    if (!localForageInit) {
        configureLocalForage();
    }

    return downloadTrack(track);
}

function downloadPlaylist(playlist) {
    return new Promise(function(resolve, reject) {
        audioPlugin.localForage.getItem(playlistPrefix + playlist.id).then(function(result) {
            if (result !== null) {
                playlist = syncPlaylists(playlist, result);
            }

            Promise.all(
                playlist.songs.map(function(song, index) {
                    switch (song.downloadStatus) {
                        case downloadStatus.NONE:
                        case downloadStatus.FAILED:
                            return downloadTrack(song).then(function(trackResult) {
                                return audioPlugin.localForage.setItem("track-url-" + song.id, trackResult).then(function(storeSongResult) {
                                    playlist.songs[index].url = trackResult;
                                    playlist.songs[index].downloadStatus = downloadStatus.DONE;
                                });
                            }).catch(function(trackErr) {
                                playlist.songs[index].downloadStatus = downloadStatus.FAILED;
                            });
                        default:
                            return Promise.resolve();
                    }
                })
            ).then(function(allSuccess) {
                playlist.downloadStatus = downloadStatus.DONE;
                audioPlugin.localForage.setItem(playlistPrefix + playlist.id, playlist).then(function() {
                    resolve(playlist);
                }).catch(function(saveErr) {
                    reject(saveErr);
                });
            }).catch(function(allErr) {
                reject(allErr);
            });
        });
    });
}

function downloadTrack(track) {
    return new Promise(function(resolve, reject) {
        window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function (dirEntry) {
            var fileTransfer = new FileTransfer();
            var uri = track.url;

            // Parameters passed to getFile create a new file or return the file if it already exists.
            dirEntry.getFile(track.id + '.mp3', { create: true, exclusive: false }, function (fileEntry) {
                fileTransfer.download(
                    uri,
                    fileEntry.nativeURL,
                    function(entry) {
                        resolve(entry.nativeURL);
                    },
                    function(error) {
                        reject(error);
                    },
                    false,
                    {
                        headers: {}
                    }
                );
            }, function(onErrorCreateFile) {
                reject(onErrorCreateFile);
            });
        }, function(onErrorFs) {
            reject(onErrorFs);
        });
    });
}

/**
 * Syncs an incoming playlist from server with saved playlist on device.
 *
 * @param {any} inputPlaylist
 * @param {any} savedPlaylist
 * @returns
 */
function syncPlaylists(inputPlaylist, savedPlaylist) {
    var mergedSongList = syncSongLists(inputPlaylist.songs, savedPlaylist.songs);

    var output = extend(savedPlaylist, inputPlaylist, true);
    output.songs = mergedSongList;

    return output;
}

/**
 * Syncs song lists to ensure already downloaded songs not downloaded again.
 *
 * @param {any} inputSongs
 * @param {any} savedSongs
 * @returns
 */
function syncSongLists(inputSongs, savedSongs) {
    var lookup = {};

    // Create lookup object for faster matching.
    savedSongs.forEach(function(song) {
        lookup[song.id] = song;
    });

    inputSongs.forEach(function(song, index) {
        if (lookup.hasOwnProperty(song.id)) {
            var savedMatch = lookup[song.id];
            song = extend(song, savedMatch, false);
        }
    });

    return inputSongs;
}

function getPlaylistLookupAsync() {
    if (isEmpty(playlistIdLookup)) {
        if (isEmpty(getIdsPromise)) {
            getIdsPromise = new Promise(function(resolve, reject) {
                audioPlugin.localForage.keys().then(function(keys) {
                    playlistIdLookup = {};

                    keys.forEach(function(key) {
                        if (key.lastIndexOf(playlistPrefix, 0) === 0) {
                            playlistIdLookup[key.substring(playlistPrefix.length)] = true;
                        }
                    });

                    resolve(playlistIdLookup);
                }).catch(function(err) {
                    reject(err);
                });
            });
        }

        return getIdsPromise;
    } else {
        return Promise.resolve(playlistIdLookup);
    }
}

function configureLocalForage() {
    // Configure local forage.
    audioPlugin.localForage.config({
        name: 'cordovaAudioPlaylists'
    });

    localForageInit = true;
}

function execPromise(success, error, pluginName, method, args) {
    return new Promise(function (resolve, reject) {
        exec(function (result) {
                resolve(result);
                if (typeof success === "function") {
                    success(result);
                }
            },
            function (reason) {
                reject(reason);
                if (typeof error === "function") {
                    error(reason);
                }
            },
            pluginName,
            method,
            args);
    });
}

function extend(obj, src, overwrite) {
    Object.keys(src).forEach(function(key) {
        if (obj.hasOwnProperty(key)) {
            if (isEmpty(obj[key]) || overwrite === true) {
                obj[key] = src[key];
            }
        } else {
            obj[key] = src[key];
        }
    });
    return obj;
}

function isEmpty(e) {
  switch (e) {
    case "":
    case 0:
    case "0":
    case null:
    case false:
    case typeof this === "undefined":
      return true;
    default:
      return false;
  }
}<|endoftext|>


<commit_before>define([
    'underscore', 'kendo', 'q',
    'Fixtures',
    'text!textassets/types/Contact.json'
], function (_, kendo, Q, Fixtures, ContactModel) {
    'use strict';

    var ContactModel = JSON.parse(ContactModel).data;
    var database = Fixtures.generateDatabase();

    var MockDatabaseTransport = kendo.Class.extend({
        read: function (request) {
            Q.delay(100).then(function () {

                // Backend's job is to apply the filters and compute the facets

//                var filters = _.map(this.state.filters, function (fieldValues, fieldName) {
//                    var activeFieldValues = filterMap(fieldValues, function (pair) { return !!pair[1]; });
//                    return _.map(_.keys(activeFieldValues), function (fieldValue) {
//                        return { field: fieldName, operator: "eq", value: fieldValue };
//                    });
//                });
//                this.dataSource.filter({ logic: 'or', filters: _.flatten(filters) });

                var facetableFieldInfos = filterMap(ContactModel.properties, function (pair) { return !pair[1].hidden; });
                var facets = _.chain(facetableFieldInfos)
                    .map(function (fieldInfo, fieldName) {
                        var grouped = _.groupBy(database, fieldName);
                        var countsByVal = _.object(_.map(grouped, function (v, k) { return [k, v.length] }));
                        return [fieldName, countsByVal];
                    }).object().value();

                //return [fieldName, _.groupBy(database, fieldName)]; // groupBy has to happen in the database
                //return [fieldName, _.union(_.pluck(database, fieldName))]; // groupBy has to happen in the database

                request.success({ results: database, facets: facets });
            }.bind(this)).done();
        }
    });

    function filterMap (record, predicate) {
        return _.object(_.filter(_.pairs(record), predicate));
    }

    return MockDatabaseTransport;
});
<commit_msg>remove unused code<commit_after>define([
    'underscore', 'kendo', 'q',
    'Fixtures',
    'text!textassets/types/Contact.json'
], function (_, kendo, Q, Fixtures, ContactModel) {
    'use strict';

    var ContactModel = JSON.parse(ContactModel).data;
    var database = Fixtures.generateDatabase();

    var MockDatabaseTransport = kendo.Class.extend({
        read: function (request) {
            Q.delay(100).then(function () {

                // Backend's job is to apply the filters and compute the facets

//                var filters = _.map(this.state.filters, function (fieldValues, fieldName) {
//                    var activeFieldValues = filterMap(fieldValues, function (pair) { return !!pair[1]; });
//                    return _.map(_.keys(activeFieldValues), function (fieldValue) {
//                        return { field: fieldName, operator: "eq", value: fieldValue };
//                    });
//                });
//                this.dataSource.filter({ logic: 'or', filters: _.flatten(filters) });

                var facetableFieldInfos = filterMap(ContactModel.properties, function (pair) { return !pair[1].hidden; });
                var facets = _.chain(facetableFieldInfos)
                    .map(function (fieldInfo, fieldName) {
                        var grouped = _.groupBy(database, fieldName);
                        var countsByVal = _.object(_.map(grouped, function (v, k) { return [k, v.length] }));
                        return [fieldName, countsByVal];
                    }).object().value();

                request.success({ results: database, facets: facets });
            }.bind(this)).done();
        }
    });

    function filterMap (record, predicate) {
        return _.object(_.filter(_.pairs(record), predicate));
    }

    return MockDatabaseTransport;
});
<|endoftext|>


<commit_before><commit_msg>Message creation finishes without exception even when there are no attachments, tags,...<commit_after><|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

sc_require('system/responder');

/** @class

  The root object for a responder chain.  A responder context can dispatch
  actions directly to a first responder; walking up the responder chain until
  it finds a responder that can handle the action.

  If no responder can be found to handle the action, it will attempt to send
  the action to the defaultResponder.

  You can have as many ResponderContext's as you want within your application.
  Every SC.Pane and SC.Application automatically implements this mixin.

  Note that to implement this you must be a responder yourself.

  @extends SC.Responder
  @since SproutCore 1.0
*/
SC.ResponderContext = SC.Responder.extend({

  // ..........................................................
  // PROPERTIES
  //

  isResponderContext: YES,

  /** @property

    When set to YES, logs tracing information about all actions sent and
    responder changes.
  */
  trace: NO,

  /** @property
    The default responder.  Set this to point to a responder object that can
    respond to events when no other view in the hierarchy handles them.

    You can also implement actions directly on the application object if you
    prefer.
  */
  defaultResponder: null,

  /** @property
    The next responder for an app is always its defaultResponder.
  */
  nextResponder: function() {
    return this.get('defaultResponder');
  }.property('defaultResponder').cacheable(),

  /** @property
    The first responder.  This is the first responder that should receive
    actions.
  */
  firstResponder: null,

  // ..........................................................
  // METHODS
  //

  /**
    Finds the next responder for the passed responder based on the responder's
    nextResponder property.  If the property is a string, then lookup the path
    in the receiver.
  */
  nextResponderFor: function(responder) {
    var next = responder.get('nextResponder');
    if (typeof next === SC.T_STRING) {
      next = SC.objectForPropertyPath(next, this);
    } else if (!next && (responder !== this)) next = this ;
    return next ;
  },

  /**
    Finds the responder name by searching the responders one time.
  */
  responderNameFor: function(responder) {
    if (!responder) return "(No Responder)";
    else if (responder._scrc_name) return responder._scrc_name;

    // none found, let's go hunting...look three levels deep
    var n = this.NAMESPACE;
    this._findResponderNamesFor(this, 3, n ? [this.NAMESPACE] : []);

    return responder._scrc_name || responder.toString(); // try again
  },

  _findResponderNamesFor: function(responder, level, path) {
    var key, value;

    for(key in responder) {
      if (key === 'nextResponder') continue ;
      value = responder[key];
      if (value && value.isResponder) {
        if (value._scrc_name) continue ;
        path.push(key);
        value._scrc_name = path.join('.');
        if (level>0) this._findResponderNamesFor(value, level-1, path);
        path.pop();
      }
    }
  },

  /**
    Makes the passed responder into the new firstResponder for this
    responder context.  This will cause the current first responder to lose
    its responder status and possibly keyResponder status as well.

    When you change the first responder, this will send callbacks to
    responders up the chain until you reach a shared responder, at which point
    it will stop notifying.

    @param {SC.Responder} responder
    @returns {SC.ResponderContext} receiver
  */
  makeFirstResponder: function(responder) {
    var current = this.get('firstResponder'),
        last    = this.get('nextResponder'),
        trace   = this.get('trace'),
        common ;

    if (this._locked) {
      if (trace) {
        console.log('%@: AFTER ACTION: makeFirstResponder => %@'.fmt(this, this.responderNameFor(responder)));
      }

      this._pendingResponder = responder;
      return ;
    }

    if (trace) {
      console.log('%@: makeFirstResponder => %@'.fmt(this, this.responderNameFor(responder)));
    }

    responder.set("becomingFirstResponder", YES);

    this._locked = YES;
    this._pendingResponder = null;

    // Find the nearest common responder in the responder chain for the new
    // responder.  If there are no common responders, use last responder.
    common = responder ? this.nextResponderFor(responder) : null;
    while (common) {
      if (common.get('hasFirstResponder')) break;
      common = (common===last) ? null : this.nextResponderFor(common);
    }
    if (!common) common = last;

    // Cleanup old first responder
    this._notifyWillLoseFirstResponder(current, current, common);
    if (current) current.set('isFirstResponder', NO);

    // Set new first responder.  If new firstResponder does not have its
    // responderContext property set, then set it.
    this.set('firstResponder', responder) ;
    if (responder) responder.set('isFirstResponder', YES);

    this._notifyDidBecomeFirstResponder(responder, responder, common);

    this._locked = NO ;
    if (this._pendingResponder) {
      this.makeFirstResponder(this._pendingResponder);
      this._pendingResponder = null;
    }

    responder.set("becomingFirstResponder", NO);

    return this ;
  },

  _notifyWillLoseFirstResponder: function(responder, cur, root) {
    if (cur === root) return ; // nothing to do

    cur.willLoseFirstResponder(responder);
    cur.set('hasFirstResponder', NO);

    var next = this.nextResponderFor(cur);
    if (next) this._notifyWillLoseFirstResponder(responder, next, root);
  },

  _notifyDidBecomeFirstResponder: function(responder, cur, root) {
    if (cur === root) return ; // nothing to do

    var next = this.nextResponderFor(cur);
    if (next) this._notifyDidBecomeFirstResponder(responder, next, root);

    cur.set('hasFirstResponder', YES);
    cur.didBecomeFirstResponder(responder);
  },

  /**
    Send the passed action down the responder chain, starting with the
    current first responder.  This will look for the first responder that
    actually implements the action method and returns YES or no value when
    called.

    @param {String} action name of action
    @param {Object} sender object sending the action
    @param {Object} context optional additonal context info
    @returns {SC.Responder} the responder that handled it or null
  */
  sendAction: function(action, sender, context) {
    var working = this.get('firstResponder'),
        last    = this.get('nextResponder'),
        trace   = this.get('trace'),
        handled = NO,
        responder;

    this._locked = YES;
    if (trace) {
      console.log("%@: begin action '%@' (%@, %@)".fmt(this, action, sender, context));
    }

    while (!handled && working) {
      if (working.tryToPerform) {
        handled = working.tryToPerform(action, sender, context);
      }

      if (!handled) {
        working = (working===last) ? null : this.nextResponderFor(working);
      }
    }

    if (trace) {
      if (!handled) console.log("%@:  action '%@' NOT HANDLED".fmt(this,action));
      else console.log("%@: action '%@' handled by %@".fmt(this, action, this.responderNameFor(working)));
    }

    this._locked = NO ;

    if (responder = this._pendingResponder) {
      this._pendingResponder= null ;
      this.makeFirstResponder(responder);
    }

    return working ;
  }

});
<commit_msg>Is this a good idea? Use begin/endPropertyChanges so hasFirstResponder is set before firstResponder notifications go out.<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

sc_require('system/responder');

/** @class

  The root object for a responder chain.  A responder context can dispatch
  actions directly to a first responder; walking up the responder chain until
  it finds a responder that can handle the action.

  If no responder can be found to handle the action, it will attempt to send
  the action to the defaultResponder.

  You can have as many ResponderContext's as you want within your application.
  Every SC.Pane and SC.Application automatically implements this mixin.

  Note that to implement this you must be a responder yourself.

  @extends SC.Responder
  @since SproutCore 1.0
*/
SC.ResponderContext = SC.Responder.extend({

  // ..........................................................
  // PROPERTIES
  //

  isResponderContext: YES,

  /** @property

    When set to YES, logs tracing information about all actions sent and
    responder changes.
  */
  trace: NO,

  /** @property
    The default responder.  Set this to point to a responder object that can
    respond to events when no other view in the hierarchy handles them.

    You can also implement actions directly on the application object if you
    prefer.
  */
  defaultResponder: null,

  /** @property
    The next responder for an app is always its defaultResponder.
  */
  nextResponder: function() {
    return this.get('defaultResponder');
  }.property('defaultResponder').cacheable(),

  /** @property
    The first responder.  This is the first responder that should receive
    actions.
  */
  firstResponder: null,

  // ..........................................................
  // METHODS
  //

  /**
    Finds the next responder for the passed responder based on the responder's
    nextResponder property.  If the property is a string, then lookup the path
    in the receiver.
  */
  nextResponderFor: function(responder) {
    var next = responder.get('nextResponder');
    if (typeof next === SC.T_STRING) {
      next = SC.objectForPropertyPath(next, this);
    } else if (!next && (responder !== this)) next = this ;
    return next ;
  },

  /**
    Finds the responder name by searching the responders one time.
  */
  responderNameFor: function(responder) {
    if (!responder) return "(No Responder)";
    else if (responder._scrc_name) return responder._scrc_name;

    // none found, let's go hunting...look three levels deep
    var n = this.NAMESPACE;
    this._findResponderNamesFor(this, 3, n ? [this.NAMESPACE] : []);

    return responder._scrc_name || responder.toString(); // try again
  },

  _findResponderNamesFor: function(responder, level, path) {
    var key, value;

    for(key in responder) {
      if (key === 'nextResponder') continue ;
      value = responder[key];
      if (value && value.isResponder) {
        if (value._scrc_name) continue ;
        path.push(key);
        value._scrc_name = path.join('.');
        if (level>0) this._findResponderNamesFor(value, level-1, path);
        path.pop();
      }
    }
  },

  /**
    Makes the passed responder into the new firstResponder for this
    responder context.  This will cause the current first responder to lose
    its responder status and possibly keyResponder status as well.

    When you change the first responder, this will send callbacks to
    responders up the chain until you reach a shared responder, at which point
    it will stop notifying.

    @param {SC.Responder} responder
    @returns {SC.ResponderContext} receiver
  */
  makeFirstResponder: function(responder) {
    var current = this.get('firstResponder'),
        last    = this.get('nextResponder'),
        trace   = this.get('trace'),
        common ;

    if (this._locked) {
      if (trace) {
        console.log('%@: AFTER ACTION: makeFirstResponder => %@'.fmt(this, this.responderNameFor(responder)));
      }

      this._pendingResponder = responder;
      return ;
    }

    if (trace) {
      console.log('%@: makeFirstResponder => %@'.fmt(this, this.responderNameFor(responder)));
    }

    responder.set("becomingFirstResponder", YES);

    this._locked = YES;
    this._pendingResponder = null;

    // Find the nearest common responder in the responder chain for the new
    // responder.  If there are no common responders, use last responder.
    common = responder ? this.nextResponderFor(responder) : null;
    while (common) {
      if (common.get('hasFirstResponder')) break;
      common = (common===last) ? null : this.nextResponderFor(common);
    }
    if (!common) common = last;

    // Cleanup old first responder
    this._notifyWillLoseFirstResponder(current, current, common);
    if (current) current.set('isFirstResponder', NO);

    // Set new first responder.  If new firstResponder does not have its
    // responderContext property set, then set it.

    // but, don't tell anyone until we have _also_ updated the hasFirstResponder state.
    this.beginPropertyChanges();

    this.set('firstResponder', responder) ;
    if (responder) responder.set('isFirstResponder', YES);

    this._notifyDidBecomeFirstResponder(responder, responder, common);

    // now, tell everyone the good news!
    this.endPropertyChanges();

    this._locked = NO ;
    if (this._pendingResponder) {
      this.makeFirstResponder(this._pendingResponder);
      this._pendingResponder = null;
    }

    responder.set("becomingFirstResponder", NO);

    return this ;
  },

  _notifyWillLoseFirstResponder: function(responder, cur, root) {
    if (cur === root) return ; // nothing to do

    cur.willLoseFirstResponder(responder);
    cur.set('hasFirstResponder', NO);

    var next = this.nextResponderFor(cur);
    if (next) this._notifyWillLoseFirstResponder(responder, next, root);
  },

  _notifyDidBecomeFirstResponder: function(responder, cur, root) {
    if (cur === root) return ; // nothing to do

    var next = this.nextResponderFor(cur);
    if (next) this._notifyDidBecomeFirstResponder(responder, next, root);

    cur.set('hasFirstResponder', YES);
    cur.didBecomeFirstResponder(responder);
  },

  /**
    Send the passed action down the responder chain, starting with the
    current first responder.  This will look for the first responder that
    actually implements the action method and returns YES or no value when
    called.

    @param {String} action name of action
    @param {Object} sender object sending the action
    @param {Object} context optional additonal context info
    @returns {SC.Responder} the responder that handled it or null
  */
  sendAction: function(action, sender, context) {
    var working = this.get('firstResponder'),
        last    = this.get('nextResponder'),
        trace   = this.get('trace'),
        handled = NO,
        responder;

    this._locked = YES;
    if (trace) {
      console.log("%@: begin action '%@' (%@, %@)".fmt(this, action, sender, context));
    }

    while (!handled && working) {
      if (working.tryToPerform) {
        handled = working.tryToPerform(action, sender, context);
      }

      if (!handled) {
        working = (working===last) ? null : this.nextResponderFor(working);
      }
    }

    if (trace) {
      if (!handled) console.log("%@:  action '%@' NOT HANDLED".fmt(this,action));
      else console.log("%@: action '%@' handled by %@".fmt(this, action, this.responderNameFor(working)));
    }

    this._locked = NO ;

    if (responder = this._pendingResponder) {
      this._pendingResponder= null ;
      this.makeFirstResponder(responder);
    }

    return working ;
  }

});
<|endoftext|>