<commit_before>/**
 * fluidinfo.js - a small and simple client for Fluidinfo written in Javascript.
 *
 * @author onigiri, ntoll & barshirtcliff
 * @version 0.1
 * @constructor
 * @param options {Object} Contains various parameters for the new session with
 * Fluidinfo.
 *

 *
username

 *
The username to use when authenticating with Fluidinfo.

 *
password

 *
The password to use when authenticating with Fluidinfo

 *
instance

 *
The instance to connect to. Either "main", "sandbox" or a bespoke
 *  instance. Defaults to "main".

 *

 * returns {Object} An object through which one interacts with Fluidinfo.
 */
fluidinfo = function(options) {
    /**
     * Encodes strings into base64. Adapted from:
     * http://www.webtoolkit.info/javascript-base64.html
    */
    var Base64 = {
        // private property
        _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
        // public method for encoding
        encode : function (input) {
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;
            input = Base64._utf8_encode(input);
            while (i < input.length) {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);
                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;
                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }
                output = output +
                this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
                this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
            }
            return output;
        },
        // private method for UTF-8 encoding
        _utf8_encode : function (string) {
            string = string.replace(/\r\n/g,"\n");
            var utftext = "";
            for (var n = 0; n < string.length; n++) {
                var c = string.charCodeAt(n);
                if (c < 128) {
                    utftext += String.fromCharCode(c);
                }
                else if((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
                else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
            }
            return utftext;
        }
    }

    /**
     * Represents a session with Fluidinfo.
     */
    var session = new Object();
    var authorizationToken = "";

    if(options) {
      if(options.instance) {
        switch(options.instance.toLowerCase()) {
          case "main":
            session.baseURL = "https://fluiddb.fluidinfo.com/";
            break;
          case "sandbox":
            session.baseURL = "https://sandbox.fluidinfo.com/";
            break;
          default:
              // validate the bespoke instance
              var urlRegex = /^(http|https):\/\/[\w\-_\.]+\/$/;
              if(urlRegex.exec(options.instance)) {
                session.baseURL = options.instance;
              } else {
                throw {
                  name: "ValueError",
                  message: "The URL must start with http[s]:// and have a trailing slash ('/') to be valid. E.g. https://localhost/"
                };
              }
        }
      }
      if((options.username != undefined) && (options.password != undefined)) {
        authorizationToken = Base64.encode(options.username + ":" + options.password);
      }
    }

    // Catch-all to make sure the library defaults to the main instance
    if(session.baseURL === undefined){
      session.baseURL = "https://fluiddb.fluidinfo.com/";
    }

    /**
     * Magic voodoo used to identify an array (potentially a Fluidinfo set).
     * Taken from page 61 of Doug Crockford's "Javascript: The Good Parts".
     *
     * @param value A value that might be an array.
     * @return {boolean} An indication if the value is an array.
     */
    function isArray(value) {
      return Object.prototype.toString.apply(value) === "[object Array]";
    };

    /**
     * Given a path that is expressed as an array of path elements, will
     * return the correctly encoded URL. e.g. ["a", "b", "c"] -> "/a/b/c"
     *
     * @param value {Array} A list of values to be appropriately encoded between
     * '/' values.
     * @return {string} The appropriately encoded URL.
     */
    function encodeURL(path) {
      var result = "";
      for(i=0; i<path.length; i++) {
        result += "/" + encodeURIComponent(path[i]);
      }
      return result.slice(1); // chops the leading slash
    };

    /**
     * Checks the passed value to discover if it's a Fluidinfo "primitive"
     * type. See
     * the Fluidinfo docs for more
     * information on Fluidinfo's types.
     *
     * @param value A value whose "type" needs identifying
     * @return A boolean indication if the value is a Fluidinfo primitive type.
     */
    function isPrimitive(value) {
      // check the easy type matches first
      var valueType = typeof(value);
      var primitiveTypes = ["number", "string", "boolean"];
      for(i=0; i<primitiveTypes.length; i++) {
        if(valueType === primitiveTypes[i]) {
          return true;
        }
      }
      // A null value is also a primitive
      if(value===null) {
        return true;
      }
      // check for an array (potential set) and validate it only contains
      // strings (currently multi-type arrays are not allowed)
      if(isArray(value)) {
        for(i=0; i<value.length; i++) {
          memberType = typeof(value[i]);
          if(memberType !== "string") {
            return false;
          }
        }
        return true;
      }
      // value hasn't matched any of the primitive checks
      return false;
    }

    /**
     * Given the options describing a request, will return the most appropriate
     * MIME to set as the value for the Content-Type header
     *
     * @param options {Object} Contains various parameters describing the
     * request.
     * @return the most appropriate MIME to use of null if not appropriate /
     * required.
     */
    function detectContentType(options) {
      // a "PUT" to objects/ or about/ endpoints means dealing with the MIME
      // of a tag-value. If no MIME type is passed in the options objects
      // then check if the value is a Fluidinfo primitive type. Otherwise
      // complain.
      var result = null;
      if(options.type==="PUT" && (options.url.match("^objects\/") || options.url.match("^about\/"))) {
        if(options.contentType){
          result = options.contentType;
        } else if (isPrimitive(options.data)) {
          result = "application/vnd.fluiddb.value+json";
        } else {
          throw { name: "ValueError", message: "Must supply Content-Type"};
        }
      } else if (options.data) {
        // all other requests to the API that have payloads will be passing JSON
        result = "application/json";
      }
      return result;
    }

    /**
     * Given a value from a Content-Type header, will return a boolean
     * indication if the associated content is JSON.
     *
     * @param contentType {string} The MIME value associated with the content
     * @result {boolean} An indication if the associated content is JSON
     */
    function isJSONData(contentType) {
      return contentType && (contentType === "application/json" || contentType === "application/vnd.fluiddb.value+json");
    }

    /**
     * Given an object representing the arguments to append to a URL will return
     * an appropriately encoded string representation.
     */
    function createArgs(args) {
      var result = "";
      if(args) {
        var arg;
        for(arg in args) {
          if(typeof args[arg] !== "function") {
              if(isArray(args[arg])) {
                for(j=0; j<args[arg].length; j++) {
                  result += "&" + encodeURIComponent(arg)+"="+encodeURIComponent(args[arg][j]);
                }
              } else {
                result += "&"+encodeURIComponent(arg)+"="+encodeURIComponent(args[arg]);
              }
            }
        }
        result = "?" + result.slice(1);
      }
      return result;
    }

    /**
     * Returns an appropriate XMLHttpRequest Object depending on the browser.
     * Based upon code from here:
     * http://www.quirksmode.org/js/xmlhttp.html
     */
    function createXMLHTTPObject() {
      var XMLHttpFactories = [
        function () {return new XMLHttpRequest()},
        function () {return new ActiveXObject("Msxml2.XMLHTTP")},
        function () {return new ActiveXObject("Msxml3.XMLHTTP")},
        function () {return new ActiveXObject("Microsoft.XMLHTTP")}
      ];
      var xhr = false;
      for(var i=0; i<XMLHttpFactories.length; i++) {
        try {
          xhr = XMLHttpFactories[i]();
        } catch(e) {
          continue;
        }
        break;
      }
      return xhr;
    }

    /**
     * Sends an appropriate XMLHTTPRequest based request to Fluidinfo.
     * @param options {Object} An object containing the following named options:
     *

     *
type

     *
The request's HTTP method. [GET, POST, PUT, DELETE or HEAD]

     *
url

     *
The full URL of the resource to which the request is made.

     *
data

     *
The body of data that may be the payload of the request (in the
     *  case of POST and PUT requests).

     *
async

     *
Indicates if the request is to be asyncronous (default is true)

     *
onSuccess

     *
A function that takes the XHR request as an argument. Called upon
     *  successful completion of the request.

     *
onError

     *
A function that takes the XHR request as an argument. Called when
     *  the request resulted in an error.

     *

     *
     */
    function sendRequest(options) {
      if(isArray(options.path)) {
        options.path = encodeURL(options.path);
      }
      var method = options.type.toUpperCase() || "GET";
      var args = createArgs(options.args);
      var url = session.baseURL+options.path+args;
      var async = options.async || true;
      var xhr = createXMLHTTPObject();
      if(!xhr) return;
      xhr.open(method, url, async);
      if(authorizationToken != ""){
        xhr.setRequestHeader("Authorization", "Basic " + authorizationToken);
      };
      var contentType = detectContentType(options);
      if(contentType) {
        xhr.setRequestHeader("Content-Type", contentType);
        if(isJSONData(contentType)) {
          options.data = JSON.stringify(options.data);
        }
      }
      xhr.onreadystatechange = function() {
        if(xhr.readyState != 4) return;
        // build a simple result object
        var result = new Object();
        result.status = xhr.status;
        result.statusText = xhr.statusText;
        result.headers = xhr.responseHeaders;
        result.raw_data = xhr.responseText;
        if(isJSONData(result.headers['Content-Type'])) {
          try {
            result.data = JSON.parse(xhr.responseText);
          } catch (e) {
            // fail silently since the raw_data is still available
            // and leaving .data as undefined will help indicate the
            // de-serialization hasn't worked for the purposes of debugging.
          }
        } else {
            result.data = xhr.responseText;
        }
        result.request = xhr;
        // call the event handlers
        if(xhr.status < 300 || xhr.status == 304) {
          if(options.onSuccess){
            options.onSuccess(result);
          }
        } else if (options.onError){
          // there appears to be a problem
          options.onError(result);
        }
      }
      xhr.send(options.data)
    }

    /**
     * Contains functions to facilitate the calling of the Fluidinfo REST API.
     */
    var api = new Object();

    /**
     * Makes an HTTP GET call to the Fluidinfo API
     *
     */
    api.get = function(options){
      options.type = "GET";
      options.data = null;
      sendRequest(options);
    }

    /**
     * Makes an HTTP POST call to the Fluidinfo API
     *
     */
    api.post = function(options){
      options.type = "POST";
      sendRequest(options);
    }

    /**
     * Makes an HTTP PUT call to the Fluidinfo API
     *
     */
    api.put = function(options){
      options.type = "PUT";
      sendRequest(options);
    }

    /**
     * Makes an HTTP DELETE call to the Fluidinfo API
     *
     */
    api.delete = function(options){
      options.type = "DELETE";
      options.data = null;
      sendRequest(options);
    }

    /**
     * Makes an HTTP HEAD call to the Fluidinfo API
     *
     */
    api.head = function(options){
      options.type = "HEAD";
      options.data = null;
      sendRequest(options);
    }

    session.api = api;

    /**
     * Easily gets results from Fluidinfo.
     */
    session.query = function(options) {
      // process the options
      if(options.select === undefined) {
        throw {
          name: "ValueError",
          message: "Missing select option."
        }
      }
      if(options.where === undefined) {
        throw {
          name: "ValueError",
          message: "Missing where option."
        }
      }
      /**
       * Takes the raw result from Fluidinfo and turns it into an easy-to-use
       * array of useful objects representing the matching results then calls
       * the onSuccess function with the newly created array.
       *
       * @param {Object} The raw result from Fluidinfo that is to be processed
       */
      var processResult = function(raw) {
        result = [];
        var data = raw.data.results;
        var objectID;
        for(objectID in data.id){
          if(typeof data.id[objectID] !== "function") {
            var obj = new Object();
            obj["id"] = objectID;
            for(tag in data.id[objectID]) {
              if(typeof data.id[objectID][tag] !== "function") {
                if(data.id[objectID][tag].value !== undefined) {
                  obj[tag] = data.id[objectID][tag].value;
                } else {
                  obj[tag] = data.id[objectID][tag];
                }
              }
            }
            result[result.length] = obj;
          }
        }
        raw.data = result;
        options.onSuccess(raw);
      }
      // Make the appropriate call to Fluidinfo
      this.api.get({path: "values",
        args: {tag: options.select, query: options.where},
        onSuccess: processResult, onError: options.onError});
    }

    return session;
}
<commit_msg>Allowing exceptions raised by de-serialising JSON to bubble up<commit_after>/**
 * fluidinfo.js - a small and simple client for Fluidinfo written in Javascript.
 *
 * @author onigiri, ntoll & barshirtcliff
 * @version 0.1
 * @constructor
 * @param options {Object} Contains various parameters for the new session with
 * Fluidinfo.
 *

 *
username

 *
The username to use when authenticating with Fluidinfo.

 *
password

 *
The password to use when authenticating with Fluidinfo

 *
instance

 *
The instance to connect to. Either "main", "sandbox" or a bespoke
 *  instance. Defaults to "main".

 *

 * returns {Object} An object through which one interacts with Fluidinfo.
 */
fluidinfo = function(options) {
    /**
     * Encodes strings into base64. Adapted from:
     * http://www.webtoolkit.info/javascript-base64.html
    */
    var Base64 = {
        // private property
        _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
        // public method for encoding
        encode : function (input) {
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;
            input = Base64._utf8_encode(input);
            while (i < input.length) {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);
                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;
                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }
                output = output +
                this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
                this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
            }
            return output;
        },
        // private method for UTF-8 encoding
        _utf8_encode : function (string) {
            string = string.replace(/\r\n/g,"\n");
            var utftext = "";
            for (var n = 0; n < string.length; n++) {
                var c = string.charCodeAt(n);
                if (c < 128) {
                    utftext += String.fromCharCode(c);
                }
                else if((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
                else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
            }
            return utftext;
        }
    }

    /**
     * Represents a session with Fluidinfo.
     */
    var session = new Object();
    var authorizationToken = "";

    if(options) {
      if(options.instance) {
        switch(options.instance.toLowerCase()) {
          case "main":
            session.baseURL = "https://fluiddb.fluidinfo.com/";
            break;
          case "sandbox":
            session.baseURL = "https://sandbox.fluidinfo.com/";
            break;
          default:
              // validate the bespoke instance
              var urlRegex = /^(http|https):\/\/[\w\-_\.]+\/$/;
              if(urlRegex.exec(options.instance)) {
                session.baseURL = options.instance;
              } else {
                throw {
                  name: "ValueError",
                  message: "The URL must start with http[s]:// and have a trailing slash ('/') to be valid. E.g. https://localhost/"
                };
              }
        }
      }
      if((options.username != undefined) && (options.password != undefined)) {
        authorizationToken = Base64.encode(options.username + ":" + options.password);
      }
    }

    // Catch-all to make sure the library defaults to the main instance
    if(session.baseURL === undefined){
      session.baseURL = "https://fluiddb.fluidinfo.com/";
    }

    /**
     * Magic voodoo used to identify an array (potentially a Fluidinfo set).
     * Taken from page 61 of Doug Crockford's "Javascript: The Good Parts".
     *
     * @param value A value that might be an array.
     * @return {boolean} An indication if the value is an array.
     */
    function isArray(value) {
      return Object.prototype.toString.apply(value) === "[object Array]";
    };

    /**
     * Given a path that is expressed as an array of path elements, will
     * return the correctly encoded URL. e.g. ["a", "b", "c"] -> "/a/b/c"
     *
     * @param value {Array} A list of values to be appropriately encoded between
     * '/' values.
     * @return {string} The appropriately encoded URL.
     */
    function encodeURL(path) {
      var result = "";
      for(i=0; i<path.length; i++) {
        result += "/" + encodeURIComponent(path[i]);
      }
      return result.slice(1); // chops the leading slash
    };

    /**
     * Checks the passed value to discover if it's a Fluidinfo "primitive"
     * type. See
     * the Fluidinfo docs for more
     * information on Fluidinfo's types.
     *
     * @param value A value whose "type" needs identifying
     * @return A boolean indication if the value is a Fluidinfo primitive type.
     */
    function isPrimitive(value) {
      // check the easy type matches first
      var valueType = typeof(value);
      var primitiveTypes = ["number", "string", "boolean"];
      for(i=0; i<primitiveTypes.length; i++) {
        if(valueType === primitiveTypes[i]) {
          return true;
        }
      }
      // A null value is also a primitive
      if(value===null) {
        return true;
      }
      // check for an array (potential set) and validate it only contains
      // strings (currently multi-type arrays are not allowed)
      if(isArray(value)) {
        for(i=0; i<value.length; i++) {
          memberType = typeof(value[i]);
          if(memberType !== "string") {
            return false;
          }
        }
        return true;
      }
      // value hasn't matched any of the primitive checks
      return false;
    }

    /**
     * Given the options describing a request, will return the most appropriate
     * MIME to set as the value for the Content-Type header
     *
     * @param options {Object} Contains various parameters describing the
     * request.
     * @return the most appropriate MIME to use of null if not appropriate /
     * required.
     */
    function detectContentType(options) {
      // a "PUT" to objects/ or about/ endpoints means dealing with the MIME
      // of a tag-value. If no MIME type is passed in the options objects
      // then check if the value is a Fluidinfo primitive type. Otherwise
      // complain.
      var result = null;
      if(options.type==="PUT" && (options.url.match("^objects\/") || options.url.match("^about\/"))) {
        if(options.contentType){
          result = options.contentType;
        } else if (isPrimitive(options.data)) {
          result = "application/vnd.fluiddb.value+json";
        } else {
          throw { name: "ValueError", message: "Must supply Content-Type"};
        }
      } else if (options.data) {
        // all other requests to the API that have payloads will be passing JSON
        result = "application/json";
      }
      return result;
    }

    /**
     * Given a value from a Content-Type header, will return a boolean
     * indication if the associated content is JSON.
     *
     * @param contentType {string} The MIME value associated with the content
     * @result {boolean} An indication if the associated content is JSON
     */
    function isJSONData(contentType) {
      return contentType && (contentType === "application/json" || contentType === "application/vnd.fluiddb.value+json");
    }

    /**
     * Given an object representing the arguments to append to a URL will return
     * an appropriately encoded string representation.
     */
    function createArgs(args) {
      var result = "";
      if(args) {
        var arg;
        for(arg in args) {
          if(typeof args[arg] !== "function") {
              if(isArray(args[arg])) {
                for(j=0; j<args[arg].length; j++) {
                  result += "&" + encodeURIComponent(arg)+"="+encodeURIComponent(args[arg][j]);
                }
              } else {
                result += "&"+encodeURIComponent(arg)+"="+encodeURIComponent(args[arg]);
              }
            }
        }
        result = "?" + result.slice(1);
      }
      return result;
    }

    /**
     * Returns an appropriate XMLHttpRequest Object depending on the browser.
     * Based upon code from here:
     * http://www.quirksmode.org/js/xmlhttp.html
     */
    function createXMLHTTPObject() {
      var XMLHttpFactories = [
        function () {return new XMLHttpRequest()},
        function () {return new ActiveXObject("Msxml2.XMLHTTP")},
        function () {return new ActiveXObject("Msxml3.XMLHTTP")},
        function () {return new ActiveXObject("Microsoft.XMLHTTP")}
      ];
      var xhr = false;
      for(var i=0; i<XMLHttpFactories.length; i++) {
        try {
          xhr = XMLHttpFactories[i]();
        } catch(e) {
          continue;
        }
        break;
      }
      return xhr;
    }

    /**
     * Sends an appropriate XMLHTTPRequest based request to Fluidinfo.
     * @param options {Object} An object containing the following named options:
     *

     *
type

     *
The request's HTTP method. [GET, POST, PUT, DELETE or HEAD]

     *
url

     *
The full URL of the resource to which the request is made.

     *
data

     *
The body of data that may be the payload of the request (in the
     *  case of POST and PUT requests).

     *
async

     *
Indicates if the request is to be asyncronous (default is true)

     *
onSuccess

     *
A function that takes the XHR request as an argument. Called upon
     *  successful completion of the request.

     *
onError

     *
A function that takes the XHR request as an argument. Called when
     *  the request resulted in an error.

     *

     *
     */
    function sendRequest(options) {
      if(isArray(options.path)) {
        options.path = encodeURL(options.path);
      }
      var method = options.type.toUpperCase() || "GET";
      var args = createArgs(options.args);
      var url = session.baseURL+options.path+args;
      var async = options.async || true;
      var xhr = createXMLHTTPObject();
      if(!xhr) return;
      xhr.open(method, url, async);
      if(authorizationToken != ""){
        xhr.setRequestHeader("Authorization", "Basic " + authorizationToken);
      };
      var contentType = detectContentType(options);
      if(contentType) {
        xhr.setRequestHeader("Content-Type", contentType);
        if(isJSONData(contentType)) {
          options.data = JSON.stringify(options.data);
        }
      }
      xhr.onreadystatechange = function() {
        if(xhr.readyState != 4) return;
        // build a simple result object
        var result = new Object();
        result.status = xhr.status;
        result.statusText = xhr.statusText;
        result.headers = xhr.responseHeaders;
        result.raw_data = xhr.responseText;
        if(isJSONData(result.headers['Content-Type'])) {
          result.data = JSON.parse(xhr.responseText);
        } else {
          result.data = xhr.responseText;
        }
        result.request = xhr;
        // call the event handlers
        if(xhr.status < 300 || xhr.status == 304) {
          if(options.onSuccess){
            options.onSuccess(result);
          }
        } else if (options.onError){
          // there appears to be a problem
          options.onError(result);
        }
      }
      xhr.send(options.data)
    }

    /**
     * Contains functions to facilitate the calling of the Fluidinfo REST API.
     */
    var api = new Object();

    /**
     * Makes an HTTP GET call to the Fluidinfo API
     *
     */
    api.get = function(options){
      options.type = "GET";
      options.data = null;
      sendRequest(options);
    }

    /**
     * Makes an HTTP POST call to the Fluidinfo API
     *
     */
    api.post = function(options){
      options.type = "POST";
      sendRequest(options);
    }

    /**
     * Makes an HTTP PUT call to the Fluidinfo API
     *
     */
    api.put = function(options){
      options.type = "PUT";
      sendRequest(options);
    }

    /**
     * Makes an HTTP DELETE call to the Fluidinfo API
     *
     */
    api.delete = function(options){
      options.type = "DELETE";
      options.data = null;
      sendRequest(options);
    }

    /**
     * Makes an HTTP HEAD call to the Fluidinfo API
     *
     */
    api.head = function(options){
      options.type = "HEAD";
      options.data = null;
      sendRequest(options);
    }

    session.api = api;

    /**
     * Easily gets results from Fluidinfo.
     */
    session.query = function(options) {
      // process the options
      if(options.select === undefined) {
        throw {
          name: "ValueError",
          message: "Missing select option."
        }
      }
      if(options.where === undefined) {
        throw {
          name: "ValueError",
          message: "Missing where option."
        }
      }
      /**
       * Takes the raw result from Fluidinfo and turns it into an easy-to-use
       * array of useful objects representing the matching results then calls
       * the onSuccess function with the newly created array.
       *
       * @param {Object} The raw result from Fluidinfo that is to be processed
       */
      var processResult = function(raw) {
        result = [];
        var data = raw.data.results;
        var objectID;
        for(objectID in data.id){
          if(typeof data.id[objectID] !== "function") {
            var obj = new Object();
            obj["id"] = objectID;
            for(tag in data.id[objectID]) {
              if(typeof data.id[objectID][tag] !== "function") {
                if(data.id[objectID][tag].value !== undefined) {
                  obj[tag] = data.id[objectID][tag].value;
                } else {
                  obj[tag] = data.id[objectID][tag];
                }
              }
            }
            result[result.length] = obj;
          }
        }
        raw.data = result;
        options.onSuccess(raw);
      }
      // Make the appropriate call to Fluidinfo
      this.api.get({path: "values",
        args: {tag: options.select, query: options.where},
        onSuccess: processResult, onError: options.onError});
    }

    return session;
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name         GameFAQs Move Message Options
// @namespace    gfaqs_movoptions
// @version      1.4
// @description  Moves the links for delete, close, edit, report to post header
// @author       Littlegator
// @grant        none
// @downloadURL http://gates.coffee/gfaqs_move_msg_options.user.js
// @include http://www.gamefaqs.com/boards/*
// ==/UserScript==

/*
    The MIT License (MIT)

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.

*/

var allPosts = document.querySelectorAll ("td.msg");

for (var J = allPosts.length - 1;  J >= 0;  --J) {
    if(allPosts[J].querySelector("div.msg_body_box")) {
        var curOptions = allPosts[J].querySelector("span.options");
        var curInfoBox = allPosts[J].querySelector("div.msg_infobox");

        curInfoBox.appendChild(curOptions);
    }

    var msgBelow = allPosts[J].querySelector('div.msg_below');

    if(msgBelow) {
        msgBelow.parentNode.removeChild(msgBelow);
    }
}

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

addGlobalStyle('span.postaction { margin-right: 5px ! important; }');
addGlobalStyle('span.options { float: right ! important; }');
addGlobalStyle('div.newbeta { margin-top: 3px ! important; }');
addGlobalStyle('div.newbeta { margin-bottom: 3px ! important; }');
<commit_msg>forgot to update script ver<commit_after>// ==UserScript==
// @name         GameFAQs Move Message Options
// @namespace    gfaqs_movoptions
// @version      1.5
// @description  Moves the links for delete, close, edit, report to post header
// @author       Littlegator
// @grant        none
// @downloadURL http://gates.coffee/gfaqs_move_msg_options.user.js
// @include http://www.gamefaqs.com/boards/*
// ==/UserScript==

/*
    The MIT License (MIT)

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.

*/

var allPosts = document.querySelectorAll ("td.msg");

for (var J = allPosts.length - 1;  J >= 0;  --J) {
    if(allPosts[J].querySelector("div.msg_body_box")) {
        var curOptions = allPosts[J].querySelector("span.options");
        var curInfoBox = allPosts[J].querySelector("div.msg_infobox");

        curInfoBox.appendChild(curOptions);
    }

    var msgBelow = allPosts[J].querySelector('div.msg_below');

    if(msgBelow) {
        msgBelow.parentNode.removeChild(msgBelow);
    }
}

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

addGlobalStyle('span.postaction { margin-right: 5px ! important; }');
addGlobalStyle('span.options { float: right ! important; }');
addGlobalStyle('div.newbeta { margin-top: 3px ! important; }');
addGlobalStyle('div.newbeta { margin-bottom: 3px ! important; }');
<|endoftext|>


<commit_before>/*
    subtask:
        Logic for quickAlgo tasks, implements the Bebras task API.
*/

var initBlocklySubTask = function(subTask, language) {
   // Blockly tasks need to always have the level-specific behavior from
   // beaver-task-2.0
   subTask.assumeLevels = true;

   if (window.forcedLevel != null) {
      for (var level in subTask.data) {
         if (window.forcedLevel != level) {
            subTask.data[level] = undefined;
         }
      }
      subTask.load = function(views, callback) {
         subTask.loadLevel(window.forcedLevel);
         callback();
      };
   } else if (subTask.data["medium"] == undefined) {
      subTask.load = function(views, callback) {
         subTask.loadLevel("easy");
         callback();
      };
   }

   if (language == undefined) {
      language = "fr";
   }

   subTask.loadLevel = function(curLevel) {
      subTask.levelGridInfos = extractLevelSpecific(subTask.gridInfos, curLevel);

      subTask.blocklyHelper = getBlocklyHelper(subTask.levelGridInfos.maxInstructions);
      subTask.answer = null;
      subTask.state = {};
      subTask.iTestCase = 0;
      if(!window.taskResultsCache) {
         window.taskResultsCache = {};
      }
      if(!window.taskResultsCache[curLevel]) {
         window.taskResultsCache[curLevel] = {};
      }

      this.level = curLevel;

      // TODO: fix bebras platform to make this unnecessary
      try {
         $('#question-iframe', window.parent.document).css('width', '100%');
      } catch(e) {
      }
      $('body').css("width", "100%").addClass('blockly');
      window.focus();

      this.iTestCase = 0;
      this.nbTestCases = subTask.data[curLevel].length;

      this.context = quickAlgoLibraries.getContext(this.display, subTask.levelGridInfos, curLevel);
      this.context.raphaelFactory = this.raphaelFactory;
      this.context.delayFactory = this.delayFactory;
      this.context.blocklyHelper = this.blocklyHelper;

      if (this.display) {
        window.quickAlgoInterface.loadInterface(this.context);
        window.quickAlgoInterface.setOptions({
           hideSaveOrLoad: subTask.levelGridInfos.hideSaveOrLoad,
           hasExample: subTask.levelGridInfos.example && subTask.levelGridInfos.example[subTask.blocklyHelper.language],
           conceptViewer: subTask.levelGridInfos.conceptViewer,
           conceptViewerLang: this.blocklyHelper.language,
           hideLoadBestAnswer: subTask.levelGridInfos.hideLoadBestAnswer
           });
      }

      this.blocklyHelper.loadContext(this.context);

      //this.answer = task.getDefaultAnswerObject();
      displayHelper.hideValidateButton = true;
      displayHelper.timeoutMinutes = 30;

      var curIncludeBlocks = extractLevelSpecific(this.context.infos.includeBlocks, curLevel);

      // Load concepts into conceptViewer; must be done before loading
      // Blockly/Scratch, as scratch-mode will modify includeBlocks
      if(this.display && subTask.levelGridInfos.conceptViewer) {
         // TODO :: testConcepts is temporary-ish
         if(this.context.conceptList) {
            var allConcepts = testConcepts.concat(this.context.conceptList);
         } else {
            var allConcepts = testConcepts;
         }

         var concepts = window.getConceptsFromBlocks(curIncludeBlocks, allConcepts, this.context);
         if(subTask.levelGridInfos.conceptViewer.length) {
            concepts = concepts.concat(subTask.levelGridInfos.conceptViewer);
         }
         concepts = window.conceptsFill(concepts, allConcepts);
         window.conceptViewer.loadConcepts(concepts);
      }

      this.blocklyHelper.setIncludeBlocks(curIncludeBlocks);

      var blocklyOptions = {
         readOnly: !!subTask.taskParams.readOnly,
         defaultCode: subTask.defaultCode
      };

      this.blocklyHelper.load(stringsLanguage, this.display, this.data[curLevel].length, blocklyOptions);

      if(this.display) {
         window.quickAlgoInterface.initTestSelector(this.nbTestCases);
      }

      subTask.changeTest(0);

      // TODO :: find a way to not have to do that?
      setTimeout(function() {
        subTask.blocklyHelper.reload();
        }, 100);
   };

   subTask.updateScale = function() {
      this.context.updateScale();
      this.blocklyHelper.updateSize();
   };

   var resetScores = function() {
   };

   var updateScores = function() {
   };

   function changeScore(robot, deltaScore) {
      scores[robot] += deltaScore;
      updateScores();
   };

   subTask.unloadLevel = function(callback) {
      if(this.display) {
         window.quickAlgoInterface.unloadLevel();
      }
      this.context.unload();
      this.blocklyHelper.unloadLevel();
      callback();
   };

   subTask.unload = function(callback) {
      var that = this;
      subTask.unloadLevel(function () {
         that.blocklyHelper.unload();
         callback();
      });
   };

   subTask.reset = function() {
      this.context.reset();
   };

   subTask.program_end = function(callback) {
      this.context.program_end(callback);
   };

   var initContextForLevel = function(iTestCase) {
      subTask.iTestCase = iTestCase;
      subTask.context.reset(subTask.data[subTask.level][iTestCase]);
      subTask.context.iTestCase = iTestCase;
      subTask.context.nbTestCases = subTask.nbTestCases;
      //      var prefix = "Test " + (subTask.iTestCase + 1) + "/" + subTask.nbTestCases + " : ";
      subTask.context.messagePrefixFailure = '';
      subTask.context.messagePrefixSuccess = '';
      subTask.context.linkBack = false;
   };

   subTask.initRun = function(callback) {
      initBlocklyRunner(subTask.context, function(message, success) {
         function handleResults(results) {
            subTask.context.display = true;
            if(callback) {
               callback(message, success);
            } else if(results.successRate >= 1) {
               // All tests passed, request validate from the platform
               platform.validate("done");
            }
            if(results.successRate < 1) {
               // Display the execution message as it won't be shown through
               // validate
               window.quickAlgoInterface.displayError(''+message+'');
            }
         }
         // Launch an evaluation after the execution
         subTask.context.display = false;
         subTask.getGrade(handleResults, true, subTask.iTestCase);
      });
      initContextForLevel(subTask.iTestCase);
   };

   subTask.run = function(callback) {
      subTask.initRun(callback);
      subTask.blocklyHelper.run(subTask.context);
   };

   subTask.submit = function() {
      this.stop();
      this.context.display = false;
      this.getAnswerObject(); // to fill this.answer;

      $('#displayHelper_graderMessage').html('
' + languageStrings.gradingInProgress + '
');

      this.getGrade(function(result) {
         $('#displayHelper_graderMessage').html("");
         subTask.context.display = true;
         initBlocklyRunner(subTask.context, function(message, success) {
            window.quickAlgoInterface.displayError(''+message+'');
            platform.validate("done");
         });
         subTask.changeTest(result.iTestCase - subTask.iTestCase);
         initContextForLevel(result.iTestCase);
         subTask.context.linkBack = true;
         subTask.context.messagePrefixSuccess = window.languageStrings.allTests;
         subTask.blocklyHelper.run(subTask.context);
      }, true);
   };

   subTask.step = function () {
      subTask.context.changeDelay(200);
      this.initRun();
      subTask.blocklyHelper.step(subTask.context);
   };

   subTask.stop = function() {
      if(this.context.runner) {
         this.context.runner.stop();
      }
      window.quickAlgoInterface.displayError(null);
      this.context.reset();
   };

   subTask.reloadStateObject = function(stateObj) {
      this.state = stateObj;
//      this.level = state.level;

//      initContextForLevel(this.level);

//      this.context.runner.stop();
   };

   subTask.loadExample = function(exampleObj) {
      subTask.blocklyHelper.loadExample(exampleObj ? exampleObj : subTask.levelGridInfos.example);
   };

   subTask.getDefaultStateObject = function() {
      return { level: "easy" };
   };

   subTask.getStateObject = function() {
      this.state.level = this.level;
      return this.state;
   };

   subTask.changeSpeed = function(speed) {
      this.context.changeDelay(speed);
      if ((this.context.runner === undefined) || !this.context.runner.isRunning()) {
         this.run();
      } else if (this.context.runner.stepMode) {
         this.context.runner.run();
      }
   };

   // used in new playback controls with speed slider
   subTask.setStepDelay = function(delay) {
    this.context.changeDelay(delay);
   };

   // used in new playback controls with speed slider
   subTask.pause = function() {
    if(this.context.runner) {
      this.context.runner.stepMode = true;
    }
   };

   // used in new playback controls with speed slider
   subTask.play = function() {
    if ((this.context.runner === undefined) || !this.context.runner.isRunning()) {
      this.run();
    } else if (this.context.runner.stepMode) {
      this.context.runner.run();
    }
   };

   subTask.getAnswerObject = function() {
      this.blocklyHelper.savePrograms();

      this.answer = this.blocklyHelper.programs;
      return this.answer;
   };

   subTask.reloadAnswerObject = function(answerObj) {
      if(typeof answerObj == "undefined") {
        this.answer = this.getDefaultAnswerObject();
      } else {
        this.answer = answerObj;
      }
      this.blocklyHelper.programs = this.answer;
      if (this.answer != undefined) {
         this.blocklyHelper.loadPrograms();
      }
   };

   subTask.getDefaultAnswerObject = function() {
      var defaultBlockly = this.blocklyHelper.getDefaultContent();
      return [{javascript:"", blockly: defaultBlockly, blocklyJS: ""}];
   };

   subTask.changeTest = function(delta) {
      var newTest = subTask.iTestCase + delta;
      if ((newTest >= 0) && (newTest < this.nbTestCases)) {
         if(this.context.runner) {
            this.context.runner.stop();
         }
         window.quickAlgoInterface.displayError(null);
         initContextForLevel(newTest);
         if(subTask.context.display) {
            window.quickAlgoInterface.updateTestSelector(newTest);
         }
      }
   };

   subTask.changeTestTo = function(iTest) {
      var delta = iTest - subTask.iTestCase;
      if(delta != 0) {
         subTask.changeTest(delta);
      }
   };

   subTask.getGrade = function(callback, display, mainTestCase) {
      // mainTest : set to indicate the first iTestCase to test (typically,
      // current iTestCase) before others; test will then stop if the
      if(subTask.context.infos && subTask.context.infos.hideValidate) {
         // There's no validation
         callback({
            message: '',
            successRate: 1,
            iTestCase: 0
            });
         return;
      }

      var oldDelay = subTask.context.infos.actionDelay;
      subTask.context.changeDelay(0);
      var code = subTask.blocklyHelper.getCodeFromXml(subTask.answer[0].blockly, "javascript");
      code = subTask.blocklyHelper.getFullCode(code);

      var checkError = '';
      var checkDisplay = function(err) { checkError = err; }
      if(!subTask.blocklyHelper.checkCode(code, checkDisplay)) {
         var results = {
            message: checkError,
            successRate: 0,
            iTestCase: 0
         };
         subTask.context.changeDelay(oldDelay);
         callback(results);
         return;
      }

      var codes = [code]; // We only ever send one code to grade
      var oldTestCase = subTask.iTestCase;

/*      var levelResultsCache = window.taskResultsCache[this.level];

      if(levelResultsCache[code]) {
         // We already have a cached result for that
         window.quickAlgoInterface.updateTestScores(levelResultsCache[code].fullResults);
         subTask.context.changeDelay(oldDelay);
         callback(levelResultsCache[code].results);
         return;
      }*/

      function startEval() {
         // Start evaluation on iTestCase
         initContextForLevel(subTask.iTestCase);
         subTask.testCaseResults[subTask.iTestCase] = {evaluating: true};
         if(display) {
            window.quickAlgoInterface.updateTestScores(subTask.testCaseResults);
         }
         subTask.context.runner.runCodes(codes);
      }

      function postEval() {
         // Behavior after an eval
         if(typeof mainTestCase == 'undefined') {
            // Normal behavior : evaluate all tests
            subTask.iTestCase++;
            if (subTask.iTestCase < subTask.nbTestCases) {
               startEval();
               return;
            }
         } else if(subTask.testCaseResults[subTask.iTestCase].successRate >= 1) {
            // A mainTestCase is defined, evaluate mainTestCase first then the
            // others until a test fails
            if(subTask.iTestCase == mainTestCase && subTask.iTestCase != 0) {
               subTask.iTestCase = 0;
               startEval();
               return;
            }
            subTask.iTestCase++;
            if(subTask.iTestCase == mainTestCase) { subTask.iTestCase++ }; // Already done
            if (subTask.iTestCase < subTask.nbTestCases) {
               startEval();
               return;
            }
         }

         // All evaluations done, tally results
         subTask.iTestCase = oldTestCase;
         if(typeof mainTestCase == 'undefined') {
            var iWorstTestCase = 0;
            var worstRate = 1;
         } else {
            // Priority to the mainTestCase if worst test case
            var iWorstTestCase = mainTestCase;
            var worstRate = subTask.testCaseResults[mainTestCase].successRate;
            // Change back to the mainTestCase
         }
         var nbSuccess = 0;
         for (var iCase = 0; iCase < subTask.nbTestCases; iCase++) {
            var sr = subTask.testCaseResults[iCase] ? subTask.testCaseResults[iCase].successRate : 0;
            if(sr >= 1) {
               nbSuccess++;
            }
            if(sr < worstRate) {
               worstRate = sr;
               iWorstTestCase = iCase;
            }
         }
         subTask.testCaseResults[iWorstTestCase].iTestCase = iWorstTestCase;
         if(display) {
            window.quickAlgoInterface.updateTestScores(subTask.testCaseResults);
         }
         if(subTask.testCaseResults[iWorstTestCase].successRate < 1) {
            if(subTask.nbTestCases == 1) {
               var msg = subTask.testCaseResults[iWorstTestCase].message;
            } else if(nbSuccess > 0) {
               var msg = languageStrings.resultsPartialSuccess.format({
                  nbSuccess: nbSuccess,
                  nbTests: subTask.nbTestCases
                  });
            } else {
               var msg = languageStrings.resultsNoSuccess;
            }
            var results = {
               message: msg,
               successRate: subTask.testCaseResults[iWorstTestCase].successRate,
               iTestCase: iWorstTestCase
            };
         } else {
            var results = subTask.testCaseResults[iWorstTestCase];
         }
         /*levelResultsCache[code] = {
            results: results,
            fullResults: subTask.testCaseResults
            };*/
         subTask.context.changeDelay(oldDelay);
         callback(results);
      }

      initBlocklyRunner(subTask.context, function(message, success) {
         // Record grade from this evaluation into testCaseResults
         var computeGrade = function(context, message) {
            var rate = 0;
            if (context.success) {
               rate = 1;
            }
            return {
               successRate: rate,
               message: message
            };
         }
         if (subTask.levelGridInfos.computeGrade != undefined) {
            computeGrade = subTask.levelGridInfos.computeGrade;
         }
         subTask.testCaseResults[subTask.iTestCase] = computeGrade(subTask.context, message)
         postEval();
      });

      subTask.iTestCase = typeof mainTestCase != 'undefined' ? mainTestCase : 0;
      subTask.testCaseResults = [];
      for(var i=0; i < subTask.iTestCase; i++) {
         // Fill testCaseResults up to the first iTestCase
         subTask.testCaseResults.push(null);
      }
      subTask.context.linkBack = true;
      subTask.context.messagePrefixSuccess = window.languageStrings.allTests;

      startEval();
   };
}
<commit_msg>quickAlgo: Fix step-by-step init<commit_after>/*
    subtask:
        Logic for quickAlgo tasks, implements the Bebras task API.
*/

var initBlocklySubTask = function(subTask, language) {
   // Blockly tasks need to always have the level-specific behavior from
   // beaver-task-2.0
   subTask.assumeLevels = true;

   if (window.forcedLevel != null) {
      for (var level in subTask.data) {
         if (window.forcedLevel != level) {
            subTask.data[level] = undefined;
         }
      }
      subTask.load = function(views, callback) {
         subTask.loadLevel(window.forcedLevel);
         callback();
      };
   } else if (subTask.data["medium"] == undefined) {
      subTask.load = function(views, callback) {
         subTask.loadLevel("easy");
         callback();
      };
   }

   if (language == undefined) {
      language = "fr";
   }

   subTask.loadLevel = function(curLevel) {
      subTask.levelGridInfos = extractLevelSpecific(subTask.gridInfos, curLevel);

      subTask.blocklyHelper = getBlocklyHelper(subTask.levelGridInfos.maxInstructions);
      subTask.answer = null;
      subTask.state = {};
      subTask.iTestCase = 0;
      if(!window.taskResultsCache) {
         window.taskResultsCache = {};
      }
      if(!window.taskResultsCache[curLevel]) {
         window.taskResultsCache[curLevel] = {};
      }

      this.level = curLevel;

      // TODO: fix bebras platform to make this unnecessary
      try {
         $('#question-iframe', window.parent.document).css('width', '100%');
      } catch(e) {
      }
      $('body').css("width", "100%").addClass('blockly');
      window.focus();

      this.iTestCase = 0;
      this.nbTestCases = subTask.data[curLevel].length;

      this.context = quickAlgoLibraries.getContext(this.display, subTask.levelGridInfos, curLevel);
      this.context.raphaelFactory = this.raphaelFactory;
      this.context.delayFactory = this.delayFactory;
      this.context.blocklyHelper = this.blocklyHelper;

      if (this.display) {
        window.quickAlgoInterface.loadInterface(this.context);
        window.quickAlgoInterface.setOptions({
           hideSaveOrLoad: subTask.levelGridInfos.hideSaveOrLoad,
           hasExample: subTask.levelGridInfos.example && subTask.levelGridInfos.example[subTask.blocklyHelper.language],
           conceptViewer: subTask.levelGridInfos.conceptViewer,
           conceptViewerLang: this.blocklyHelper.language,
           hideLoadBestAnswer: subTask.levelGridInfos.hideLoadBestAnswer
           });
      }

      this.blocklyHelper.loadContext(this.context);

      //this.answer = task.getDefaultAnswerObject();
      displayHelper.hideValidateButton = true;
      displayHelper.timeoutMinutes = 30;

      var curIncludeBlocks = extractLevelSpecific(this.context.infos.includeBlocks, curLevel);

      // Load concepts into conceptViewer; must be done before loading
      // Blockly/Scratch, as scratch-mode will modify includeBlocks
      if(this.display && subTask.levelGridInfos.conceptViewer) {
         // TODO :: testConcepts is temporary-ish
         if(this.context.conceptList) {
            var allConcepts = testConcepts.concat(this.context.conceptList);
         } else {
            var allConcepts = testConcepts;
         }

         var concepts = window.getConceptsFromBlocks(curIncludeBlocks, allConcepts, this.context);
         if(subTask.levelGridInfos.conceptViewer.length) {
            concepts = concepts.concat(subTask.levelGridInfos.conceptViewer);
         }
         concepts = window.conceptsFill(concepts, allConcepts);
         window.conceptViewer.loadConcepts(concepts);
      }

      this.blocklyHelper.setIncludeBlocks(curIncludeBlocks);

      var blocklyOptions = {
         readOnly: !!subTask.taskParams.readOnly,
         defaultCode: subTask.defaultCode
      };

      this.blocklyHelper.load(stringsLanguage, this.display, this.data[curLevel].length, blocklyOptions);

      if(this.display) {
         window.quickAlgoInterface.initTestSelector(this.nbTestCases);
      }

      subTask.changeTest(0);

      // TODO :: find a way to not have to do that?
      setTimeout(function() {
        subTask.blocklyHelper.reload();
        }, 100);
   };

   subTask.updateScale = function() {
      this.context.updateScale();
      this.blocklyHelper.updateSize();
   };

   var resetScores = function() {
   };

   var updateScores = function() {
   };

   function changeScore(robot, deltaScore) {
      scores[robot] += deltaScore;
      updateScores();
   };

   subTask.unloadLevel = function(callback) {
      if(this.display) {
         window.quickAlgoInterface.unloadLevel();
      }
      this.context.unload();
      this.blocklyHelper.unloadLevel();
      callback();
   };

   subTask.unload = function(callback) {
      var that = this;
      subTask.unloadLevel(function () {
         that.blocklyHelper.unload();
         callback();
      });
   };

   subTask.reset = function() {
      this.context.reset();
   };

   subTask.program_end = function(callback) {
      this.context.program_end(callback);
   };

   var initContextForLevel = function(iTestCase) {
      subTask.iTestCase = iTestCase;
      subTask.context.reset(subTask.data[subTask.level][iTestCase]);
      subTask.context.iTestCase = iTestCase;
      subTask.context.nbTestCases = subTask.nbTestCases;
      //      var prefix = "Test " + (subTask.iTestCase + 1) + "/" + subTask.nbTestCases + " : ";
      subTask.context.messagePrefixFailure = '';
      subTask.context.messagePrefixSuccess = '';
      subTask.context.linkBack = false;
   };

   subTask.initRun = function(callback) {
      initBlocklyRunner(subTask.context, function(message, success) {
         function handleResults(results) {
            subTask.context.display = true;
            if(callback) {
               callback(message, success);
            } else if(results.successRate >= 1) {
               // All tests passed, request validate from the platform
               platform.validate("done");
            }
            if(results.successRate < 1) {
               // Display the execution message as it won't be shown through
               // validate
               window.quickAlgoInterface.displayError(''+message+'');
            }
         }
         // Launch an evaluation after the execution
         subTask.context.display = false;
         subTask.getGrade(handleResults, true, subTask.iTestCase);
      });
      initContextForLevel(subTask.iTestCase);
   };

   subTask.run = function(callback) {
      subTask.initRun(callback);
      subTask.blocklyHelper.run(subTask.context);
   };

   subTask.submit = function() {
      this.stop();
      this.context.display = false;
      this.getAnswerObject(); // to fill this.answer;

      $('#displayHelper_graderMessage').html('
' + languageStrings.gradingInProgress + '
');

      this.getGrade(function(result) {
         $('#displayHelper_graderMessage').html("");
         subTask.context.display = true;
         initBlocklyRunner(subTask.context, function(message, success) {
            window.quickAlgoInterface.displayError(''+message+'');
            platform.validate("done");
         });
         subTask.changeTest(result.iTestCase - subTask.iTestCase);
         initContextForLevel(result.iTestCase);
         subTask.context.linkBack = true;
         subTask.context.messagePrefixSuccess = window.languageStrings.allTests;
         subTask.blocklyHelper.run(subTask.context);
      }, true);
   };

   subTask.step = function () {
      subTask.context.changeDelay(200);
      if ((this.context.runner === undefined) || !this.context.runner.isRunning()) {
        this.initRun();
      }
      subTask.blocklyHelper.step(subTask.context);
   };

   subTask.stop = function() {
      if(this.context.runner) {
         this.context.runner.stop();
      }
      window.quickAlgoInterface.displayError(null);
      this.context.reset();
   };

   subTask.reloadStateObject = function(stateObj) {
      this.state = stateObj;
//      this.level = state.level;

//      initContextForLevel(this.level);

//      this.context.runner.stop();
   };

   subTask.loadExample = function(exampleObj) {
      subTask.blocklyHelper.loadExample(exampleObj ? exampleObj : subTask.levelGridInfos.example);
   };

   subTask.getDefaultStateObject = function() {
      return { level: "easy" };
   };

   subTask.getStateObject = function() {
      this.state.level = this.level;
      return this.state;
   };

   subTask.changeSpeed = function(speed) {
      this.context.changeDelay(speed);
      if ((this.context.runner === undefined) || !this.context.runner.isRunning()) {
         this.run();
      } else if (this.context.runner.stepMode) {
         this.context.runner.run();
      }
   };

   // used in new playback controls with speed slider
   subTask.setStepDelay = function(delay) {
    this.context.changeDelay(delay);
   };

   // used in new playback controls with speed slider
   subTask.pause = function() {
    if(this.context.runner) {
      this.context.runner.stepMode = true;
    }
   };

   // used in new playback controls with speed slider
   subTask.play = function() {
    if ((this.context.runner === undefined) || !this.context.runner.isRunning()) {
      this.run();
    } else if (this.context.runner.stepMode) {
      this.context.runner.run();
    }
   };

   subTask.getAnswerObject = function() {
      this.blocklyHelper.savePrograms();

      this.answer = this.blocklyHelper.programs;
      return this.answer;
   };

   subTask.reloadAnswerObject = function(answerObj) {
      if(typeof answerObj == "undefined") {
        this.answer = this.getDefaultAnswerObject();
      } else {
        this.answer = answerObj;
      }
      this.blocklyHelper.programs = this.answer;
      if (this.answer != undefined) {
         this.blocklyHelper.loadPrograms();
      }
   };

   subTask.getDefaultAnswerObject = function() {
      var defaultBlockly = this.blocklyHelper.getDefaultContent();
      return [{javascript:"", blockly: defaultBlockly, blocklyJS: ""}];
   };

   subTask.changeTest = function(delta) {
      var newTest = subTask.iTestCase + delta;
      if ((newTest >= 0) && (newTest < this.nbTestCases)) {
         if(this.context.runner) {
            this.context.runner.stop();
         }
         window.quickAlgoInterface.displayError(null);
         initContextForLevel(newTest);
         if(subTask.context.display) {
            window.quickAlgoInterface.updateTestSelector(newTest);
         }
      }
   };

   subTask.changeTestTo = function(iTest) {
      var delta = iTest - subTask.iTestCase;
      if(delta != 0) {
         subTask.changeTest(delta);
      }
   };

   subTask.getGrade = function(callback, display, mainTestCase) {
      // mainTest : set to indicate the first iTestCase to test (typically,
      // current iTestCase) before others; test will then stop if the
      if(subTask.context.infos && subTask.context.infos.hideValidate) {
         // There's no validation
         callback({
            message: '',
            successRate: 1,
            iTestCase: 0
            });
         return;
      }

      var oldDelay = subTask.context.infos.actionDelay;
      subTask.context.changeDelay(0);
      var code = subTask.blocklyHelper.getCodeFromXml(subTask.answer[0].blockly, "javascript");
      code = subTask.blocklyHelper.getFullCode(code);

      var checkError = '';
      var checkDisplay = function(err) { checkError = err; }
      if(!subTask.blocklyHelper.checkCode(code, checkDisplay)) {
         var results = {
            message: checkError,
            successRate: 0,
            iTestCase: 0
         };
         subTask.context.changeDelay(oldDelay);
         callback(results);
         return;
      }

      var codes = [code]; // We only ever send one code to grade
      var oldTestCase = subTask.iTestCase;

/*      var levelResultsCache = window.taskResultsCache[this.level];

      if(levelResultsCache[code]) {
         // We already have a cached result for that
         window.quickAlgoInterface.updateTestScores(levelResultsCache[code].fullResults);
         subTask.context.changeDelay(oldDelay);
         callback(levelResultsCache[code].results);
         return;
      }*/

      function startEval() {
         // Start evaluation on iTestCase
         initContextForLevel(subTask.iTestCase);
         subTask.testCaseResults[subTask.iTestCase] = {evaluating: true};
         if(display) {
            window.quickAlgoInterface.updateTestScores(subTask.testCaseResults);
         }
         subTask.context.runner.runCodes(codes);
      }

      function postEval() {
         // Behavior after an eval
         if(typeof mainTestCase == 'undefined') {
            // Normal behavior : evaluate all tests
            subTask.iTestCase++;
            if (subTask.iTestCase < subTask.nbTestCases) {
               startEval();
               return;
            }
         } else if(subTask.testCaseResults[subTask.iTestCase].successRate >= 1) {
            // A mainTestCase is defined, evaluate mainTestCase first then the
            // others until a test fails
            if(subTask.iTestCase == mainTestCase && subTask.iTestCase != 0) {
               subTask.iTestCase = 0;
               startEval();
               return;
            }
            subTask.iTestCase++;
            if(subTask.iTestCase == mainTestCase) { subTask.iTestCase++ }; // Already done
            if (subTask.iTestCase < subTask.nbTestCases) {
               startEval();
               return;
            }
         }

         // All evaluations done, tally results
         subTask.iTestCase = oldTestCase;
         if(typeof mainTestCase == 'undefined') {
            var iWorstTestCase = 0;
            var worstRate = 1;
         } else {
            // Priority to the mainTestCase if worst test case
            var iWorstTestCase = mainTestCase;
            var worstRate = subTask.testCaseResults[mainTestCase].successRate;
            // Change back to the mainTestCase
         }
         var nbSuccess = 0;
         for (var iCase = 0; iCase < subTask.nbTestCases; iCase++) {
            var sr = subTask.testCaseResults[iCase] ? subTask.testCaseResults[iCase].successRate : 0;
            if(sr >= 1) {
               nbSuccess++;
            }
            if(sr < worstRate) {
               worstRate = sr;
               iWorstTestCase = iCase;
            }
         }
         subTask.testCaseResults[iWorstTestCase].iTestCase = iWorstTestCase;
         if(display) {
            window.quickAlgoInterface.updateTestScores(subTask.testCaseResults);
         }
         if(subTask.testCaseResults[iWorstTestCase].successRate < 1) {
            if(subTask.nbTestCases == 1) {
               var msg = subTask.testCaseResults[iWorstTestCase].message;
            } else if(nbSuccess > 0) {
               var msg = languageStrings.resultsPartialSuccess.format({
                  nbSuccess: nbSuccess,
                  nbTests: subTask.nbTestCases
                  });
            } else {
               var msg = languageStrings.resultsNoSuccess;
            }
            var results = {
               message: msg,
               successRate: subTask.testCaseResults[iWorstTestCase].successRate,
               iTestCase: iWorstTestCase
            };
         } else {
            var results = subTask.testCaseResults[iWorstTestCase];
         }
         /*levelResultsCache[code] = {
            results: results,
            fullResults: subTask.testCaseResults
            };*/
         subTask.context.changeDelay(oldDelay);
         callback(results);
      }

      initBlocklyRunner(subTask.context, function(message, success) {
         // Record grade from this evaluation into testCaseResults
         var computeGrade = function(context, message) {
            var rate = 0;
            if (context.success) {
               rate = 1;
            }
            return {
               successRate: rate,
               message: message
            };
         }
         if (subTask.levelGridInfos.computeGrade != undefined) {
            computeGrade = subTask.levelGridInfos.computeGrade;
         }
         subTask.testCaseResults[subTask.iTestCase] = computeGrade(subTask.context, message)
         postEval();
      });

      subTask.iTestCase = typeof mainTestCase != 'undefined' ? mainTestCase : 0;
      subTask.testCaseResults = [];
      for(var i=0; i < subTask.iTestCase; i++) {
         // Fill testCaseResults up to the first iTestCase
         subTask.testCaseResults.push(null);
      }
      subTask.context.linkBack = true;
      subTask.context.messagePrefixSuccess = window.languageStrings.allTests;

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


<commit_before>/**
 * 选择列表
 *
 * Created by GUY on 2015/11/1.
 * @class BI.SelectList
 * @extends BI.Widget
 */
BI.SelectList = BI.inherit(BI.Widget, {

    _defaultConfig: function () {
        return BI.extend(BI.SelectList.superclass._defaultConfig.apply(this, arguments), {
            baseCls: "bi-select-list",
            direction: BI.Direction.Top, // toolbar的位置
            logic: {
                dynamic: true
            },
            items: [],
            itemsCreator: BI.emptyFn,
            hasNext: BI.emptyFn,
            onLoaded: BI.emptyFn,
            toolbar: {
                type: "bi.multi_select_bar",
                iconWrapperWidth: 36
            },
            el: {
                type: "bi.list_pane"
            }
        });
    },
    _init: function () {
        BI.SelectList.superclass._init.apply(this, arguments);
        var self = this, o = this.options;

        // 全选
        this.toolbar = BI.createWidget(o.toolbar);
        this.allSelected = false;
        this.toolbar.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
            self.allSelected = this.isSelected();
            if (type === BI.Events.CLICK) {
                self.setAllSelected(self.allSelected);
                self.fireEvent(BI.SelectList.EVENT_CHANGE, value, obj);
            }
            self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
        });

        this.list = BI.createWidget(o.el, {
            type: "bi.list_pane",
            items: o.items,
            itemsCreator: function (op, callback) {
                op.times === 1 && self.toolbar.setVisible(false);
                o.itemsCreator(op, function (items) {
                    callback.apply(self, arguments);
                    if (op.times === 1) {
                        self.toolbar.setVisible(items && items.length > 0);
                        self.toolbar.setEnable(items && items.length > 0);
                    }
                    self._checkAllSelected();
                });
            },
            onLoaded: o.onLoaded,
            hasNext: o.hasNext
        });

        this.list.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
            if (type === BI.Events.CLICK) {
                self._checkAllSelected();
                self.fireEvent(BI.SelectList.EVENT_CHANGE, value, obj);
            }
            self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
        });

        BI.createWidget(BI.extend({
            element: this
        }, BI.LogicFactory.createLogic(BI.LogicFactory.createLogicTypeByDirection(o.direction), BI.extend({
            scrolly: true
        }, o.logic, {
            items: BI.LogicFactory.createLogicItemsByDirection(o.direction, this.toolbar, this.list)
        }))));

        if (o.items.length <= 0) {
            this.toolbar.setVisible(false);
            this.toolbar.setEnable(false);
        }
        if(BI.isNotNull(o.value)){
            this.setValue(o.value);
        }
    },

    _checkAllSelected: function () {
        var selectLength = this.list.getValue().length;
        var notSelectLength = this.getAllLeaves().length - selectLength;
        var hasNext = this.list.hasNext();
        var isAlreadyAllSelected = this.toolbar.isSelected();
        var isHalf = selectLength > 0 && (notSelectLength > 0 || (!isAlreadyAllSelected && hasNext));
        isHalf = isHalf || (notSelectLength > 0 && hasNext && isAlreadyAllSelected);
        this.toolbar.setHalfSelected(isHalf);
        !isHalf && this.toolbar.setSelected(selectLength > 0 && notSelectLength <= 0 && (!hasNext || isAlreadyAllSelected));
    },

    setAllSelected: function (v) {
        BI.each(this.getAllButtons(), function (i, btn) {
            (btn.setSelected || btn.setAllSelected).apply(btn, [v]);
        });
        this.allSelected = !!v;
        this.toolbar.setSelected(v);
        this.toolbar.setHalfSelected(false);
    },

    setToolBarVisible: function (b) {
        this.toolbar.setVisible(b);
    },

    isAllSelected: function () {
        return this.allSelected;
        // return this.toolbar.isSelected();
    },

    hasPrev: function () {
        return this.list.hasPrev();
    },

    hasNext: function () {
        return this.list.hasNext();
    },

    prependItems: function (items) {
        this.list.prependItems.apply(this.list, arguments);
    },

    addItems: function (items) {
        this.list.addItems.apply(this.list, arguments);
    },

    setValue: function (data) {
        var selectAll = data.type === BI.ButtonGroup.CHOOSE_TYPE_ALL;
        this.setAllSelected(selectAll);
        this.list[selectAll ? "setNotSelectedValue" : "setValue"](data.value);
        this._checkAllSelected();
    },

    getValue: function () {
        if (this.isAllSelected() === false) {
            return {
                type: BI.ButtonGroup.CHOOSE_TYPE_MULTI,
                value: this.list.getValue(),
                assist: this.list.getNotSelectedValue()
            };
        }
        return {
            type: BI.ButtonGroup.CHOOSE_TYPE_ALL,
            value: this.list.getNotSelectedValue(),
            assist: this.list.getValue()
        };

    },

    empty: function () {
        this.list.empty();
    },

    populate: function (items) {
        var o = this.options;
        this.toolbar.setVisible(!BI.isEmptyArray(items));
        this.toolbar.setEnable(!BI.isEmptyArray(items));
        this.list.populate.apply(this.list, arguments);
        this._checkAllSelected();
    },

    _setEnable: function (enable) {
        BI.SelectList.superclass._setEnable.apply(this, arguments);
        this.toolbar.setEnable(enable);
    },

    resetHeight: function (h) {
        var toolHeight = ( this.toolbar.element.outerHeight() || 25) * ( this.toolbar.isVisible() ? 1 : 0);
        this.list.resetHeight ? this.list.resetHeight(h - toolHeight) :
            this.list.element.css({"max-height": h - toolHeight + "px"});
    },

    setNotSelectedValue: function () {
        this.list.setNotSelectedValue.apply(this.list, arguments);
        this._checkAllSelected();
    },

    getNotSelectedValue: function () {
        return this.list.getNotSelectedValue();
    },

    getAllButtons: function () {
        return this.list.getAllButtons();
    },

    getAllLeaves: function () {
        return this.list.getAllLeaves();
    },

    getSelectedButtons: function () {
        return this.list.getSelectedButtons();
    },

    getNotSelectedButtons: function () {
        return this.list.getNotSelectedButtons();
    },

    getIndexByValue: function (value) {
        return this.list.getIndexByValue(value);
    },

    getNodeById: function (id) {
        return this.list.getNodeById(id);
    },

    getNodeByValue: function (value) {
        return this.list.getNodeByValue(value);
    }
});
BI.SelectList.EVENT_CHANGE = "EVENT_CHANGE";
BI.shortcut("bi.select_list", BI.SelectList);<commit_msg>update<commit_after>/**
 * 选择列表
 *
 * Created by GUY on 2015/11/1.
 * @class BI.SelectList
 * @extends BI.Widget
 */
BI.SelectList = BI.inherit(BI.Widget, {

    _defaultConfig: function () {
        return BI.extend(BI.SelectList.superclass._defaultConfig.apply(this, arguments), {
            baseCls: "bi-select-list",
            direction: BI.Direction.Top, // toolbar的位置
            logic: {
                dynamic: true
            },
            items: [],
            itemsCreator: BI.emptyFn,
            hasNext: BI.emptyFn,
            onLoaded: BI.emptyFn,
            toolbar: {
                type: "bi.multi_select_bar",
                iconWrapperWidth: 36
            },
            el: {
                type: "bi.list_pane"
            }
        });
    },
    _init: function () {
        BI.SelectList.superclass._init.apply(this, arguments);
        var self = this, o = this.options;

        // 全选
        this.toolbar = BI.createWidget(o.toolbar);
        this.allSelected = false;
        this.toolbar.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
            self.allSelected = this.isSelected();
            if (type === BI.Events.CLICK) {
                self.setAllSelected(self.allSelected);
                self.fireEvent(BI.SelectList.EVENT_CHANGE, value, obj);
            }
            self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
        });

        this.list = BI.createWidget(o.el, {
            type: "bi.list_pane",
            items: o.items,
            itemsCreator: function (op, callback) {
                op.times === 1 && self.toolbar.setVisible(false);
                o.itemsCreator(op, function (items) {
                    callback.apply(self, arguments);
                    if (op.times === 1) {
                        self.toolbar.setVisible(items && items.length > 0);
                        self.toolbar.setEnable(items && items.length > 0);
                    }
                    self._checkAllSelected();
                });
            },
            onLoaded: o.onLoaded,
            hasNext: o.hasNext
        });

        this.list.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
            if (type === BI.Events.CLICK) {
                self._checkAllSelected();
                self.fireEvent(BI.SelectList.EVENT_CHANGE, value, obj);
            }
            self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
        });

        BI.createWidget(BI.extend({
            element: this
        }, BI.LogicFactory.createLogic(BI.LogicFactory.createLogicTypeByDirection(o.direction), BI.extend({
            scrolly: true
        }, o.logic, {
            items: BI.LogicFactory.createLogicItemsByDirection(o.direction, this.toolbar, this.list)
        }))));

        if (o.items.length <= 0) {
            this.toolbar.setVisible(false);
            this.toolbar.setEnable(false);
        }
        if(BI.isNotNull(o.value)){
            this.setValue(o.value);
        }
    },

    _checkAllSelected: function () {
        var selectLength = this.list.getValue().length;
        var notSelectLength = this.getAllLeaves().length - selectLength;
        var hasNext = this.list.hasNext();
        var isAlreadyAllSelected = this.toolbar.isSelected();
        var isHalf = selectLength > 0 && (notSelectLength > 0 || (!isAlreadyAllSelected && hasNext));
        isHalf = isHalf || (notSelectLength > 0 && hasNext && isAlreadyAllSelected);
        this.toolbar.setHalfSelected(isHalf);
        !isHalf && this.toolbar.setSelected(selectLength > 0 && notSelectLength <= 0 && (!hasNext || isAlreadyAllSelected));
    },

    setAllSelected: function (v) {
        BI.each(this.getAllButtons(), function (i, btn) {
            (btn.setSelected || btn.setAllSelected).apply(btn, [v]);
        });
        this.allSelected = !!v;
        this.toolbar.setSelected(v);
        this.toolbar.setHalfSelected(false);
    },

    setToolBarVisible: function (b) {
        this.toolbar.setVisible(b);
    },

    isAllSelected: function () {
        return this.allSelected;
        // return this.toolbar.isSelected();
    },

    hasPrev: function () {
        return this.list.hasPrev();
    },

    hasNext: function () {
        return this.list.hasNext();
    },

    prependItems: function (items) {
        this.list.prependItems.apply(this.list, arguments);
    },

    addItems: function (items) {
        this.list.addItems.apply(this.list, arguments);
    },

    setValue: function (data) {
        var selectAll = data.type === BI.ButtonGroup.CHOOSE_TYPE_ALL;
        this.setAllSelected(selectAll);
        this.list[selectAll ? "setNotSelectedValue" : "setValue"](data.value);
        this._checkAllSelected();
    },

    getValue: function () {
        if (this.isAllSelected() === false) {
            return {
                type: BI.ButtonGroup.CHOOSE_TYPE_MULTI,
                value: this.list.getValue(),
                assist: this.list.getNotSelectedValue()
            };
        }
        return {
            type: BI.ButtonGroup.CHOOSE_TYPE_ALL,
            value: this.list.getNotSelectedValue(),
            assist: this.list.getValue()
        };

    },

    empty: function () {
        this.list.empty();
    },

    populate: function (items) {
        this.toolbar.setVisible(!BI.isEmptyArray(items));
        this.toolbar.setEnable(!BI.isEmptyArray(items));
        this.list.populate.apply(this.list, arguments);
        this._checkAllSelected();
    },

    _setEnable: function (enable) {
        BI.SelectList.superclass._setEnable.apply(this, arguments);
        this.toolbar.setEnable(enable);
    },

    resetHeight: function (h) {
        var toolHeight = ( this.toolbar.element.outerHeight() || 25) * ( this.toolbar.isVisible() ? 1 : 0);
        this.list.resetHeight ? this.list.resetHeight(h - toolHeight) :
            this.list.element.css({"max-height": h - toolHeight + "px"});
    },

    setNotSelectedValue: function () {
        this.list.setNotSelectedValue.apply(this.list, arguments);
        this._checkAllSelected();
    },

    getNotSelectedValue: function () {
        return this.list.getNotSelectedValue();
    },

    getAllButtons: function () {
        return this.list.getAllButtons();
    },

    getAllLeaves: function () {
        return this.list.getAllLeaves();
    },

    getSelectedButtons: function () {
        return this.list.getSelectedButtons();
    },

    getNotSelectedButtons: function () {
        return this.list.getNotSelectedButtons();
    },

    getIndexByValue: function (value) {
        return this.list.getIndexByValue(value);
    },

    getNodeById: function (id) {
        return this.list.getNodeById(id);
    },

    getNodeByValue: function (value) {
        return this.list.getNodeByValue(value);
    }
});
BI.SelectList.EVENT_CHANGE = "EVENT_CHANGE";
BI.shortcut("bi.select_list", BI.SelectList);<|endoftext|>


<commit_before>import React, { PropTypes }  from 'react';

function ProgressBar({id, maxValue=100, value, text}) {
    const percent = value * 100 / maxValue;
    const style = {width: `${percent}%`};

    return (

                {text}

    );
}

ProgressBar.propTypes = {
    id: PropTypes.string,
    text: PropTypes.string,
    maxValue: PropTypes.number,
    value: PropTypes.number.isRequired
};

export default ProgressBar;<commit_msg>fixed style name<commit_after>import React, { PropTypes }  from 'react';

function ProgressBar({id, maxValue=100, value, text}) {
    const percent = value * 100 / maxValue;
    const style = {width: `${percent}%`};

    return (

                {text}

    );
}

ProgressBar.propTypes = {
    id: PropTypes.string,
    text: PropTypes.string,
    maxValue: PropTypes.number,
    value: PropTypes.number.isRequired
};

export default ProgressBar;<|endoftext|>


<commit_before>define([
    'flight/lib/component'
], function(
    defineComponent) {
    'use strict';

    return defineComponent(FindPath);

    function FindPath() {
        this.after('initialize', function() {
            this.$node.html('<button class="btn btn-mini btn-primary">Add Vertices</button>');
        });
    }
});
<commit_msg>Show how many vertices will be added<commit_after>define([
    'flight/lib/component',
    'service/longRunningProcess'
], function(
    defineComponent,
    LongRunningProcessService) {
    'use strict';

    var longRunningProcessService = new LongRunningProcessService();

    return defineComponent(FindPath);

    function FindPath() {
        this.after('teardown', function() {
            this.$node.empty();
        });

        this.after('initialize', function() {
            if (_.isNumber(this.attr.process.resultsCount)) {
                this.$node.html('<button class="btn btn-mini btn-primary">Add ' + this.attr.process.resultsCount + ' Vertices</button>');
            }
        });
    }
});
<|endoftext|>