<commit_before>define(['chai', 'UserMedia','NavigatorWrapper','VideoWrapper', 'ConnectionWrapper'], function(chai, UserMedia, NavigatorWrapper, VideoWrapper, ConnectionWrapper) {

        suite('UserMedia.hasGetUserMedia', function() {
            var sut,  instanceNavigator, instanceVideo;
            setup(function() {
                instanceNavigator = new NavigatorWrapper();
                instanceVideo = new VideoWrapper();
                sut = new UserMedia(instanceVideo, instanceNavigator, {});
            });

            teardown(function() {
                sut = null;
                instanceNavigator = null;
                instanceVideo = null;
            });

            test('test_hasGetUserMedia_whenCalled_CallsNavigatorWrapper', function() {
                var mockNavigator = sinon.mock(instanceNavigator);
                mockNavigator.expects('getUserMedia').once().withArgs();
                sut.hasGetUserMedia();
            });

            test('test_hasGetUserMedia_whenNavigatorThrowsException_returnFalse', function() {
                var getMediaBackup = window.navigator.getMedia;
                window.navigator.getMedia = 1;

                var stubNavigator = sinon.stub(instanceNavigator, 'getUserMedia');
                stubNavigator.throws(new Error());

                var actual = sut.hasGetUserMedia();
                chai.assert.equal(false, actual);

                stubNavigator.restore();
                window.navigator.getMedia = getMediaBackup;
            });

            test('test_hasGetUserMedia_whenNavigatorReturnsTrue_returnTrue', function() {
                var stubNavigator = sinon.stub(instanceNavigator, 'hasGetUserMedia');
                stubNavigator.returns(true);

                var actual = sut.hasGetUserMedia();
                chai.assert.equal(true, actual);

                stubNavigator.restore();
            });

        });

        suite('UserMedia.stopUserMedia', function() {
            "use strict";
            var sut,  instanceNavigator, instanceVideo;
            setup(function() {
                instanceNavigator = new NavigatorWrapper();
                instanceVideo = new VideoWrapper();
                sut = new UserMedia(instanceVideo, instanceNavigator, {});
            });

            teardown(function() {
                sut = null;
                instanceNavigator = null;
                instanceVideo = null;
            });
            test('test_stopUserMedia_whenCalled_callsStopPlaying', function() {
                var mockVideo = sinon.mock(instanceVideo);
                mockVideo.expects('stopPlaying').once();

                sut.stopMedia();

                mockVideo.restore();
            });
        });

        suite('UserMedia.localQueryCamera', function() {
            "use strict";
            var sut,  instanceNavigator, instanceVideo;
            setup(function() {
                instanceNavigator = new NavigatorWrapper();
                instanceVideo = new VideoWrapper();
                sut = new UserMedia(instanceVideo, instanceNavigator, {});
            });

            teardown(function() {
                sut = null;
                instanceNavigator = null;
                instanceVideo = null;
            });
            test('test_queryCamera_whenCalled_callsGetUserMedia', function() {
                var mockNavigator = sinon.mock(instanceNavigator);
                mockNavigator.expects('getUserMedia').once();

                sut.localQueryCamera();
                mockNavigator.restore();
            });
        });
});<commit_msg>fix tests user media<commit_after>define(['chai', 'UserMedia','NavigatorWrapper','VideoWrapper', 'ConnectionWrapper'], function(chai, UserMedia, NavigatorWrapper, VideoWrapper, ConnectionWrapper) {

        suite('UserMedia.hasGetUserMedia', function() {
            var sut,  instanceNavigator, instanceVideo;
            setup(function() {
                instanceNavigator = new NavigatorWrapper();
                instanceVideo = new VideoWrapper();
                sut = new UserMedia(null, instanceVideo, instanceNavigator, {});
            });

            teardown(function() {
                sut = null;
                instanceNavigator = null;
                instanceVideo = null;
            });

            test('test_hasGetUserMedia_whenCalled_CallsNavigatorWrapper', function() {
                var mockNavigator = sinon.mock(instanceNavigator);
                mockNavigator.expects('getUserMedia').once().withArgs();
                sut.hasGetUserMedia();
            });

            test('test_hasGetUserMedia_whenNavigatorThrowsException_returnFalse', function() {
                var getMediaBackup = window.navigator.getMedia;
                window.navigator.getMedia = 1;

                var stubNavigator = sinon.stub(instanceNavigator, 'getUserMedia');
                stubNavigator.throws(new Error());

                var actual = sut.hasGetUserMedia();
                chai.assert.equal(false, actual);

                stubNavigator.restore();
                window.navigator.getMedia = getMediaBackup;
            });

            test('test_hasGetUserMedia_whenNavigatorReturnsTrue_returnTrue', function() {
                var stubNavigator = sinon.stub(instanceNavigator, 'hasGetUserMedia');
                stubNavigator.returns(true);

                var actual = sut.hasGetUserMedia();
                chai.assert.equal(true, actual);

                stubNavigator.restore();
            });

        });

        suite('UserMedia.stopUserMedia', function() {
            "use strict";
            var sut,  instanceNavigator, instanceVideo;
            setup(function() {
                instanceNavigator = new NavigatorWrapper();
                instanceVideo = new VideoWrapper();
                sut = new UserMedia(null, instanceVideo, instanceNavigator, {});
            });

            teardown(function() {
                sut = null;
                instanceNavigator = null;
                instanceVideo = null;
            });
            test('test_stopUserMedia_whenCalled_callsStopPlaying', function() {
                var mockVideo = sinon.mock(instanceVideo);
                mockVideo.expects('stopPlaying').once();

                sut.stopMedia();

                mockVideo.restore();
            });
        });

        suite('UserMedia.localQueryCamera', function() {
            "use strict";
            var sut,  instanceNavigator, instanceVideo;
            setup(function() {
                instanceNavigator = new NavigatorWrapper();
                instanceVideo = new VideoWrapper();
                sut = new UserMedia(null, instanceVideo, instanceNavigator, {});
            });

            teardown(function() {
                sut = null;
                instanceNavigator = null;
                instanceVideo = null;
            });
            test('test_queryCamera_whenCalled_callsGetUserMedia', function() {
                var mockNavigator = sinon.mock(instanceNavigator);
                mockNavigator.expects('getUserMedia').once();

                sut.localQueryCamera();
                mockNavigator.restore();
            });
        });
});<|endoftext|>


<commit_before>/**
 * FiltrES provides filtres.compile() to compile user expressions to an ElasticSearch query.
 *
 * See https://github.com/abeisgreat/filtres for tutorial, reference and examples.
 * MIT License - based on Filtrex by Joe Walnes (https://github.com/joewalnes/filtrex)
 *
 * Includes Jison by Zachary Carter. See http://jison.org/
 *
 * -Abe Haskins (originally Joe Walnes)
 */

//var queryBase = '{"query" : {"filtered" : { "filter": [';
//var queryEnd = ']}}}';
var queryCleaner = /\$|\.|\(|\)|\=|\~|\[|\]|\!|\<|\>|\'|\"|\+/g;
var debug = false;

function compileExpression(expression, failToQuerystring) {
  if (!compileExpression.parser) {
    // Building the original parser is the heaviest part. Do it
    // once and cache the result in our own function.
    compileExpression.parser = filtrexParser();
  }

  var tree, queryIsValid = true;

  try {
    tree = compileExpression.parser.parse(expression);

    var js = [];
    //js.push(queryBase);
    function toJs(node) {
      if (Array.isArray(node)) {
        node.forEach(toJs);
      } else {
        js.push(node);
      }
    }
    tree.forEach(toJs);
    //js.push(queryEnd);
  } catch (err) {
    if (debug) console.log(err);
    queryIsValid = false;
  }

  if (debug) console.log(expression, tree, js);

  try {
    return JSON.parse(js.join(''));
  } catch (err) {
    if (debug) console.log(js, err);
    queryIsValid = false;
  }

  if (failToQuerystring) {
    return {
      "query" : {
        "query_string" : {
          "query": expression.replace(queryCleaner, '')
        }
      }
    };
  } else {
    if (debug) console.log("ERROR", js && js.length? js.join('') : 'Parse failed');
    throw "Invalid Query";
  }
}

function filtrexParser() {

  // Language parser powered by Jison <http://zaach.github.com/jison/>,
  // which is a pure JavaScript implementation of
  // Bison <http://www.gnu.org/software/bison/>.

  var Jison = require('jison')

  function code(args, skipParentheses) {
    skipParentheses = true;
    var argsJs = args.map(function(a) {
      return typeof(a) == 'number' ? ('$' + a) : JSON.stringify(a);
    }).join(',');

    return skipParentheses
        ? '$$ = [' + argsJs + '];'
        : '$$ = ["(", ' + argsJs + ', ")"];';
  }

  var grammar = {
    // Lexical tokens
    lex: {
      rules: [
        /*['\\*', 'return "*";'],
        ['\\/', 'return "/";'],
        ['-'  , 'return "-";'],
        ['\\+', 'return "+";'],
        ['\\^', 'return "^";'],
        ['\\%', 'return "%";'],*/
        ['\\(', 'return "(";'],
        ['\\)', 'return ")";'],
        ['\\,', 'return ",";'],
        ['==', 'return "==";'],
        ['\\!=', 'return "!=";'],
        ['>=', 'return ">=";'],
        ['<=', 'return "<=";'],
        ['<', 'return "<";'],
        ['>', 'return ">";'],
        //['~=', 'return "~=";'],
        //['~!=', 'return "~!=";'],
        //['\\?', 'return "?";'],
        //['\\:', 'return ":";'],
        ['and[^\\w]', 'return "and";'],
        ['or[^\\w]' , 'return "or";'],
        ['not[^\\w]', 'return "not";'],
        ['in[^\\w]', 'return "in";'],

        ['\\s+',  ''], // skip whitespace
        ['[0-9]+(?:\\.[0-9]+)?\\b', 'return "NUMBER";'], // 212.321
        ['[a-zA-Z][\\.a-zA-Z0-9_]*', 'return "SYMBOL";'], // some.Symbol22
        ['"(?:[^"])*"', 'yytext = yytext.substr(1, yyleng-2); return "STRING";'], // "foo"

        // End
        ['$', 'return "EOF";'],
      ]
    },
    // Operator precedence - lowest precedence first.
    // See http://www.gnu.org/software/bison/manual/html_node/Precedence.html
    // for a good explanation of how it works in Bison (and hence, Jison).
    // Different languages have different rules, but this seems a good starting
    // point: http://en.wikipedia.org/wiki/Order_of_operations#Programming_languages
    operators: [
      //['left', '?', ':'],
      ['left', 'or'],
      ['left', 'and'],
      ['left', 'in'],
      ['left', '==', '!='],
      ['left', '<', '<=', '>', '>='],
      //['left', '~=', '~!='],
      //['left', '+', '-'],
      //['left', '*', '/', '%'],
      //['left', '^'],
      ['left', 'not'],
      ['left', 'UMINUS'],
    ],
    // Grammar
    bnf: {
      expressions: [ // Entry point
        ['e EOF', 'return $1;']
      ],
      e: [
        // Math operations (maybe client-side?)
        /*
        ['e + e'  , code([1, '+', 3])],
        ['e - e'  , code([1, '-', 3])],
        ['e * e'  , code([1, '*', 3])],
        ['e / e'  , code([1, '/', 3])],
        ['e % e'  , code([1, '%', 3])],
        ['e ^ e'  , code(['Math.pow(', 1, ',', 3, ')'])],
        */

        // Comparisons
        ['- e'    , code(['-', 2]), {prec: 'UMINUS'}],                        // done
        ['e and e', code(['{"bool": { "must": [', 1, ', ', 3, ']}}'])],               // done
        ['e or e' , code(['{"bool": { "should": [', 1, ', ', 3, ']}}'])],             // done
        ['not e'  , code(['{"bool": { "must_not": [', 2, ']}}'])],                  // done
        ['e == e' , code(['{"', 1, '": {"$eq":', 3, '}}'])],                    // done
        ['e != e' , code(['{"', 1, '": {"$ne":', 3, '}}'])],      // done

        //['e ~= STRING' , code(['{"bool": { "must": { "regexp": {"', 1, '": "', 3, '"}}}}'])],   // done
        //['e ~!= STRING' , code(['{"bool": { "must_not": { "regexp": {"', 1, '": "', 3, '"}}}}'])],  // done

        ['e < e'  , code(['{"range": {"', 1, '": {"lt": ' , 3, '}}}'])],              // done
        ['e <= e' , code(['{"range": {"', 1, '": {"lte": ' , 3, '}}}'])],             // done
        ['e > e'  , code(['{"range": {"', 1, '": {"gt": ' , 3, '}}}'])],              // done
        ['e >= e' , code(['{"range": {"', 1, '": {"gte": ' , 3, '}}}'])],             // done
        //['e ? e : e', code([1, '?', 3, ':', 5])],
        ['( e )'  , code([2])],                                   // done

        // Literals
        ['NUMBER' , code([1])],                                   // done
        ['STRING' , code(['"', 1, '"'])],                             // done
        ['SYMBOL' , code([1])],                                   // done

        // Functions
        //['SYMBOL ( argsList )', code(['functions.', 1, '(', 3, ')'])],
        //['e in ( inSet )', code([1, ' in (function(o) { ', 4, 'return o; })({})'])],
        //['e not in ( inSet )', code(['!(', 1, ' in (function(o) { ', 5, 'return o; })({}))'])],
      ],/*
      argsList: [
        ['e', code([1], true)],
        ['argsList , e', code([1, ',', 3], true)],
      ],
      inSet: [
        ['e', code(['o[', 1, '] = true; '], true)],
        ['inSet , e', code([1, 'o[', 3, '] = true; '], true)],
      ]*/
    }
  };
  return new Jison.Parser(grammar);
}

var filtres = {
  compile: compileExpression
};

module.exports = filtres;
<commit_msg>hacky hacky  and  support<commit_after>/**
 * FiltrES provides filtres.compile() to compile user expressions to an ElasticSearch query.
 *
 * See https://github.com/abeisgreat/filtres for tutorial, reference and examples.
 * MIT License - based on Filtrex by Joe Walnes (https://github.com/joewalnes/filtrex)
 *
 * Includes Jison by Zachary Carter. See http://jison.org/
 *
 * -Abe Haskins (originally Joe Walnes)
 */

//var queryBase = '{"query" : {"filtered" : { "filter": [';
//var queryEnd = ']}}}';
var queryCleaner = /\$|\.|\(|\)|\=|\~|\[|\]|\!|\<|\>|\'|\"|\+/g;
var debug = false;

function compileExpression(expression, failToQuerystring) {
  if (!compileExpression.parser) {
    // Building the original parser is the heaviest part. Do it
    // once and cache the result in our own function.
    compileExpression.parser = filtrexParser();
  }

  var tree, queryIsValid = true;

  try {
    tree = compileExpression.parser.parse(expression);

    var js = [];
    //js.push(queryBase);
    function toJs(node) {
      if (Array.isArray(node)) {
        node.forEach(toJs);
      } else {
        // For whatever reason $and and $or get parsed by json into $$[$0-1]
        // so I'm hackily ouputing $jand and $jor and replacing them before
        // parsing to JSON
        node = node.replace('$jand', '$and');
        node = node.replace('$jor', '$or');
        js.push(node);
      }
    }
    tree.forEach(toJs);
    //js.push(queryEnd);
  } catch (err) {
    if (debug) console.log(err);
    queryIsValid = false;
  }

  if (debug) console.log(expression, tree, js);

  try {
    return JSON.parse(js.join(''));
  } catch (err) {
    if (debug) console.log(js, err);
    queryIsValid = false;
  }

  if (failToQuerystring) {
    return {
      "query" : {
        "query_string" : {
          "query": expression.replace(queryCleaner, '')
        }
      }
    };
  } else {
    if (debug) console.log("ERROR", js && js.length? js.join('') : 'Parse failed');
    throw "Invalid Query";
  }
}

function filtrexParser() {

  // Language parser powered by Jison <http://zaach.github.com/jison/>,
  // which is a pure JavaScript implementation of
  // Bison <http://www.gnu.org/software/bison/>.

  var Jison = require('jison')

  function code(args, skipParentheses) {
    skipParentheses = true;
    var argsJs = args.map(function(a) {
      return typeof(a) == 'number' ? ('$' + a) : JSON.stringify(a);
    }).join(',');

    return skipParentheses
        ? '$$ = [' + argsJs + '];'
        : '$$ = ["(", ' + argsJs + ', ")"];';
  }

  var grammar = {
    // Lexical tokens
    lex: {
      rules: [
        /*['\\*', 'return "*";'],
        ['\\/', 'return "/";'],
        ['-'  , 'return "-";'],
        ['\\+', 'return "+";'],
        ['\\^', 'return "^";'],
        ['\\%', 'return "%";'],*/
        ['\\(', 'return "(";'],
        ['\\)', 'return ")";'],
        ['\\,', 'return ",";'],
        ['==', 'return "==";'],
        ['\\!=', 'return "!=";'],
        ['>=', 'return ">=";'],
        ['<=', 'return "<=";'],
        ['<', 'return "<";'],
        ['>', 'return ">";'],
        //['~=', 'return "~=";'],
        //['~!=', 'return "~!=";'],
        //['\\?', 'return "?";'],
        //['\\:', 'return ":";'],
        ['and[^\\w]', 'return "and";'],
        ['or[^\\w]' , 'return "or";'],
        ['not[^\\w]', 'return "not";'],
        ['in[^\\w]', 'return "in";'],

        ['\\s+',  ''], // skip whitespace
        ['[0-9]+(?:\\.[0-9]+)?\\b', 'return "NUMBER";'], // 212.321
        ['[a-zA-Z][\\.a-zA-Z0-9_]*', 'return "SYMBOL";'], // some.Symbol22
        ['"(?:[^"])*"', 'yytext = yytext.substr(1, yyleng-2); return "STRING";'], // "foo"

        // End
        ['$', 'return "EOF";'],
      ]
    },
    // Operator precedence - lowest precedence first.
    // See http://www.gnu.org/software/bison/manual/html_node/Precedence.html
    // for a good explanation of how it works in Bison (and hence, Jison).
    // Different languages have different rules, but this seems a good starting
    // point: http://en.wikipedia.org/wiki/Order_of_operations#Programming_languages
    operators: [
      //['left', '?', ':'],
      ['left', 'or'],
      ['left', 'and'],
      ['left', 'in'],
      ['left', '==', '!='],
      ['left', '<', '<=', '>', '>='],
      //['left', '~=', '~!='],
      //['left', '+', '-'],
      //['left', '*', '/', '%'],
      //['left', '^'],
      ['left', 'not'],
      ['left', 'UMINUS'],
    ],
    // Grammar
    bnf: {
      expressions: [ // Entry point
        ['e EOF', 'return $1;']
      ],
      e: [
        // Math operations (maybe client-side?)
        /*
        ['e + e'  , code([1, '+', 3])],
        ['e - e'  , code([1, '-', 3])],
        ['e * e'  , code([1, '*', 3])],
        ['e / e'  , code([1, '/', 3])],
        ['e % e'  , code([1, '%', 3])],
        ['e ^ e'  , code(['Math.pow(', 1, ',', 3, ')'])],
        */

        // Comparisons
        ['- e'    , code(['-', 2]), {prec: 'UMINUS'}],                        // done
        // hacky $jand and $jor since $and and $or seem to mean something
        // in jison. I don't know what I'm doing
        // TODO: fix this hacky mess
        ['e and e', code(['{"$jand": [', 1, ', ', 3, ']}'])],               // done
        ['e or e' , code(['{"$jor": [', 1, ', ', 3, ']}'])],             // done
        ['not e'  , code(['{"bool": { "must_not": [', 2, ']}}'])],                  // done
        ['e == e' , code(['{"', 1, '": {"$eq":', 3, '}}'])],                    // done
        ['e != e' , code(['{"', 1, '": {"$ne":', 3, '}}'])],      // done

        //['e ~= STRING' , code(['{"bool": { "must": { "regexp": {"', 1, '": "', 3, '"}}}}'])],   // done
        //['e ~!= STRING' , code(['{"bool": { "must_not": { "regexp": {"', 1, '": "', 3, '"}}}}'])],  // done

        ['e < e'  , code(['{"range": {"', 1, '": {"lt": ' , 3, '}}}'])],              // done
        ['e <= e' , code(['{"range": {"', 1, '": {"lte": ' , 3, '}}}'])],             // done
        ['e > e'  , code(['{"range": {"', 1, '": {"gt": ' , 3, '}}}'])],              // done
        ['e >= e' , code(['{"range": {"', 1, '": {"gte": ' , 3, '}}}'])],             // done
        //['e ? e : e', code([1, '?', 3, ':', 5])],
        ['( e )'  , code([2])],                                   // done

        // Literals
        ['NUMBER' , code([1])],                                   // done
        ['STRING' , code(['"', 1, '"'])],                             // done
        ['SYMBOL' , code([1])],                                   // done

        // Functions
        //['SYMBOL ( argsList )', code(['functions.', 1, '(', 3, ')'])],
        //['e in ( inSet )', code([1, ' in (function(o) { ', 4, 'return o; })({})'])],
        //['e not in ( inSet )', code(['!(', 1, ' in (function(o) { ', 5, 'return o; })({}))'])],
      ],/*
      argsList: [
        ['e', code([1], true)],
        ['argsList , e', code([1, ',', 3], true)],
      ],
      inSet: [
        ['e', code(['o[', 1, '] = true; '], true)],
        ['inSet , e', code([1, 'o[', 3, '] = true; '], true)],
      ]*/
    }
  };
  return new Jison.Parser(grammar);
}

var filtres = {
  compile: compileExpression
};

module.exports = filtres;
<|endoftext|>


<commit_before>// ==========================================================================
// Plyr UI
// ==========================================================================

import captions from './captions';
import controls from './controls';
import support from './support';
import browser from './utils/browser';
import { getElement, toggleClass } from './utils/elements';
import { ready, triggerEvent } from './utils/events';
import i18n from './utils/i18n';
import is from './utils/is';
import loadImage from './utils/load-image';

const ui = {
    addStyleHook() {
        toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
        toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);
    },

    // Toggle native HTML5 media controls
    toggleNativeControls(toggle = false) {
        if (toggle && this.isHTML5) {
            this.media.setAttribute('controls', '');
        } else {
            this.media.removeAttribute('controls');
        }
    },

    // Setup the UI
    build() {
        // Re-attach media element listeners
        // TODO: Use event bubbling?
        this.listeners.media();

        // Don't setup interface if no support
        if (!this.supported.ui) {
            this.debug.warn(`Basic support only for ${this.provider} ${this.type}`);

            // Restore native controls
            ui.toggleNativeControls.call(this, true);

            // Bail
            return;
        }

        // Inject custom controls if not present
        if (!is.element(this.elements.controls)) {
            // Inject custom controls
            controls.inject.call(this);

            // Re-attach control listeners
            this.listeners.controls();
        }

        // Remove native controls
        ui.toggleNativeControls.call(this);

        // Setup captions for HTML5
        if (this.isHTML5) {
            captions.setup.call(this);
        }

        // Reset volume
        this.volume = null;

        // Reset mute state
        this.muted = null;

        // Reset loop state
        this.loop = null;

        // Reset quality setting
        this.quality = null;

        // Reset speed
        this.speed = null;

        // Reset volume display
        controls.updateVolume.call(this);

        // Reset time display
        controls.timeUpdate.call(this);

        // Update the UI
        ui.checkPlaying.call(this);

        // Check for picture-in-picture support
        toggleClass(
            this.elements.container,
            this.config.classNames.pip.supported,
            support.pip && this.isHTML5 && this.isVideo,
        );

        // Check for airplay support
        toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);

        // Add iOS class
        toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);

        // Add touch class
        toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);

        // Ready for API calls
        this.ready = true;

        // Ready event at end of execution stack
        setTimeout(() => {
            triggerEvent.call(this, this.media, 'ready');
        }, 0);

        // Set the title
        ui.setTitle.call(this);

        // Assure the poster image is set, if the property was added before the element was created
        if (this.poster) {
            ui.setPoster.call(this, this.poster, false).catch(() => {});
        }

        // Manually set the duration if user has overridden it.
        // The event listeners for it doesn't get called if preload is disabled (#701)
        if (this.config.duration) {
            controls.durationUpdate.call(this);
        }
    },

    // Setup aria attribute for play and iframe title
    setTitle() {
        // Find the current text
        let label = i18n.get('play', this.config);

        // If there's a media title set, use that for the label
        if (is.string(this.config.title) && !is.empty(this.config.title)) {
            label += `, ${this.config.title}`;
        }

        // If there's a play button, set label
        Array.from(this.elements.buttons.play || []).forEach(button => {
            button.setAttribute('aria-label', label);
        });

        // Set iframe title
        // https://github.com/sampotts/plyr/issues/124
        if (this.isEmbed) {
            const iframe = getElement.call(this, 'iframe');

            if (!is.element(iframe)) {
                return;
            }

            // Default to media type
            const title = !is.empty(this.config.title) ? this.config.title : 'video';
            const format = i18n.get('frameTitle', this.config);

            iframe.setAttribute('title', format.replace('{title}', title));
        }
    },

    // Toggle poster
    togglePoster(enable) {
        toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable);
    },

    // Set the poster image (async)
    // Used internally for the poster setter, with the passive option forced to false
    setPoster(poster, passive = true) {
        // Don't override if call is passive
        if (passive && this.poster) {
            return Promise.reject(new Error('Poster already set'));
        }

        // Set property synchronously to respect the call order
        this.media.setAttribute('poster', poster);

        // Wait until ui is ready
        return (
            ready
                .call(this)
                // Load image
                .then(() => loadImage(poster))
                .catch(err => {
                    // Hide poster on error unless it's been set by another call
                    if (poster === this.poster) {
                        ui.togglePoster.call(this, false);
                    }
                    // Rethrow
                    throw err;
                })
                .then(() => {
                    // Prevent race conditions
                    if (poster !== this.poster) {
                        throw new Error('setPoster cancelled by later call to setPoster');
                    }
                })
                .then(() => {
                    Object.assign(this.elements.poster.style, {
                        backgroundImage: `url('${poster}')`,
                        // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube)
                        backgroundSize: '',
                    });

                    ui.togglePoster.call(this, true);

                    return poster;
                })
        );
    },

    // Check playing state
    checkPlaying(event) {
        // Class hooks
        toggleClass(this.elements.container, this.config.classNames.playing, this.playing);
        toggleClass(this.elements.container, this.config.classNames.paused, this.paused);
        toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped);

        // Set state
        Array.from(this.elements.buttons.play || []).forEach(target => {
            Object.assign(target, { pressed: this.playing });
            target.setAttribute('aria-label', i18n.get(this.playing ? 'pause' : 'play', this.config));
        });

        // Only update controls on non timeupdate events
        if (is.event(event) && event.type === 'timeupdate') {
            return;
        }

        // Toggle controls
        ui.toggleControls.call(this);
    },

    // Check if media is loading
    checkLoading(event) {
        this.loading = ['stalled', 'waiting'].includes(event.type);

        // Clear timer
        clearTimeout(this.timers.loading);

        // Timer to prevent flicker when seeking
        this.timers.loading = setTimeout(
            () => {
                // Update progress bar loading class state
                toggleClass(this.elements.container, this.config.classNames.loading, this.loading);

                // Update controls visibility
                ui.toggleControls.call(this);
            },
            this.loading ? 250 : 0,
        );
    },

    // Toggle controls based on state and `force` argument
    toggleControls(force) {
        const { controls: controlsElement } = this.elements;

        if (controlsElement && this.config.hideControls) {
            // Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
            const recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now();

            // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
            this.toggleControls(
                Boolean(
                    force ||
                        this.loading ||
                        this.paused ||
                        controlsElement.pressed ||
                        controlsElement.hover ||
                        recentTouchSeek,
                ),
            );
        }
    },
};

export default ui;
<commit_msg>Ensure poster image is not downloaded again for HTML5 videos<commit_after>// ==========================================================================
// Plyr UI
// ==========================================================================

import captions from './captions';
import controls from './controls';
import support from './support';
import browser from './utils/browser';
import { getElement, toggleClass } from './utils/elements';
import { ready, triggerEvent } from './utils/events';
import i18n from './utils/i18n';
import is from './utils/is';
import loadImage from './utils/load-image';

const ui = {
    addStyleHook() {
        toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
        toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);
    },

    // Toggle native HTML5 media controls
    toggleNativeControls(toggle = false) {
        if (toggle && this.isHTML5) {
            this.media.setAttribute('controls', '');
        } else {
            this.media.removeAttribute('controls');
        }
    },

    // Setup the UI
    build() {
        // Re-attach media element listeners
        // TODO: Use event bubbling?
        this.listeners.media();

        // Don't setup interface if no support
        if (!this.supported.ui) {
            this.debug.warn(`Basic support only for ${this.provider} ${this.type}`);

            // Restore native controls
            ui.toggleNativeControls.call(this, true);

            // Bail
            return;
        }

        // Inject custom controls if not present
        if (!is.element(this.elements.controls)) {
            // Inject custom controls
            controls.inject.call(this);

            // Re-attach control listeners
            this.listeners.controls();
        }

        // Remove native controls
        ui.toggleNativeControls.call(this);

        // Setup captions for HTML5
        if (this.isHTML5) {
            captions.setup.call(this);
        }

        // Reset volume
        this.volume = null;

        // Reset mute state
        this.muted = null;

        // Reset loop state
        this.loop = null;

        // Reset quality setting
        this.quality = null;

        // Reset speed
        this.speed = null;

        // Reset volume display
        controls.updateVolume.call(this);

        // Reset time display
        controls.timeUpdate.call(this);

        // Update the UI
        ui.checkPlaying.call(this);

        // Check for picture-in-picture support
        toggleClass(
            this.elements.container,
            this.config.classNames.pip.supported,
            support.pip && this.isHTML5 && this.isVideo,
        );

        // Check for airplay support
        toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);

        // Add iOS class
        toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);

        // Add touch class
        toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);

        // Ready for API calls
        this.ready = true;

        // Ready event at end of execution stack
        setTimeout(() => {
            triggerEvent.call(this, this.media, 'ready');
        }, 0);

        // Set the title
        ui.setTitle.call(this);

        // Assure the poster image is set, if the property was added before the element was created
        if (this.poster) {
            ui.setPoster.call(this, this.poster, false).catch(() => {});
        }

        // Manually set the duration if user has overridden it.
        // The event listeners for it doesn't get called if preload is disabled (#701)
        if (this.config.duration) {
            controls.durationUpdate.call(this);
        }
    },

    // Setup aria attribute for play and iframe title
    setTitle() {
        // Find the current text
        let label = i18n.get('play', this.config);

        // If there's a media title set, use that for the label
        if (is.string(this.config.title) && !is.empty(this.config.title)) {
            label += `, ${this.config.title}`;
        }

        // If there's a play button, set label
        Array.from(this.elements.buttons.play || []).forEach(button => {
            button.setAttribute('aria-label', label);
        });

        // Set iframe title
        // https://github.com/sampotts/plyr/issues/124
        if (this.isEmbed) {
            const iframe = getElement.call(this, 'iframe');

            if (!is.element(iframe)) {
                return;
            }

            // Default to media type
            const title = !is.empty(this.config.title) ? this.config.title : 'video';
            const format = i18n.get('frameTitle', this.config);

            iframe.setAttribute('title', format.replace('{title}', title));
        }
    },

    // Toggle poster
    togglePoster(enable) {
        toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable);
    },

    // Set the poster image (async)
    // Used internally for the poster setter, with the passive option forced to false
    setPoster(poster, passive = true) {
        // Don't override if call is passive
        if (passive && this.poster) {
            return Promise.reject(new Error('Poster already set'));
        }

        // Set property synchronously to respect the call order
        this.media.setAttribute('poster', poster);

        // HTML5 uses native poster attribute
        if (this.isHTML5) {
            return Promise.resolve(poster);
        }

        // Wait until ui is ready
        return (
            ready
                .call(this)
                // Load image
                .then(() => loadImage(poster))
                .catch(err => {
                    // Hide poster on error unless it's been set by another call
                    if (poster === this.poster) {
                        ui.togglePoster.call(this, false);
                    }
                    // Rethrow
                    throw err;
                })
                .then(() => {
                    // Prevent race conditions
                    if (poster !== this.poster) {
                        throw new Error('setPoster cancelled by later call to setPoster');
                    }
                })
                .then(() => {
                    Object.assign(this.elements.poster.style, {
                        backgroundImage: `url('${poster}')`,
                        // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube)
                        backgroundSize: '',
                    });

                    ui.togglePoster.call(this, true);

                    return poster;
                })
        );
    },

    // Check playing state
    checkPlaying(event) {
        // Class hooks
        toggleClass(this.elements.container, this.config.classNames.playing, this.playing);
        toggleClass(this.elements.container, this.config.classNames.paused, this.paused);
        toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped);

        // Set state
        Array.from(this.elements.buttons.play || []).forEach(target => {
            Object.assign(target, { pressed: this.playing });
            target.setAttribute('aria-label', i18n.get(this.playing ? 'pause' : 'play', this.config));
        });

        // Only update controls on non timeupdate events
        if (is.event(event) && event.type === 'timeupdate') {
            return;
        }

        // Toggle controls
        ui.toggleControls.call(this);
    },

    // Check if media is loading
    checkLoading(event) {
        this.loading = ['stalled', 'waiting'].includes(event.type);

        // Clear timer
        clearTimeout(this.timers.loading);

        // Timer to prevent flicker when seeking
        this.timers.loading = setTimeout(
            () => {
                // Update progress bar loading class state
                toggleClass(this.elements.container, this.config.classNames.loading, this.loading);

                // Update controls visibility
                ui.toggleControls.call(this);
            },
            this.loading ? 250 : 0,
        );
    },

    // Toggle controls based on state and `force` argument
    toggleControls(force) {
        const { controls: controlsElement } = this.elements;

        if (controlsElement && this.config.hideControls) {
            // Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
            const recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now();

            // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
            this.toggleControls(
                Boolean(
                    force ||
                        this.loading ||
                        this.paused ||
                        controlsElement.pressed ||
                        controlsElement.hover ||
                        recentTouchSeek,
                ),
            );
        }
    },
};

export default ui;
<|endoftext|>


<commit_before>/*!
 * jskit.js v0.1
 *
 * Copyright 2013, arthur87
 * https://github.com/arthur87/JSKit
 *
 * http://www.opensource.org/licenses/mit-license.php
 */

var JSKit = (function() {
    function JSKit() {

    }

    /**
     * 連想配列をJSON形式に変換します。
     * @param values 連想配列
     * @return JSON形式
     */
    JSKit.json_encode = function(values) {
        var flag = true;
        var str = "{";
        for(var i in values) {
            if(!flag) str += ',';
            str += '"' + i.replace('"', '\\"', 'g') + '":';
            if(isNaN(values[i])) { // 文字列の場合
                str += '"' + values[i].replace('"', '\\"', 'g') + '"';
            }else if(values[i] == "") { // 空のとき
                str += '""';
            }else { // 数字のとき
                str += values[i];
            }
            flag = false;
        }
        str += '}';
        return str;
    };

    /**
     * 指定したGETパラメータを取得します。
     * @params key 取得したいGETパラメータ
     * @params value GETパラメータが存在しないときに返す値
     * @return
     */
    JSKit.getURLParams = function(key, value) {
        var query = window.location.search.substring(1);
        params = query.split('&');
        for(var i = 0; i < params.length; i++) {
            var position = params[i].indexOf('=');
            if(position > 0 && key == params[i].substring(0, position)) {
                return params[i].substring(position+1);
            }
        }
        return value;
    };

    /**
     * タッチイベントをサポートしているとき、trueを返します。
     * @return
     */
    JSKit.isSupportedTouchEvent = function() {
        return ('createTouch' in document) || ('ontouchstart' in document);
    };

    /**
     * iOSがイメージピッカーをサポートしているとき、trueを返します。
     *
     * @return
     */
    JSKit.isSupportedImagePicker = function() {
        var uas = { iphone: 'iphone os ' };
        var ua = navigator.userAgent.toLowerCase();
        if(ua.indexOf(uas.iphone > 0)) {
        	// iOS 6以上のときtrueを返します。
            return (ua.substr(ua.indexOf(uas.iphone) + uas.iphone.length, 1) >= 6);
        }
        return true;
    };

    /**
     * 数値を指定した範囲の中に収めます。
     * xがa以上b以下のときxがそのまま返ります。またxがaより小さいときはb、bより大きいときはbが返ります。
     * @param x 計算対象の値です。
     * @param a 範囲の下限です。
     * @param b 範囲の上限です。
     * @return 計算結果
     */
    JSKit.constrain = function(x, a, b) {
        if(a <= x && x <= b) {
            return x;
        }
        return (a > x) ? a : b;
    };

    /**
     * 数値をある範囲から別の範囲に変換します。
     * @param x 変換したい数値です。
     * @param in_min 現在の範囲の下限です。
     * @param in_max 現在の範囲の上限です。
     * @param out_min 変換後の範囲の下限です。
     * @param out_max 変換後の範囲の上限です。
     * @return 計算結果
     */
    JSKit.map = function(x, in_min, in_max, out_min, out_max) {
        return Math.floor((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min);
    };

    /**
     * 擬似乱数を生成します。
     * @param min 生成する乱数の上限です。
     * @param max 生成する乱数の下限です。
     * @return 計算結果
     */
    JSKit.mt_rand = function(max, min) {
        return Math.floor((max - min + 1) * Math.random() + min);
    };

    /**
     * 数値を指定した小数部の桁数で打ち切ります。
     * @param x 数値
     * @param y 小数部の桁数
     * @return 計算結果
     *
     * <listing version="3.0">
     * JSKit.format(3.14159, 3); // 3.141
     * </listing>
     */
    JSKit.format = function(x, y) {
        return Math.floor(x * Math.pow(10, y)) / Math.pow(10, y);
    };

    /**
     * ランダムな文字列を生成します。
     * typeには、num、alpha、alnumが指定できます。
     * @params length 文字列長
     * @params type 文字列を構成する文字種
     */
    JSKit.random = function(length, type) {
        var alphas = 'abcdefghijklmnopqestuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        var nums = '1234567890';
        var list = '';
        var result = '';

        if(type == 'num') {
            list = nums;
        }else if(type == 'alpha') {
            list = alphas;
        }else if(type == 'alnum') {
            list = alphas+nums;
        }else {
            return '';
        }
        for(var i = 0; i < length; i++) {
            result += list.substr(JSKit.mt_rand(0, list.length), 1);
        }
        return result;
    };

    /**
     * UNIX時間を取得します。
     */
    JSKit.time = function() {
        return new Date/1000|0;
    };

    /**
     * Cookieの値を取得します。
     * http://www9.plala.or.jp/oyoyon/html/script/cookie.html
     *
     * @param name 名前
     * @param value Cookieが存在しないときに返す値
     */
    JSKit.getCookie = function(name, value) {
        if (!name || !document.cookie) {
            return value;
        }
        var cookies = document.cookie.split("; ");
        for (var i = 0; i < cookies.length; i++) {
            var str = cookies[i].split("=");
            if (str[0] == name) {
                return unescape(str[1]);
            }
        }
        return value;
    };

    /**
     * Cookieの値を設定します。
     * http://www9.plala.or.jp/oyoyon/html/script/cookie.html
     *
     * @param name 名前
     * @param value 値
     * @param domain 参照可能なドメインを指定します。0のとき発行したサーバ、1のとき発行したドメインを含むサーバ。
     * @param path 参照可能なパスを指定します。0のとき発行したディレクトリ、1のとき発行したページ。
     * @param expires 有効期限を日数で指定します。0を指定するとセッション中、-1を指定するとCookieを削除。
     * @param secure 1のときSSL通信時のCookieの暗号化を有効にします。
     */
    JSKit.setCookie = function(name, value, domain, path, expires, secure) {
        var str = name + "=" + escape(value);
        if (domain) {
            if (domain == 1) domain = location.hostname.replace(/^[^\.]*/, "");
             str += "; domain=" + domain;
        }
        if (path) {
            if (path == 1) path = location.pathname;
            str += "; path=" + path;
        }
        if (expires) {
            expires = new Date(new Date().getTime() + (60 * 60 * 24 * 1000 * expires));
            expires = expires.toGMTString();
            str += "; expires=" + expires;
        }
        if (secure && location.protocol == "https:") {
            str += "; secure";
        }

        document.cookie = str;
    };

    return JSKit;
}());<commit_msg>メソッド名を変更しました。<commit_after>/*!
 * jskit.js v0.2
 *
 * Copyright 2013, arthur87
 * https://github.com/arthur87/JSKit
 *
 * http://www.opensource.org/licenses/mit-license.php
 */

var JSKit = (function() {
    function JSKit() {

    }

    /**
     * 連想配列をJSON形式に変換します。
     * @param values 連想配列
     * @return JSON形式
     */
    JSKit.jsonEncode = function(values) {
        var flag = true;
        var str = "{";
        for(var i in values) {
            if(!flag) str += ',';
            str += '"' + i.replace('"', '\\"', 'g') + '":';
            if(isNaN(values[i])) { // 文字列の場合
                str += '"' + values[i].replace('"', '\\"', 'g') + '"';
            }else if(values[i] == "") { // 空のとき
                str += '""';
            }else { // 数字のとき
                str += values[i];
            }
            flag = false;
        }
        str += '}';
        return str;
    };

    /**
     * 指定したGETパラメータを取得します。
     * @params key 取得したいGETパラメータ
     * @params value GETパラメータが存在しないときに返す値
     * @return
     */
    JSKit.getURLParam = function(key, value) {
        var query = window.location.search.substring(1);
        params = query.split('&');
        for(var i = 0; i < params.length; i++) {
            var position = params[i].indexOf('=');
            if(position > 0 && key == params[i].substring(0, position)) {
                return params[i].substring(position+1);
            }
        }
        return value;
    };

    /**
     * タッチイベントをサポートしているとき、trueを返します。
     * @return
     */
    JSKit.isSupportedTouchEvent = function() {
        return ('createTouch' in document) || ('ontouchstart' in document);
    };

    /**
     * iOSがイメージピッカーをサポートしているとき、trueを返します。
     *
     * @return
     */
    JSKit.isSupportedImagePicker = function() {
        var uas = { iphone: 'iphone os ' };
        var ua = navigator.userAgent.toLowerCase();
        if(ua.indexOf(uas.iphone > 0)) {
        	// iOS 6以上のときtrueを返します。
            return (ua.substr(ua.indexOf(uas.iphone) + uas.iphone.length, 1) >= 6);
        }
        return true;
    };

    /**
     * 数値を指定した範囲の中に収めます。
     * xがa以上b以下のときxがそのまま返ります。またxがaより小さいときはb、bより大きいときはbが返ります。
     * @param x 計算対象の値です。
     * @param a 範囲の下限です。
     * @param b 範囲の上限です。
     * @return 計算結果
     */
    JSKit.constrain = function(x, a, b) {
        if(a <= x && x <= b) {
            return x;
        }
        return (a > x) ? a : b;
    };

    /**
     * 数値をある範囲から別の範囲に変換します。
     * @param x 変換したい数値です。
     * @param in_min 現在の範囲の下限です。
     * @param in_max 現在の範囲の上限です。
     * @param out_min 変換後の範囲の下限です。
     * @param out_max 変換後の範囲の上限です。
     * @return 計算結果
     */
    JSKit.map = function(x, in_min, in_max, out_min, out_max) {
        return Math.floor((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min);
    };

    /**
     * 擬似乱数を生成します。
     * @param min 生成する乱数の上限です。
     * @param max 生成する乱数の下限です。
     * @return 計算結果
     */
    JSKit.rand = function(max, min) {
        return Math.floor((max - min + 1) * Math.random() + min);
    };

    /**
     * 数値を指定した小数部の桁数で打ち切ります。
     * @param x 数値
     * @param y 小数部の桁数
     * @return 計算結果
     *
     * <listing version="3.0">
     * JSKit.format(3.14159, 3); // 3.141
     * </listing>
     */
    JSKit.format = function(x, y) {
        return Math.floor(x * Math.pow(10, y)) / Math.pow(10, y);
    };

    /**
     * ランダムな文字列を生成します。
     * typeには、num、alpha、alnumが指定できます。
     * @params length 文字列長
     * @params type 文字列を構成する文字種
     */
    JSKit.random = function(length, type) {
        var alphas = 'abcdefghijklmnopqestuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        var nums = '1234567890';
        var list = '';
        var result = '';

        if(type == 'num') {
            list = nums;
        }else if(type == 'alpha') {
            list = alphas;
        }else if(type == 'alnum') {
            list = alphas+nums;
        }else {
            return '';
        }
        for(var i = 0; i < length; i++) {
            result += list.substr(JSKit.mt_rand(0, list.length), 1);
        }
        return result;
    };

    /**
     * UNIX時間を取得します。
     */
    JSKit.time = function() {
        return new Date/1000|0;
    };

    /**
     * Cookieの値を取得します。
     * http://www9.plala.or.jp/oyoyon/html/script/cookie.html
     *
     * @param name 名前
     * @param value Cookieが存在しないときに返す値
     */
    JSKit.getCookie = function(name, value) {
        if (!name || !document.cookie) {
            return value;
        }
        var cookies = document.cookie.split("; ");
        for (var i = 0; i < cookies.length; i++) {
            var str = cookies[i].split("=");
            if (str[0] == name) {
                return unescape(str[1]);
            }
        }
        return value;
    };

    /**
     * Cookieの値を設定します。
     * http://www9.plala.or.jp/oyoyon/html/script/cookie.html
     *
     * @param name 名前
     * @param value 値
     * @param domain 参照可能なドメインを指定します。0のとき発行したサーバ、1のとき発行したドメインを含むサーバ。
     * @param path 参照可能なパスを指定します。0のとき発行したディレクトリ、1のとき発行したページ。
     * @param expires 有効期限を日数で指定します。0を指定するとセッション中、-1を指定するとCookieを削除。
     * @param secure 1のときSSL通信時のCookieの暗号化を有効にします。
     */
    JSKit.setCookie = function(name, value, domain, path, expires, secure) {
        var str = name + "=" + escape(value);
        if (domain) {
            if (domain == 1) domain = location.hostname.replace(/^[^\.]*/, "");
             str += "; domain=" + domain;
        }
        if (path) {
            if (path == 1) path = location.pathname;
            str += "; path=" + path;
        }
        if (expires) {
            expires = new Date(new Date().getTime() + (60 * 60 * 24 * 1000 * expires));
            expires = expires.toGMTString();
            str += "; expires=" + expires;
        }
        if (secure && location.protocol == "https:") {
            str += "; secure";
        }

        document.cookie = str;
    };

    return JSKit;
}());<|endoftext|>


<commit_before>// Test the widget framework.
casper.notebook_test(function () {
    var index;

    // Test widget dependencies ////////////////////////////////////////////////
    this.then(function () {

        // Check if the WidgetManager class is defined.
        this.test.assert(this.evaluate(function() {
            return IPython.WidgetManager != undefined;
        }), 'WidgetManager class is defined');
    });

    index = this.append_cell(
        'from IPython.html import widgets\n' +
        'from IPython.display import display, clear_output\n' +
        'print("Success")');
    this.execute_cell_then(index);

    this.wait(500); // Wait for require.js async callbacks to load dependencies.

    this.then(function () {
        // Check if the widget manager has been instanciated.
        this.test.assert(this.evaluate(function() {
            return IPython.widget_manager != undefined;
        }), 'Notebook widget manager instanciated');
    });

    // Check widget mapping ////////////////////////////////////////////////////
    index = this.append_cell(
        'names = [name for name in dir(widgets)' +
        ' if name.endswith("Widget") and name!= "Widget"]\n' +
        'for name in names:\n' +
        '    print(name)\n');
    this.execute_cell_then(index, function(index){

        // Get the widget names that are registered with the widget manager.  Assume
        // a 1 to 1 mapping of model and widgets names (model names just have 'model'
        // suffixed).
        var javascript_names = this.evaluate(function () {
            names = [];
            for (var name in IPython.widget_manager.widget_model_types) {
                names.push(name.replace('Model',''));
            }
            return names;
        });

        // Get the widget names registered in python.
        var python_names = this.get_output_cell(index).text.split('\n');

        // Make sure the two lists have the same items.
        for (var i in javascript_names) {
            var javascript_name = javascript_names[i];
            var found = false;
            for (var j in python_names) {
                var python_name = python_names[j];
                if (python_name==javascript_name) {
                    found = true;
                    break;
                }
            }
            this.test.assert(found, javascript_name + ' exists in python');
        }
        for (var i in python_names) {
            var python_name = python_names[i];
            if (python_name.length > 0) {
                var found = false;
                for (var j in javascript_names) {
                    var javascript_name = javascript_names[j];
                    if (python_name==javascript_name) {
                        found = true;
                        break;
                    }
                }
                this.test.assert(found, python_name + ' exists in javascript');
            }
        }
    });

    // Test bool widget ////////////////////////////////////////////////////////
    var bool_index = this.append_cell(
        'bool_widget = widgets.BoolWidget(description="Title", value=True)\n' +
        'display(bool_widget)\n'+
        'display(bool_widget, view_name="ToggleButtonView")\n' +
        'print("Success")');
    this.execute_cell_then(bool_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create bool widget cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-hbox-single input'),
            'Checkbox exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is checked.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel'),
            'Checkbox label exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel', 'html')=="Title",
            'Checkbox labeled correctly.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea div button'),
            'Toggle button exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea div button', 'html')=="Title",
            'Toggle button labeled correctly.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is toggled.');

    });

    index = this.append_cell(
        'bool_widget.value = False\n' +
        'print("Success")');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Change bool widget value cell executed with correct output.');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is not checked. (1)');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is not toggled. (1)');

        // Try toggling the bool by clicking on the toggle button.
        this.cell_element_function(bool_index, '.widget-area .widget-subarea div button', 'click');

        this.test.assert(this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is checked. (2)');

        this.test.assert(this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is toggled. (2)');

        // Try toggling the bool by clicking on the checkbox.
        this.cell_element_function(bool_index, '.widget-area .widget-subarea .widget-hbox-single input', 'click');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is not checked. (3)');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is not toggled. (3)');

    });

    // Test button widget //////////////////////////////////////////////////////
    var button_index = this.append_cell(
        'button = widgets.ButtonWidget(description="Title")\n' +
        'display(button)\n'+
        'print("Success")\n' +
        'def handle_click(sender):\n' +
        '    print("Clicked")\n' +
        'button.on_click(handle_click)');
    this.execute_cell_then(button_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create button cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea button'),
            'Widget button exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea button', 'html')=='Title',
            'Set button description.');

        this.cell_element_function(index,
            '.widget-area .widget-subarea button', 'click');
    });

    this.wait(500); // Wait for click to execute in kernel and write output

    this.then(function () {
        this.test.assert(this.get_output_cell(button_index, 1).text == 'Clicked\n',
            'Button click event fires.');
    });

    index = this.append_cell(
        'button.close()\n'+
        'print("Success")');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Close button cell executed with correct output.');

        this.test.assert(! this.cell_element_exists(button_index,
            '.widget-area .widget-subarea button'),
            'Widget button doesn\'t exists.');
    });

    // Test container widget ///////////////////////////////////////////////////
    var container_index = this.append_cell(
        'container = widgets.ContainerWidget()\n' +
        'button = widgets.ButtonWidget(parent=container)\n'+
        'display(container)\n'+
        'container.add_class("my-test-class")\n'+
        'print("Success")\n');
    this.execute_cell_then(container_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create container cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-container'),
            'Widget container exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .my-test-class'),
            'add_class works.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .my-test-class button'),
            'Container parent/child relationship works.');
    });

    index = this.append_cell(
        'container.set_css("display", "none")\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Set container class CSS cell executed with correct output.');

        this.test.assert(this.cell_element_function(container_index,
            '.widget-area .widget-subarea .my-test-class', 'css', ['display'])=='none',
            'set_css works.');
    });

    index = this.append_cell(
        'container.remove_class("my-test-class")\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Remove container class cell executed with correct output.');

        this.test.assert(! this.cell_element_exists(container_index,
            '.widget-area .widget-subarea .my-test-class'),
            'remove_class works.');
    });

    index = this.append_cell(
        'display(button)\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Display container child executed with correct output.');

        this.test.assert(! this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-container'),
            'Parent container not displayed.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea button'),
            'Child displayed.');
    });

    // Test float range widget /////////////////////////////////////////////////
    var slider_query = '.widget-area .widget-subarea .widget-hbox-single .slider';
    var float_text_query = '.widget-area .widget-subarea .widget-hbox-single .widget-numeric-text';

    var floatrange_index = this.append_cell(
        'floatrange = widgets.FloatRangeWidget()\n' +
        'display(floatrange)\n' +
        'display(floatrange, view_name="FloatTextView")\n' +
        'print("Success")\n');
    this.execute_cell_then(floatrange_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create float range cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index, slider_query),
            'Widget slider exists.');

        this.test.assert(this.cell_element_exists(index, float_text_query),
            'Widget float textbox exists.');
    });

    index = this.append_cell(
        'floatrange.max = 50.0\n' +
        'floatrange.min = -50.0\n' +
        'floatrange.value = 25.0\n' +
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Float range properties cell executed with correct output.');

        this.test.assert(this.cell_element_exists(floatrange_index, slider_query),
            'Widget slider exists.');

        this.test.assert(this.cell_element_function(floatrange_index, slider_query,
            'slider', ['value']) == 25.0,
            'Slider set to Python value.');

        this.test.assert(this.cell_element_function(floatrange_index, float_text_query,
            'val') == 25.0, 'Float textbox set to Python value.');

        // Clear the float textbox value and then set it to 1 by emulating
        // keyboard presses.
        this.cell_element_function(floatrange_index, float_text_query, 'val', ['']);
        this.sendKeys(float_text_query, '1');
    });

    this.wait(500); // Wait for slide to execute in kernel

    index = this.append_cell('print(floatrange.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '1.0\n',
            'Float textbox set float range value');

        // Clear the float textbox value and then set it to 120 by emulating
        // keyboard presses.
        this.cell_element_function(floatrange_index, float_text_query, 'val', ['']);
        this.sendKeys(float_text_query, '120');
    });

    this.wait(500); // Wait for slide to execute in kernel

    index = this.append_cell('print(floatrange.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '50.0\n',
            'Float textbox value bound');

        // Clear the float textbox value and then set it to 'hello world' by
        // emulating keyboard presses.  'hello world' should get filtered...
        this.cell_element_function(floatrange_index, float_text_query, 'val', ['']);
        this.sendKeys(float_text_query, 'hello world');
    });

    this.wait(500); // Wait for slide to execute in kernel

    index = this.append_cell('print(floatrange.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '50.0\n',
            'Invalid float textbox characters ignored');
    });

});

<commit_msg>Added float widget tests<commit_after>// Test the widget framework.
casper.notebook_test(function () {
    var index;

    // Test widget dependencies ////////////////////////////////////////////////
    this.then(function () {

        // Check if the WidgetManager class is defined.
        this.test.assert(this.evaluate(function() {
            return IPython.WidgetManager != undefined;
        }), 'WidgetManager class is defined');
    });

    index = this.append_cell(
        'from IPython.html import widgets\n' +
        'from IPython.display import display, clear_output\n' +
        'print("Success")');
    this.execute_cell_then(index);

    this.wait(500); // Wait for require.js async callbacks to load dependencies.

    this.then(function () {
        // Check if the widget manager has been instanciated.
        this.test.assert(this.evaluate(function() {
            return IPython.widget_manager != undefined;
        }), 'Notebook widget manager instanciated');
    });

    // Check widget mapping ////////////////////////////////////////////////////
    index = this.append_cell(
        'names = [name for name in dir(widgets)' +
        ' if name.endswith("Widget") and name!= "Widget"]\n' +
        'for name in names:\n' +
        '    print(name)\n');
    this.execute_cell_then(index, function(index){

        // Get the widget names that are registered with the widget manager.  Assume
        // a 1 to 1 mapping of model and widgets names (model names just have 'model'
        // suffixed).
        var javascript_names = this.evaluate(function () {
            names = [];
            for (var name in IPython.widget_manager.widget_model_types) {
                names.push(name.replace('Model',''));
            }
            return names;
        });

        // Get the widget names registered in python.
        var python_names = this.get_output_cell(index).text.split('\n');

        // Make sure the two lists have the same items.
        for (var i in javascript_names) {
            var javascript_name = javascript_names[i];
            var found = false;
            for (var j in python_names) {
                var python_name = python_names[j];
                if (python_name==javascript_name) {
                    found = true;
                    break;
                }
            }
            this.test.assert(found, javascript_name + ' exists in python');
        }
        for (var i in python_names) {
            var python_name = python_names[i];
            if (python_name.length > 0) {
                var found = false;
                for (var j in javascript_names) {
                    var javascript_name = javascript_names[j];
                    if (python_name==javascript_name) {
                        found = true;
                        break;
                    }
                }
                this.test.assert(found, python_name + ' exists in javascript');
            }
        }
    });

    // Test bool widget ////////////////////////////////////////////////////////
    var bool_index = this.append_cell(
        'bool_widget = widgets.BoolWidget(description="Title", value=True)\n' +
        'display(bool_widget)\n'+
        'display(bool_widget, view_name="ToggleButtonView")\n' +
        'print("Success")');
    this.execute_cell_then(bool_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create bool widget cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-hbox-single input'),
            'Checkbox exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is checked.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel'),
            'Checkbox label exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel', 'html')=="Title",
            'Checkbox labeled correctly.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea div button'),
            'Toggle button exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea div button', 'html')=="Title",
            'Toggle button labeled correctly.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is toggled.');

    });

    index = this.append_cell(
        'bool_widget.value = False\n' +
        'print("Success")');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Change bool widget value cell executed with correct output.');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is not checked. (1)');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is not toggled. (1)');

        // Try toggling the bool by clicking on the toggle button.
        this.cell_element_function(bool_index, '.widget-area .widget-subarea div button', 'click');

        this.test.assert(this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is checked. (2)');

        this.test.assert(this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is toggled. (2)');

        // Try toggling the bool by clicking on the checkbox.
        this.cell_element_function(bool_index, '.widget-area .widget-subarea .widget-hbox-single input', 'click');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is not checked. (3)');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is not toggled. (3)');

    });

    // Test button widget //////////////////////////////////////////////////////
    var button_index = this.append_cell(
        'button = widgets.ButtonWidget(description="Title")\n' +
        'display(button)\n'+
        'print("Success")\n' +
        'def handle_click(sender):\n' +
        '    print("Clicked")\n' +
        'button.on_click(handle_click)');
    this.execute_cell_then(button_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create button cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea button'),
            'Widget button exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea button', 'html')=='Title',
            'Set button description.');

        this.cell_element_function(index,
            '.widget-area .widget-subarea button', 'click');
    });

    this.wait(500); // Wait for click to execute in kernel and write output

    this.then(function () {
        this.test.assert(this.get_output_cell(button_index, 1).text == 'Clicked\n',
            'Button click event fires.');
    });

    // Close the button widget asynchronisly.
    index = this.append_cell('button.close()\n');
    this.execute_cell(index);

    this.wait(500); // Wait for the button to close.

    this.then(function(){
        this.test.assert(! this.cell_element_exists(button_index,
            '.widget-area .widget-subarea button'),
            'Widget button doesn\'t exists.');
    });

    // Test container widget ///////////////////////////////////////////////////
    var container_index = this.append_cell(
        'container = widgets.ContainerWidget()\n' +
        'button = widgets.ButtonWidget(parent=container)\n'+
        'display(container)\n'+
        'container.add_class("my-test-class")\n'+
        'print("Success")\n');
    this.execute_cell_then(container_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create container cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-container'),
            'Widget container exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .my-test-class'),
            'add_class works.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .my-test-class button'),
            'Container parent/child relationship works.');
    });

    index = this.append_cell(
        'container.set_css("display", "none")\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Set container class CSS cell executed with correct output.');

        this.test.assert(this.cell_element_function(container_index,
            '.widget-area .widget-subarea .my-test-class', 'css', ['display'])=='none',
            'set_css works.');
    });

    index = this.append_cell(
        'container.remove_class("my-test-class")\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Remove container class cell executed with correct output.');

        this.test.assert(! this.cell_element_exists(container_index,
            '.widget-area .widget-subarea .my-test-class'),
            'remove_class works.');
    });

    index = this.append_cell(
        'display(button)\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Display container child executed with correct output.');

        this.test.assert(! this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-container'),
            'Parent container not displayed.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea button'),
            'Child displayed.');
    });

    // Test float range widget /////////////////////////////////////////////////
    var slider_query = '.widget-area .widget-subarea .widget-hbox-single .slider';
    var float_text_query = '.widget-area .widget-subarea .widget-hbox-single .widget-numeric-text';

    var floatrange_index = this.append_cell(
        'floatrange = widgets.FloatRangeWidget()\n' +
        'display(floatrange)\n' +
        'display(floatrange, view_name="FloatTextView")\n' +
        'print("Success")\n');
    this.execute_cell_then(floatrange_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create float range cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index, slider_query),
            'Widget slider exists.');

        this.test.assert(this.cell_element_exists(index, float_text_query),
            'Widget float textbox exists.');
    });

    index = this.append_cell(
        'floatrange.max = 50.0\n' +
        'floatrange.min = -50.0\n' +
        'floatrange.value = 25.0\n' +
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Float range properties cell executed with correct output.');

        this.test.assert(this.cell_element_exists(floatrange_index, slider_query),
            'Widget slider exists.');

        this.test.assert(this.cell_element_function(floatrange_index, slider_query,
            'slider', ['value']) == 25.0,
            'Slider set to Python value.');

        this.test.assert(this.cell_element_function(floatrange_index, float_text_query,
            'val') == 25.0, 'Float textbox set to Python value.');

        // Clear the float textbox value and then set it to 1 by emulating
        // keyboard presses.
        this.cell_element_function(floatrange_index, float_text_query, 'val', ['']);
        this.sendKeys(float_text_query, '1');
    });

    this.wait(500); // Wait for change to execute in kernel

    index = this.append_cell('print(floatrange.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '1.0\n',
            'Float textbox set float range value');

        // Clear the float textbox value and then set it to 120 by emulating
        // keyboard presses.
        this.cell_element_function(floatrange_index, float_text_query, 'val', ['']);
        this.sendKeys(float_text_query, '120');
    });

    this.wait(500); // Wait for change to execute in kernel

    index = this.append_cell('print(floatrange.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '50.0\n',
            'Float textbox value bound');

        // Clear the float textbox value and then set it to 'hello world' by
        // emulating keyboard presses.  'hello world' should get filtered...
        this.cell_element_function(floatrange_index, float_text_query, 'val', ['']);
        this.sendKeys(float_text_query, 'hello world');
    });

    this.wait(500); // Wait for change to execute in kernel

    index = this.append_cell('print(floatrange.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '50.0\n',
            'Invalid float textbox characters ignored');
    });

    // Test float widget ///////////////////////////////////////////////////////
    var float_text_query_2 = '.widget-area .widget-subarea .widget-hbox-single .my-second-float-text';

    var float_index = this.append_cell(
        'float_widget = widgets.FloatWidget()\n' +
        'display(float_widget)\n' +
        'float_widget.add_class("my-second-float-text")\n' +
        'print("Success")\n');
    this.execute_cell_then(float_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create float cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index, float_text_query_2),
            'Widget float textbox exists.');

        this.cell_element_function(float_index, float_text_query_2, 'val', ['']);
        this.sendKeys(float_text_query_2, '1.05');
    });

    this.wait(500); // Wait for change to execute in kernel

    index = this.append_cell('print(float_widget.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '1.05\n',
            'Float textbox value set.');
        this.cell_element_function(float_index, float_text_query_2, 'val', ['']);
        this.sendKeys(float_text_query_2, '123456789.0');
    });

    this.wait(500); // Wait for change to execute in kernel

    index = this.append_cell('print(float_widget.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '123456789.0\n',
            'Long float textbox value set (probably triggers throttling).');
        this.cell_element_function(float_index, float_text_query_2, 'val', ['']);
        this.sendKeys(float_text_query_2, '12hello');
    });

    this.wait(500); // Wait for change to execute in kernel

    index = this.append_cell('print(float_widget.value)\n');
    this.execute_cell_then(index, function(index){
        this.test.assert(this.get_output_cell(index).text == '12.0\n',
            'Float textbox value set.');
    });
});

<|endoftext|>


<commit_before>// Abstract Persistence driver

'use strict';

var clear          = require('es5-ext/array/#/clear')
  , ensureArray    = require('es5-ext/array/valid-array')
  , assign         = require('es5-ext/object/assign')
  , ensureCallable = require('es5-ext/object/valid-callable')
  , ensureObject   = require('es5-ext/object/valid-object')
  , ensureString   = require('es5-ext/object/validate-stringifiable-value')
  , emitError      = require('event-emitter/emit-error')
  , d              = require('d')
  , autoBind       = require('d/auto-bind')
  , lazy           = require('d/lazy')
  , ee             = require('event-emitter')
  , getStamp       = require('time-uuid/time')
  , ensureDatabase = require('dbjs/valid-dbjs')
  , ensureType     = require('dbjs/valid-dbjs-type')
  , Event          = require('dbjs/_setup/event')
  , unserialize    = require('dbjs/_setup/unserialize/value')
  , serialize      = require('dbjs/_setup/serialize/value')
  , resolveKeyPath = require('dbjs/_setup/utils/resolve-property-path')
  , once           = require('timers-ext/once')

  , isModelId = RegExp.prototype.test.bind(/^[A-Z]/)
  , tokenize = resolveKeyPath.tokenize, resolveObject = resolveKeyPath.resolveObject
  , create = Object.create, stringify = JSON.stringify;

var PersistenceDriver = module.exports = Object.defineProperties(function (dbjs/*, options*/) {
	var autoSaveFilter, options;
	if (!(this instanceof PersistenceDriver)) return new PersistenceDriver(dbjs, arguments[1]);
	options = Object(arguments[1]);
	this.db = ensureDatabase(dbjs);
	autoSaveFilter = (options.autoSaveFilter != null)
		? ensureCallable(options.autoSaveFilter) : this.constructor.defaultAutoSaveFilter;
	dbjs.objects.on('update', function (event) {
		if (event.sourceId === 'persistentLayer') return;
		if (!autoSaveFilter(event)) return;
		this._eventsToStore.push(event);
		this._exportEvents();
	}.bind(this));
}, {
	defaultAutoSaveFilter: d(function (event) { return !isModelId(event.object.master.__id__); })
});

var notImplemented = function () { throw new Error("Not implemented"); };

ee(Object.defineProperties(PersistenceDriver.prototype, assign({
	_importValue: d(function (id, value, stamp) {
		var proto;
		if (this._loadedEventsMap[id + '.' + stamp]) return;
		this._loadedEventsMap[id + '.' + stamp] = true;
		value = unserialize(value, this.db.objects);
		if (value && value.__id__ && (value.constructor.prototype === value)) proto = value.constructor;
		return new Event(this.db.objects.unserialize(id, proto), value, stamp, 'persistentLayer');
	}),
	_ensureCustomKey: d(function (key) {
		key = ensureString(key);
		if (key[0] !== '_') {
			throw new Error("Provided key " + stringify(key) + " is not a valid custom key");
		}
		return key;
	}),
	_ensureOpen: d(function () {
		if (this.isClosed) throw new Error("Database not accessible");
	}),
	isClosed: d(false),
	getCustom: d(function (key) {
		this._ensureOpen();
		return this._getCustom(this._ensureCustomKey(key))(function (value) {
			if (!value) return;
			return unserialize(value, this.db.objects);
		}.bind(this));
	}),
	_getCustom: d(notImplemented),
	loadValue: d(function (id) {
		this._ensureOpen();
		return this._loadValue(ensureString(id));
	}),
	_loadValue: d(notImplemented),
	loadObject: d(function (id) {
		this._ensureOpen();
		id = ensureString(id);
		return this._loadObject(id);
	}),
	_loadObject: d(notImplemented),
	loadAll: d(function () {
		this._ensureOpen();
		return this._loadAll();
	}),
	_loadAll: d(notImplemented),
	storeCustom: d(function (key, value) {
		this._ensureOpen();
		if (value !== undefined) value = serialize(value);
		return this._storeCustom(this._ensureCustomKey(key), value);
	}),
	_storeCustom: d(notImplemented),
	storeEvent: d(function (event) {
		this._ensureOpen();
		return this._storeEvent(ensureObject(event));
	}),
	_storeEvent: d(notImplemented),
	storeEvents: d(function (events) {
		this._ensureOpen();
		return this._storeEvents(ensureArray(events));
	}),
	_storeEvents: d(notImplemented),
	trackComputed: d(function (type, keyPath) {
		var names, key, onAdd, onDelete;
		ensureType(type);
		names = tokenize(ensureString(keyPath));
		this._ensureOpen();
		key = names[names.length - 1];
		onAdd = function (obj) {
			var observable, value, stamp, id;
			obj = resolveObject(obj, names);
			if (!obj) return null;
			if (obj.isKeyStatic(key)) {
				value = obj[key];
				stamp = 0;
			} else {
				observable = obj._getObservable_(key);
				observable.on('change', this._onComputedChange);
				value = observable.value;
				stamp = observable.lastModified;
			}
			id = obj.__id__ + '/' + key;
			value = serialize(value);
			this._getComputed(id)(function (old) {
				if (old) {
					if (old.stamp === stamp) {
						if (old.value === value) return;
						++stamp; // most likely model update
					} else if (old.stamp > stamp) {
						stamp = old.stamp + 1;
					}
				}
				return this._storeComputed(id, value, stamp);
			}).done();
		}.bind(this);
		onDelete = function (obj) {
			obj = resolveObject(obj, names);
			if (!obj) return null;
			if (obj.isKeyStatic(key)) return;
			obj._getObservable_(key).off('chnge', this._onComputedChange);
		}.bind(this);
		type.instances.forEach(onAdd);
		type.instances.on('change', function (event) {
			if (event.type === 'add') {
				onAdd(event.value);
				return;
			}
			if (event.type === 'delete') {
				onDelete(event.value);
				return;
			}
			if (event.type === 'batch') {
				if (event.added) event.added.forEach(onAdd);
				if (event.deleted) event.deleted.forEach(onDelete);
			}
		});
	}),
	_getComputed: d(notImplemented),
	_storeComputed: d(notImplemented),
	close: d(function () {
		this._ensureOpen();
		this.isClosed = true;
		return this._close();
	}),
	_close: d(notImplemented)
}, autoBind({
	emitError: d(emitError),
	_onComputedChange: d(function (event) {
		if (event.target.object.conctrutor === event.target.object.database.Base) return;
		this._storeComputed(event.target.dbId, serialize(event.newValue),
			event.dbjs ? event.dbjs.stamp : getStamp());
	})
}), lazy({
	_loadedEventsMap: d(function () { return create(null); }),
	_eventsToStore: d(function () { return []; }),
	_exportEvents: d(function () {
		return once(function () {
			this.storeEvents(this._eventsToStore);
			clear.call(this._eventsToStore);
		}.bind(this));
	})
}))));
<commit_msg>Keep serialized value in other variable<commit_after>// Abstract Persistence driver

'use strict';

var clear          = require('es5-ext/array/#/clear')
  , ensureArray    = require('es5-ext/array/valid-array')
  , assign         = require('es5-ext/object/assign')
  , ensureCallable = require('es5-ext/object/valid-callable')
  , ensureObject   = require('es5-ext/object/valid-object')
  , ensureString   = require('es5-ext/object/validate-stringifiable-value')
  , emitError      = require('event-emitter/emit-error')
  , d              = require('d')
  , autoBind       = require('d/auto-bind')
  , lazy           = require('d/lazy')
  , ee             = require('event-emitter')
  , getStamp       = require('time-uuid/time')
  , ensureDatabase = require('dbjs/valid-dbjs')
  , ensureType     = require('dbjs/valid-dbjs-type')
  , Event          = require('dbjs/_setup/event')
  , unserialize    = require('dbjs/_setup/unserialize/value')
  , serialize      = require('dbjs/_setup/serialize/value')
  , resolveKeyPath = require('dbjs/_setup/utils/resolve-property-path')
  , once           = require('timers-ext/once')

  , isModelId = RegExp.prototype.test.bind(/^[A-Z]/)
  , tokenize = resolveKeyPath.tokenize, resolveObject = resolveKeyPath.resolveObject
  , create = Object.create, stringify = JSON.stringify;

var PersistenceDriver = module.exports = Object.defineProperties(function (dbjs/*, options*/) {
	var autoSaveFilter, options;
	if (!(this instanceof PersistenceDriver)) return new PersistenceDriver(dbjs, arguments[1]);
	options = Object(arguments[1]);
	this.db = ensureDatabase(dbjs);
	autoSaveFilter = (options.autoSaveFilter != null)
		? ensureCallable(options.autoSaveFilter) : this.constructor.defaultAutoSaveFilter;
	dbjs.objects.on('update', function (event) {
		if (event.sourceId === 'persistentLayer') return;
		if (!autoSaveFilter(event)) return;
		this._eventsToStore.push(event);
		this._exportEvents();
	}.bind(this));
}, {
	defaultAutoSaveFilter: d(function (event) { return !isModelId(event.object.master.__id__); })
});

var notImplemented = function () { throw new Error("Not implemented"); };

ee(Object.defineProperties(PersistenceDriver.prototype, assign({
	_importValue: d(function (id, value, stamp) {
		var proto;
		if (this._loadedEventsMap[id + '.' + stamp]) return;
		this._loadedEventsMap[id + '.' + stamp] = true;
		value = unserialize(value, this.db.objects);
		if (value && value.__id__ && (value.constructor.prototype === value)) proto = value.constructor;
		return new Event(this.db.objects.unserialize(id, proto), value, stamp, 'persistentLayer');
	}),
	_ensureCustomKey: d(function (key) {
		key = ensureString(key);
		if (key[0] !== '_') {
			throw new Error("Provided key " + stringify(key) + " is not a valid custom key");
		}
		return key;
	}),
	_ensureOpen: d(function () {
		if (this.isClosed) throw new Error("Database not accessible");
	}),
	isClosed: d(false),
	getCustom: d(function (key) {
		this._ensureOpen();
		return this._getCustom(this._ensureCustomKey(key))(function (value) {
			if (!value) return;
			return unserialize(value, this.db.objects);
		}.bind(this));
	}),
	_getCustom: d(notImplemented),
	loadValue: d(function (id) {
		this._ensureOpen();
		return this._loadValue(ensureString(id));
	}),
	_loadValue: d(notImplemented),
	loadObject: d(function (id) {
		this._ensureOpen();
		id = ensureString(id);
		return this._loadObject(id);
	}),
	_loadObject: d(notImplemented),
	loadAll: d(function () {
		this._ensureOpen();
		return this._loadAll();
	}),
	_loadAll: d(notImplemented),
	storeCustom: d(function (key, value) {
		this._ensureOpen();
		if (value !== undefined) value = serialize(value);
		return this._storeCustom(this._ensureCustomKey(key), value);
	}),
	_storeCustom: d(notImplemented),
	storeEvent: d(function (event) {
		this._ensureOpen();
		return this._storeEvent(ensureObject(event));
	}),
	_storeEvent: d(notImplemented),
	storeEvents: d(function (events) {
		this._ensureOpen();
		return this._storeEvents(ensureArray(events));
	}),
	_storeEvents: d(notImplemented),
	trackComputed: d(function (type, keyPath) {
		var names, key, onAdd, onDelete;
		ensureType(type);
		names = tokenize(ensureString(keyPath));
		this._ensureOpen();
		key = names[names.length - 1];
		onAdd = function (obj) {
			var observable, value, stamp, id, sValue;
			obj = resolveObject(obj, names);
			if (!obj) return null;
			if (obj.isKeyStatic(key)) {
				value = obj[key];
				stamp = 0;
			} else {
				observable = obj._getObservable_(key);
				observable.on('change', this._onComputedChange);
				value = observable.value;
				stamp = observable.lastModified;
			}
			id = obj.__id__ + '/' + key;
			sValue = serialize(value);
			this._getComputed(id)(function (old) {
				if (old) {
					if (old.stamp === stamp) {
						if (old.value === sValue) return;
						++stamp; // most likely model update
					} else if (old.stamp > stamp) {
						stamp = old.stamp + 1;
					}
				}
				return this._storeComputed(id, sValue, stamp);
			}).done();
		}.bind(this);
		onDelete = function (obj) {
			obj = resolveObject(obj, names);
			if (!obj) return null;
			if (obj.isKeyStatic(key)) return;
			obj._getObservable_(key).off('chnge', this._onComputedChange);
		}.bind(this);
		type.instances.forEach(onAdd);
		type.instances.on('change', function (event) {
			if (event.type === 'add') {
				onAdd(event.value);
				return;
			}
			if (event.type === 'delete') {
				onDelete(event.value);
				return;
			}
			if (event.type === 'batch') {
				if (event.added) event.added.forEach(onAdd);
				if (event.deleted) event.deleted.forEach(onDelete);
			}
		});
	}),
	_getComputed: d(notImplemented),
	_storeComputed: d(notImplemented),
	close: d(function () {
		this._ensureOpen();
		this.isClosed = true;
		return this._close();
	}),
	_close: d(notImplemented)
}, autoBind({
	emitError: d(emitError),
	_onComputedChange: d(function (event) {
		if (event.target.object.conctrutor === event.target.object.database.Base) return;
		this._storeComputed(event.target.dbId, serialize(event.newValue),
			event.dbjs ? event.dbjs.stamp : getStamp());
	})
}), lazy({
	_loadedEventsMap: d(function () { return create(null); }),
	_eventsToStore: d(function () { return []; }),
	_exportEvents: d(function () {
		return once(function () {
			this.storeEvents(this._eventsToStore);
			clear.call(this._eventsToStore);
		}.bind(this));
	})
}))));
<|endoftext|>