<commit_before>#!/usr/bin/env node
"use strict";

var request = require("request");
var cheerio = require("cheerio");
var colors = require("colors");
var path = require("path");
var fs = require("fs");
var childProcess = require('child_process');

var fetch = function (text, callback) {
    var encoded = encodeURIComponent(text)
    var options = {
        "url": "http://tw.dictionary.search.yahoo.com/search?p="+encoded,
    };
    request(options, function(error, response, body) {
        callback(error, body);
    });
};

var parseKeywords = function ($, cur) {
    return cur.find("b b").map(function (i, elem) {
        return $(this).text();
    }).get();
};

var parseExamples = function ($, cur) {
    return cur.find(".example_translation")
    .filter(function (i, elem) {
        return $(this).text().length > 0;
    })
    .map(function (i, elem) {
        var parent = $(this).parent();
        $(this).remove();
        return {
            "english": parent.text(),
            "chinese": $(this).text(),
            "keywords": parseKeywords($, parent),
        }
    }).get();
};

var parseExplanations = function ($, cur) {
    return cur.find("li").map(function (i, elem) {
        return {
            "explanation": $(this).find("h4").text().replace(/\d+\. /, ""),
            "examples": parseExamples($, $(this)),
        };
    }).get();
};

var parseTypes = function ($, cur) {
    return cur.find(".lst").first().find(".compArticleList").map(function (i, elem) {
        return {
            "desc": $(this).prev().find(".title").text(),
            "explanations": parseExplanations($, $(this)),
        };
    }).get();
};

var parseAudio = function ($, cur) {
    var obj = JSON.parse(cur.find("#iconStyle").text());
    if (obj["sound_url_1"].length === 0) return null;
    return obj["sound_url_1"].filter(function (elem) {
        return ('mp3' in elem);
    })[0]['mp3'];
};

var parse = function (data) {
    var $ = cheerio.load(data);
    var root = $("#main");
    return {
        "word": root.find("#term").first().text(),
        "pronunciation": root.find("#pronunciation_pos").text(),
        "audio": parseAudio($, root),
        "types": parseTypes($, root),
        "suggesstion": root.find("h2").find("i").text(),
    };
};

var highlighter = function (pre, cur) {
    var re = new RegExp('\\b'+cur+'\\b')
    return pre.replace(re, cur.bold);
};

var display = function (text, info) {
    if (text !== info.word) {
        console.log(info.word.cyan.bold.underline);
    }
    if (info.pronunciation) {
        console.log(info.pronunciation.replace(/\[([^\]]+)\]/g, ": ["+"$1".bold+"]"));
    }
    info.types.forEach(function (type) {
        console.log("%s", type.desc.red.bold);
        for (var i=0; i<type.explanations.length; i++) {
            var exp = type.explanations[i];
            console.log("  %d. %s", i+1, exp.explanation);
            for (var j=0; j<exp.examples.length; j++) {
                var ex = exp.examples[j];
                console.log("     %s",
                        ex.keywords.reduce(highlighter, ex.english).cyan);
                console.log("     %s", ex.chinese.green);
            }
        }
    });
    return 0;
};

var play = function (config, info) {
    if (config.playerCmd) {
        childProcess.exec(config.playerCmd + " " + info.audio);
    }
};

var loadConfig = function () {
    var name = (process.platform=="win32") ? "USERPROFILE" : "HOME";
    var file = path.join(process.env[name], ".ydict.json");
    if (!fs.existsSync(file)) return {};
    return require(file);
}

exports.lookup = function (text, callback) {
    fetch(text, function (err, data) {
        if (err) {
            callback(err);
        } else {
            callback(null, parse(data));
        }
    });
};

var main = function () {
    var args = process.argv.splice(2);
    if (args.length === 0) {
        console.error("Error: Word or phrase needed.");
        process.exit(1);
    }
    var config = loadConfig();
    var text = args.join(" ");
    exports.lookup(text, function callback(err, info) {
        if (err) {
            console.error("Some error happened:", err);
            process.exit(2);
        } else {
            if (info.word) {
                play(config, info);
                display(text, info);
            } else if (info.suggesstion) {
                console.warn("拼字檢查: %s -> %s".red.bold,
                        text, info.suggesstion);
                exports.lookup(info.suggesstion, callback);
            } else {
                console.log("查無此字".red.bold);
                process.exit(3);
            }
        }
    });
};

if (require.main === module) {
    main();
}
<commit_msg>Ignore exception in parseAudio<commit_after>#!/usr/bin/env node
"use strict";

var request = require("request");
var cheerio = require("cheerio");
var colors = require("colors");
var path = require("path");
var fs = require("fs");
var childProcess = require('child_process');

var fetch = function (text, callback) {
    var encoded = encodeURIComponent(text)
    var options = {
        "url": "http://tw.dictionary.search.yahoo.com/search?p="+encoded,
    };
    request(options, function(error, response, body) {
        callback(error, body);
    });
};

var parseKeywords = function ($, cur) {
    return cur.find("b b").map(function (i, elem) {
        return $(this).text();
    }).get();
};

var parseExamples = function ($, cur) {
    return cur.find(".example_translation")
    .filter(function (i, elem) {
        return $(this).text().length > 0;
    })
    .map(function (i, elem) {
        var parent = $(this).parent();
        $(this).remove();
        return {
            "english": parent.text(),
            "chinese": $(this).text(),
            "keywords": parseKeywords($, parent),
        }
    }).get();
};

var parseExplanations = function ($, cur) {
    return cur.find("li").map(function (i, elem) {
        return {
            "explanation": $(this).find("h4").text().replace(/\d+\. /, ""),
            "examples": parseExamples($, $(this)),
        };
    }).get();
};

var parseTypes = function ($, cur) {
    return cur.find(".lst").first().find(".compArticleList").map(function (i, elem) {
        return {
            "desc": $(this).prev().find(".title").text(),
            "explanations": parseExplanations($, $(this)),
        };
    }).get();
};

var parseAudio = function ($, cur) {
    try {
        var obj = JSON.parse(cur.find("#iconStyle").text());
        if (obj["sound_url_1"].length === 0) return null;
        return obj["sound_url_1"].filter(function (elem) {
            return ('mp3' in elem);
        })[0]['mp3'];
    } catch(e) {
        return null;
    }
};

var parse = function (data) {
    var $ = cheerio.load(data);
    var root = $("#main");
    return {
        "word": root.find("#term").first().text(),
        "pronunciation": root.find("#pronunciation_pos").text(),
        "audio": parseAudio($, root),
        "types": parseTypes($, root),
        "suggesstion": root.find("h2").find("i").text(),
    };
};

var highlighter = function (pre, cur) {
    var re = new RegExp('\\b'+cur+'\\b')
    return pre.replace(re, cur.bold);
};

var display = function (text, info) {
    if (text !== info.word) {
        console.log(info.word.cyan.bold.underline);
    }
    if (info.pronunciation) {
        console.log(info.pronunciation.replace(/\[([^\]]+)\]/g, ": ["+"$1".bold+"]"));
    }
    info.types.forEach(function (type) {
        console.log("%s", type.desc.red.bold);
        for (var i=0; i<type.explanations.length; i++) {
            var exp = type.explanations[i];
            console.log("  %d. %s", i+1, exp.explanation);
            for (var j=0; j<exp.examples.length; j++) {
                var ex = exp.examples[j];
                console.log("     %s",
                        ex.keywords.reduce(highlighter, ex.english).cyan);
                console.log("     %s", ex.chinese.green);
            }
        }
    });
    return 0;
};

var play = function (config, info) {
    if (config.playerCmd) {
        childProcess.exec(config.playerCmd + " " + info.audio);
    }
};

var loadConfig = function () {
    var name = (process.platform=="win32") ? "USERPROFILE" : "HOME";
    var file = path.join(process.env[name], ".ydict.json");
    if (!fs.existsSync(file)) return {};
    return require(file);
}

exports.lookup = function (text, callback) {
    fetch(text, function (err, data) {
        if (err) {
            callback(err);
        } else {
            callback(null, parse(data));
        }
    });
};

var main = function () {
    var args = process.argv.splice(2);
    if (args.length === 0) {
        console.error("Error: Word or phrase needed.");
        process.exit(1);
    }
    var config = loadConfig();
    var text = args.join(" ");
    exports.lookup(text, function callback(err, info) {
        if (err) {
            console.error("Some error happened:", err);
            process.exit(2);
        } else {
            if (info.word) {
                play(config, info);
                display(text, info);
            } else if (info.suggesstion) {
                console.warn("拼字檢查: %s -> %s".red.bold,
                        text, info.suggesstion);
                exports.lookup(info.suggesstion, callback);
            } else {
                console.log("查無此字".red.bold);
                process.exit(3);
            }
        }
    });
};

if (require.main === module) {
    main();
}
<|endoftext|>


<commit_before>var $ = (function() {
  var slice = [].slice, k,
    ADJ_OPS = {append: 'beforeEnd', prepend: 'afterBegin', before: 'beforeBegin', after: 'afterEnd'};

  function $(_){
    function fn(_){ return arguments.callee.dom.forEach(_), arguments.callee; }
    fn.dom = slice.call(document.querySelectorAll(fn.selector = _));
    for(k in $.fn) fn[k] = $.fn[k];
    return fn;
  }

  $.fn = {
    get: function(idx){ return idx === undefined ? this.dom : this.dom[idx] },
    html: function(html){
      return this(function(el){ el.innerHTML = html });
    },
    attr: function(name,value){
      if(value === undefined) return this.dom[0].getAttribute(name) || undefined;
      return this(function(el){ el.setAttribute(name,value); });;
    },
    css: function(style){
      return this(function(el){ el.style.cssText += ';'+style });
    },
    anim: function(transform, opacity, dur){
      return this.css('-webkit-transition:all '+(dur||0.5)+'s;'+
        '-webkit-transform:'+transform+';opacity:'+(opacity===0?0:opacity||1));
    },
    live: function(event, callback){
      var selector = this.selector;
      document.body.addEventListener(event, function(event){
        var target = event.target, nodes = slice.call(document.querySelectorAll(selector));
        while(target && nodes.indexOf(target)<0) target = target.parentNode;
        if(target && !(target===document)) callback.call(target, event);
      }, false);
      return this;
    }
  };

  for(k in ADJ_OPS)
    $.fn[k] = (function(op){
      return function(html){ return this(function(el){ el.insertAdjacentHTML(op,html) }) };
    })(ADJ_OPS[k]);

  function ajax(method, url, success){
    var r = new XMLHttpRequest();
    r.onreadystatechange = function(){
      if(r.readyState==4 && (r.status==200 || r.status==0))
        success(r.responseText);
    };
    r.open(method,url,true);
    r.send(null);
  }

  $.get = function(url, success){ ajax('GET', url, success); };
  $.post = function(url, success){ ajax('POST', url, success); };
  $.getJSON = function(url, success){
    $.get(url, function(json){ success(JSON.parse(json)) });
  };

  return $;
})();
<commit_msg>remove extra ;<commit_after>var $ = (function() {
  var slice = [].slice, k,
    ADJ_OPS = {append: 'beforeEnd', prepend: 'afterBegin', before: 'beforeBegin', after: 'afterEnd'};

  function $(_){
    function fn(_){ return arguments.callee.dom.forEach(_), arguments.callee; }
    fn.dom = slice.call(document.querySelectorAll(fn.selector = _));
    for(k in $.fn) fn[k] = $.fn[k];
    return fn;
  }

  $.fn = {
    get: function(idx){ return idx === undefined ? this.dom : this.dom[idx] },
    html: function(html){
      return this(function(el){ el.innerHTML = html });
    },
    attr: function(name,value){
      if(value === undefined) return this.dom[0].getAttribute(name) || undefined;
      return this(function(el){ el.setAttribute(name,value) });
    },
    css: function(style){
      return this(function(el){ el.style.cssText += ';'+style });
    },
    anim: function(transform, opacity, dur){
      return this.css('-webkit-transition:all '+(dur||0.5)+'s;'+
        '-webkit-transform:'+transform+';opacity:'+(opacity===0?0:opacity||1));
    },
    live: function(event, callback){
      var selector = this.selector;
      document.body.addEventListener(event, function(event){
        var target = event.target, nodes = slice.call(document.querySelectorAll(selector));
        while(target && nodes.indexOf(target)<0) target = target.parentNode;
        if(target && !(target===document)) callback.call(target, event);
      }, false);
      return this;
    }
  };

  for(k in ADJ_OPS)
    $.fn[k] = (function(op){
      return function(html){ return this(function(el){ el.insertAdjacentHTML(op,html) }) };
    })(ADJ_OPS[k]);

  function ajax(method, url, success){
    var r = new XMLHttpRequest();
    r.onreadystatechange = function(){
      if(r.readyState==4 && (r.status==200 || r.status==0))
        success(r.responseText);
    };
    r.open(method,url,true);
    r.send(null);
  }

  $.get = function(url, success){ ajax('GET', url, success); };
  $.post = function(url, success){ ajax('POST', url, success); };
  $.getJSON = function(url, success){
    $.get(url, function(json){ success(JSON.parse(json)) });
  };

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


<commit_before>(function(global) {
  global.S3MP = (function() {

    // Wrap this into underscore library extension
    _.mixin({
      findIndex : function (collection, filter) {
        for (var i = 0; i < collection.length; i++) {
          if (filter(collection[i], i, collection)) {
            return i;
          }
        }
        return -1;
      }
    });
// S3MP Constructor
function S3MP(options) {
  var files
    , progress_timer = []
    , S3MP = this;

  _.extend(this, options);
  this.headers = _.object(_.map(options.headers, function(v,k) { return ["x-amz-" + k.toLowerCase(), v] }));

  this.uploadList = [];

  // Handles all of the success/failure events, and
  // progress notifiers
  this.handler = {

    // Activate an appropriate number of parts (number = pipes)
    // when all of the parts have been successfully initialized
    beginUpload: function() {
      var i = [];
      function beginUpload(pipes, uploadObj) {
        var key = uploadObj.key
          , num_parts = uploadObj.parts.length;

        if (typeof i[key] === "undefined") {
          i[key] = 0;
        }

        i[key]++;

        if (i[key] === num_parts) {
          for (var j=0; j<pipes; j++) {
            uploadObj.parts[j].activate();
          }
          S3MP.handler.startProgressTimer(key);
          S3MP.onStart(uploadObj); // This probably needs to go somewhere else.
        }
      }
      return beginUpload;
    }(),

    // called when an upload is paused or the network connection cuts out
    onError: function(uploadObj, part) {
      S3MP.onError(uploadObj, part);
    },

    // called when a single part has successfully uploaded
    onPartSuccess: function(uploadObj, finished_part) {
      var parts, i, ETag;

      parts = uploadObj.parts;
      finished_part.status = "complete";

      // Append the ETag (in the response header) to the ETags array
      ETag = finished_part.xhr.getResponseHeader("ETag");
      uploadObj.Etags.push({ ETag: ETag.replace(/\"/g, ''), partNum: finished_part.num });

      // Increase the uploaded count and delete the finished part
      uploadObj.uploaded += finished_part.size;
      uploadObj.inprogress[finished_part.num] = 0;
      i = _.indexOf(parts, finished_part);
      parts.splice(i,1);

      // activate one of the remaining parts
      if (parts.length) {
        i = _.findIndex(parts, function(el, index, collection) {
          if (el.status !== "active") {
            return true;
          }
        });
        if (i !== -1){
          parts[i].activate();
        }
      }

      // If no parts remain then the upload has finished
      if (!parts.length) {
        this.onComplete(uploadObj);
      }
    },

    // called when all parts have successfully uploaded
    onComplete: function(uploadObj) {
      var key = _.indexOf(S3MP.uploadList, uploadObj);

      // Stop the onprogress timer
      this.clearProgressTimer(key);

      // Tell the server to put together the pieces
      S3MP.completeMultipart(uploadObj, function(obj) {
        // Notify the client that the upload has succeeded when we
        // get confirmation from the server
        if (obj.location) {
          obj.extra_data ? S3MP.onComplete(uploadObj, obj.extra_data) : S3MP.onComplete(uploadObj);
        }
      });

    },

    // Called by progress_timer
    onProgress: function(key, size, done, percent, speed) {
      S3MP.onProgress(key, size, done, percent, speed);
    },

    startProgressTimer: function() {
      var last_upload_chunk = [];
      var fn = function(key) {
        progress_timer[key] = global.setInterval(function() {
          var upload, size, done, percent, speed;

          if (typeof last_upload_chunk[key] === "undefined") {
            last_upload_chunk[key] = 0;
          }

          upload = S3MP.uploadList[key];
          size = upload.size;
          done = upload.uploaded;

          _.each(upload.inprogress,function(val, index) {
            part_available = _.find(upload.parts, function(part){
              return part.num == index
            })
            if(part_available && val !== undefined) done += val;
          });

          percent = done/size * 100;
          speed = done - last_upload_chunk[key];
          last_upload_chunk[key] = done;

          upload.handler.onProgress(key, size, done, percent, speed);
        }, 1000);
      };
      return fn;
    }(),

    clearProgressTimer: function(key) {
      global.clearInterval(progress_timer[key]);
    }

  };

  // List of files may come from a FileList object or an array of files
  if (this.fileSelector) {
    files = $(this.fileSelector).get(0).files; // FileList object
  } else {
    files = this.fileList; // array specified in configuration
  }

  _.each(files, function(file, key) {
    var upload = new Upload(file, S3MP, key);
    S3MP.uploadList.push(upload);
    upload.init();
  });

};

S3MP.prototype.initiateMultipart = function(upload, cb) {
  var url, body, xhr;

  url = '/s3_multipart/uploads';
  body = JSON.stringify({ object_name  : upload.name,
                          content_type : upload.type,
                          content_size : upload.size,
                          headers      : this.headers,
                          context      : $(this.fileInputElement).data("context"),
                          uploader     : $(this.fileInputElement).data("uploader")
                        });

  xhr = this.createXhrRequest('POST', url);
  this.deliverRequest(xhr, body, cb);

};

S3MP.prototype.signPartRequests = function(id, object_name, upload_id, parts, cb) {
  var content_lengths, url, body, xhr;

  content_lengths = _.reduce(_.rest(parts), function(memo, part) {
    return memo + "-" + part.size;
  }, parts[0].size);

  url = "/s3_multipart/uploads/"+id;
  body = JSON.stringify({ object_name     : object_name,
                          upload_id       : upload_id,
                          content_lengths : content_lengths
                        });

  xhr = this.createXhrRequest('PUT', url);
  this.deliverRequest(xhr, body, cb);
};

S3MP.prototype.completeMultipart = function(uploadObj, cb) {
  var url, body, xhr;

  url = '/s3_multipart/uploads/'+uploadObj.id;
  body = JSON.stringify({ object_name    : uploadObj.object_name,
                          upload_id      : uploadObj.upload_id,
                          content_length : uploadObj.size,
                          parts          : uploadObj.Etags
                        });

  xhr = this.createXhrRequest('PUT', url);
  this.deliverRequest(xhr, body, cb);
};

// Specify callbacks, request body, and settings for requests that contact
// the site server, and send the request.
S3MP.prototype.deliverRequest = function(xhr, body, cb) {
  var self = this;

  xhr.onload = function() {
    response = JSON.parse(this.responseText);
    if (response.error) {
      return self.onError({
        name: "ServerResponse",
        message: response.error
      });
    }
    cb(response);
  };

  xhr.onerror = function() {
    // To-do: Handle communication errors
  };

  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));

  xhr.send(body);
}

S3MP.prototype.createXhrRequest = function() {
  var xhrRequest;

  // Sniff for xhr object
  if (typeof XMLHttpRequest.constructor === "function") {
    xhrRequest = XMLHttpRequest;
  } else if (typeof XDomainRequest !== "undefined") {
    xhrRequest = XDomainRequest;
  } else {
    xhrRequest = null; // Error out to the client (To-do)
  }

  return function(method, url, cb, open) { // open defaults to true
    var args, xhr, open = true;

    args = Array.prototype.slice.call(arguments);
    if (typeof args[0] === "undefined") {
      cb = null;
      open = false;
    }

    xhr = new xhrRequest();
    if (open) { // open the request unless specified otherwise
      xhr.open(method, url, true);
    }
    xhr.onreadystatechange = cb;

    return xhr;
  };

}();

S3MP.prototype.sliceBlob = function() {
  try {
    var test_blob = new Blob();
  } catch(e) {
    return "Unsupported";
  }

  if (test_blob.slice) {
    return function(blob, start, end) {
      return blob.slice(start, end);
    }
  } else if (test_blob.mozSlice) {
    return function(blob, start, end) {
      return blob.mozSlice(start, end);
    }
  } else if (test_blob.webkitSlice) {
    return function(blob, start, end) {
      return blob.webkitSlice(start, end);
    }
  } else {
    return "Unsupported";
  }
}();

// utility function to return an upload object given a file
S3MP.prototype._returnUploadObj = function(key) {
  var uploadObj = _.find(this.uploadList, function(uploadObj) {
    return uploadObj.key === key;
  });
  return uploadObj;
};

// cancel a given file upload
S3MP.prototype.cancel = function(key) {
  var uploadObj, i;

  uploadObj = this._returnUploadObj(key);
  i = _.indexOf(this.uploadList, uploadObj);

  this.uploadList.splice(i,i+1);
  this.onCancel(key);
};

// pause a given file upload
S3MP.prototype.pause = function(key) {
  var uploadObj = this._returnUploadObj(key);

  _.each(uploadObj.parts, function(part, key, list) {
    if (part.status == "active") {
      part.pause();
    }
  });

  this.onPause(key);
};

// resume a given file upload
S3MP.prototype.resume = function(key) {
  var uploadObj = this._returnUploadObj(key);

  _.each(uploadObj.parts, function(part, key, list) {
    if (part.status == "paused") {
      part.activate();
    }
  });

  this.onResume(key);
};

// Upload constructor
function Upload(file, o, key) {
  function Upload() {
    var upload, id, parts, part, segs, chunk_segs, chunk_lens, pipes, blob;

    upload = this;

    this.key = key;
    this.file = file;
    this.name = file.name;
    this.size = file.size;
    this.type = file.type;
    this.Etags = [];
    this.inprogress = [];
    this.uploaded = 0;
    this.status = "";

    // Break the file into an appropriate amount of chunks
    // This needs to be optimized for various browsers types/versions
    if (this.size > 1000000000) { // size greater than 1gb
      num_segs = 100;
      pipes = 10;
    } else if (this.size > 500000000) { // greater than 500mb
      num_segs = 50;
      pipes = 5;
    } else if (this.size > 100000000) { // greater than 100 mb
      num_segs = 20;
      pipes = 5;
    } else if (this.size > 50000000) { // greater than 50 mb
      num_segs = 5;
      pipes = 2;
    } else if (this.size > 10000000) { // greater than 10 mb
      num_segs = 2;
      pipes = 2;
    } else { // greater than 5 mb (S3 does not allow multipart uploads < 5 mb)
      num_segs = 1;
      pipes = 1;
    }

    chunk_segs = _.range(num_segs + 1);
    chunk_lens = _.map(chunk_segs, function(seg) {
      return Math.round(seg * (file.size/num_segs));
    });

    if (upload.sliceBlob == "Unsupported") {
      this.parts = [new UploadPart(file, 0, upload)];
    } else {
      this.parts = _.map(chunk_lens, function(len, i) {
        blob = upload.sliceBlob(file, len, chunk_lens[i+1]);
        return new UploadPart(blob, i+1, upload);
      });
      this.parts.pop(); // Remove the empty blob at the end of the array
    }

    // init function will initiate the multipart upload, sign all the parts, and
    // start uploading some parts in parallel
    this.init = function() {
      upload.initiateMultipart(upload, function(obj) {
        var id = upload.id = obj.id
          , upload_id = upload.upload_id = obj.upload_id
          , object_name = upload.object_name = obj.key // uuid generated by the server, different from name
          , parts = upload.parts;

        upload.signPartRequests(id, object_name, upload_id, parts, function(response) {
          _.each(parts, function(part, key) {
            part.date = response.uploads[key].date;
            part.auth = response.uploads[key].authorization;

            // Notify handler that an xhr request has been opened
            upload.handler.beginUpload(pipes, upload);
          });
        });
      });
    }
  };
  // Inherit the properties and prototype methods of the passed in S3MP instance object
  Upload.prototype = o;
  return new Upload();
}

// Upload part constructor
function UploadPart(blob, key, upload) {
  var part, xhr;

  part = this;

  this.size = blob.size;
  this.blob = blob;
  this.num = key;
  this.upload = upload;

  this.xhr = xhr = upload.createXhrRequest();
  xhr.onload = function() {
    upload.handler.onPartSuccess(upload, part);
  };
  xhr.onerror = function() {
    upload.handler.onError(upload, part);
  };
  xhr.upload.onprogress = _.throttle(function(e) {
    if (e.lengthComputable) {
      upload.inprogress[key] = e.loaded;
    }
  }, 1000);

};

UploadPart.prototype.activate = function() {
  var protocol = window.location.protocol;
  this.xhr.open('PUT', protocol+'//'+this.upload.bucket+'.s3.amazonaws.com/'+this.upload.object_name+'?partNumber='+this.num+'&uploadId='+this.upload.upload_id, true);
  this.xhr.setRequestHeader('x-amz-date', this.date);
  this.xhr.setRequestHeader('Authorization', this.auth);

  this.xhr.send(this.blob);
  this.status = "active";
};

UploadPart.prototype.pause = function() {
  this.xhr.abort();
  this.status = "paused";
};

return S3MP;

  }());

}(this));<commit_msg>Pipe config changed for large files<commit_after>(function(global) {
  global.S3MP = (function() {

    // Wrap this into underscore library extension
    _.mixin({
      findIndex : function (collection, filter) {
        for (var i = 0; i < collection.length; i++) {
          if (filter(collection[i], i, collection)) {
            return i;
          }
        }
        return -1;
      }
    });
// S3MP Constructor
function S3MP(options) {
  var files
    , progress_timer = []
    , S3MP = this;

  _.extend(this, options);
  this.headers = _.object(_.map(options.headers, function(v,k) { return ["x-amz-" + k.toLowerCase(), v] }));

  this.uploadList = [];

  // Handles all of the success/failure events, and
  // progress notifiers
  this.handler = {

    // Activate an appropriate number of parts (number = pipes)
    // when all of the parts have been successfully initialized
    beginUpload: function() {
      var i = [];
      function beginUpload(pipes, uploadObj) {
        var key = uploadObj.key
          , num_parts = uploadObj.parts.length;

        if (typeof i[key] === "undefined") {
          i[key] = 0;
        }

        i[key]++;

        if (i[key] === num_parts) {
          for (var j=0; j<pipes; j++) {
            uploadObj.parts[j].activate();
          }
          S3MP.handler.startProgressTimer(key);
          S3MP.onStart(uploadObj); // This probably needs to go somewhere else.
        }
      }
      return beginUpload;
    }(),

    // called when an upload is paused or the network connection cuts out
    onError: function(uploadObj, part) {
      S3MP.onError(uploadObj, part);
    },

    // called when a single part has successfully uploaded
    onPartSuccess: function(uploadObj, finished_part) {
      var parts, i, ETag;

      parts = uploadObj.parts;
      finished_part.status = "complete";

      // Append the ETag (in the response header) to the ETags array
      ETag = finished_part.xhr.getResponseHeader("ETag");
      uploadObj.Etags.push({ ETag: ETag.replace(/\"/g, ''), partNum: finished_part.num });

      // Increase the uploaded count and delete the finished part
      uploadObj.uploaded += finished_part.size;
      uploadObj.inprogress[finished_part.num] = 0;
      i = _.indexOf(parts, finished_part);
      parts.splice(i,1);

      // activate one of the remaining parts
      if (parts.length) {
        i = _.findIndex(parts, function(el, index, collection) {
          if (el.status !== "active") {
            return true;
          }
        });
        if (i !== -1){
          parts[i].activate();
        }
      }

      // If no parts remain then the upload has finished
      if (!parts.length) {
        this.onComplete(uploadObj);
      }
    },

    // called when all parts have successfully uploaded
    onComplete: function(uploadObj) {
      var key = _.indexOf(S3MP.uploadList, uploadObj);

      // Stop the onprogress timer
      this.clearProgressTimer(key);

      // Tell the server to put together the pieces
      S3MP.completeMultipart(uploadObj, function(obj) {
        // Notify the client that the upload has succeeded when we
        // get confirmation from the server
        if (obj.location) {
          obj.extra_data ? S3MP.onComplete(uploadObj, obj.extra_data) : S3MP.onComplete(uploadObj);
        }
      });

    },

    // Called by progress_timer
    onProgress: function(key, size, done, percent, speed) {
      S3MP.onProgress(key, size, done, percent, speed);
    },

    startProgressTimer: function() {
      var last_upload_chunk = [];
      var fn = function(key) {
        progress_timer[key] = global.setInterval(function() {
          var upload, size, done, percent, speed;

          if (typeof last_upload_chunk[key] === "undefined") {
            last_upload_chunk[key] = 0;
          }

          upload = S3MP.uploadList[key];
          size = upload.size;
          done = upload.uploaded;

          _.each(upload.inprogress,function(val, index) {
            part_available = _.find(upload.parts, function(part){
              return part.num == index
            })
            if(part_available && val !== undefined) done += val;
          });

          percent = done/size * 100;
          speed = done - last_upload_chunk[key];
          last_upload_chunk[key] = done;

          upload.handler.onProgress(key, size, done, percent, speed);
        }, 1000);
      };
      return fn;
    }(),

    clearProgressTimer: function(key) {
      global.clearInterval(progress_timer[key]);
    }

  };

  // List of files may come from a FileList object or an array of files
  if (this.fileSelector) {
    files = $(this.fileSelector).get(0).files; // FileList object
  } else {
    files = this.fileList; // array specified in configuration
  }

  _.each(files, function(file, key) {
    var upload = new Upload(file, S3MP, key);
    S3MP.uploadList.push(upload);
    upload.init();
  });

};

S3MP.prototype.initiateMultipart = function(upload, cb) {
  var url, body, xhr;

  url = '/s3_multipart/uploads';
  body = JSON.stringify({ object_name  : upload.name,
                          content_type : upload.type,
                          content_size : upload.size,
                          headers      : this.headers,
                          context      : $(this.fileInputElement).data("context"),
                          uploader     : $(this.fileInputElement).data("uploader")
                        });

  xhr = this.createXhrRequest('POST', url);
  this.deliverRequest(xhr, body, cb);

};

S3MP.prototype.signPartRequests = function(id, object_name, upload_id, parts, cb) {
  var content_lengths, url, body, xhr;

  content_lengths = _.reduce(_.rest(parts), function(memo, part) {
    return memo + "-" + part.size;
  }, parts[0].size);

  url = "/s3_multipart/uploads/"+id;
  body = JSON.stringify({ object_name     : object_name,
                          upload_id       : upload_id,
                          content_lengths : content_lengths
                        });

  xhr = this.createXhrRequest('PUT', url);
  this.deliverRequest(xhr, body, cb);
};

S3MP.prototype.completeMultipart = function(uploadObj, cb) {
  var url, body, xhr;

  url = '/s3_multipart/uploads/'+uploadObj.id;
  body = JSON.stringify({ object_name    : uploadObj.object_name,
                          upload_id      : uploadObj.upload_id,
                          content_length : uploadObj.size,
                          parts          : uploadObj.Etags
                        });

  xhr = this.createXhrRequest('PUT', url);
  this.deliverRequest(xhr, body, cb);
};

// Specify callbacks, request body, and settings for requests that contact
// the site server, and send the request.
S3MP.prototype.deliverRequest = function(xhr, body, cb) {
  var self = this;

  xhr.onload = function() {
    response = JSON.parse(this.responseText);
    if (response.error) {
      return self.onError({
        name: "ServerResponse",
        message: response.error
      });
    }
    cb(response);
  };

  xhr.onerror = function() {
    // To-do: Handle communication errors
  };

  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));

  xhr.send(body);
}

S3MP.prototype.createXhrRequest = function() {
  var xhrRequest;

  // Sniff for xhr object
  if (typeof XMLHttpRequest.constructor === "function") {
    xhrRequest = XMLHttpRequest;
  } else if (typeof XDomainRequest !== "undefined") {
    xhrRequest = XDomainRequest;
  } else {
    xhrRequest = null; // Error out to the client (To-do)
  }

  return function(method, url, cb, open) { // open defaults to true
    var args, xhr, open = true;

    args = Array.prototype.slice.call(arguments);
    if (typeof args[0] === "undefined") {
      cb = null;
      open = false;
    }

    xhr = new xhrRequest();
    if (open) { // open the request unless specified otherwise
      xhr.open(method, url, true);
    }
    xhr.onreadystatechange = cb;

    return xhr;
  };

}();

S3MP.prototype.sliceBlob = function() {
  try {
    var test_blob = new Blob();
  } catch(e) {
    return "Unsupported";
  }

  if (test_blob.slice) {
    return function(blob, start, end) {
      return blob.slice(start, end);
    }
  } else if (test_blob.mozSlice) {
    return function(blob, start, end) {
      return blob.mozSlice(start, end);
    }
  } else if (test_blob.webkitSlice) {
    return function(blob, start, end) {
      return blob.webkitSlice(start, end);
    }
  } else {
    return "Unsupported";
  }
}();

// utility function to return an upload object given a file
S3MP.prototype._returnUploadObj = function(key) {
  var uploadObj = _.find(this.uploadList, function(uploadObj) {
    return uploadObj.key === key;
  });
  return uploadObj;
};

// cancel a given file upload
S3MP.prototype.cancel = function(key) {
  var uploadObj, i;

  uploadObj = this._returnUploadObj(key);
  i = _.indexOf(this.uploadList, uploadObj);

  this.uploadList.splice(i,i+1);
  this.onCancel(key);
};

// pause a given file upload
S3MP.prototype.pause = function(key) {
  var uploadObj = this._returnUploadObj(key);

  _.each(uploadObj.parts, function(part, key, list) {
    if (part.status == "active") {
      part.pause();
    }
  });

  this.onPause(key);
};

// resume a given file upload
S3MP.prototype.resume = function(key) {
  var uploadObj = this._returnUploadObj(key);

  _.each(uploadObj.parts, function(part, key, list) {
    if (part.status == "paused") {
      part.activate();
    }
  });

  this.onResume(key);
};

// Upload constructor
function Upload(file, o, key) {
  function Upload() {
    var upload, id, parts, part, segs, chunk_segs, chunk_lens, pipes, blob;

    upload = this;

    this.key = key;
    this.file = file;
    this.name = file.name;
    this.size = file.size;
    this.type = file.type;
    this.Etags = [];
    this.inprogress = [];
    this.uploaded = 0;
    this.status = "";

    // Break the file into an appropriate amount of chunks
    // This needs to be optimized for various browsers types/versions
    if (this.size > 1000000000) { // size greater than 1gb
      num_segs = 200;
      pipes = 5;
    } else if (this.size > 500000000) { // greater than 500mb
      num_segs = 100;
      pipes = 3;
    } else if (this.size > 100000000) { // greater than 100 mb
      num_segs = 20;
      pipes = 5;
    } else if (this.size > 50000000) { // greater than 50 mb
      num_segs = 5;
      pipes = 2;
    } else if (this.size > 10000000) { // greater than 10 mb
      num_segs = 2;
      pipes = 2;
    } else { // greater than 5 mb (S3 does not allow multipart uploads < 5 mb)
      num_segs = 1;
      pipes = 1;
    }

    chunk_segs = _.range(num_segs + 1);
    chunk_lens = _.map(chunk_segs, function(seg) {
      return Math.round(seg * (file.size/num_segs));
    });

    if (upload.sliceBlob == "Unsupported") {
      this.parts = [new UploadPart(file, 0, upload)];
    } else {
      this.parts = _.map(chunk_lens, function(len, i) {
        blob = upload.sliceBlob(file, len, chunk_lens[i+1]);
        return new UploadPart(blob, i+1, upload);
      });
      this.parts.pop(); // Remove the empty blob at the end of the array
    }

    // init function will initiate the multipart upload, sign all the parts, and
    // start uploading some parts in parallel
    this.init = function() {
      upload.initiateMultipart(upload, function(obj) {
        var id = upload.id = obj.id
          , upload_id = upload.upload_id = obj.upload_id
          , object_name = upload.object_name = obj.key // uuid generated by the server, different from name
          , parts = upload.parts;

        upload.signPartRequests(id, object_name, upload_id, parts, function(response) {
          _.each(parts, function(part, key) {
            part.date = response.uploads[key].date;
            part.auth = response.uploads[key].authorization;

            // Notify handler that an xhr request has been opened
            upload.handler.beginUpload(pipes, upload);
          });
        });
      });
    }
  };
  // Inherit the properties and prototype methods of the passed in S3MP instance object
  Upload.prototype = o;
  return new Upload();
}

// Upload part constructor
function UploadPart(blob, key, upload) {
  var part, xhr;

  part = this;

  this.size = blob.size;
  this.blob = blob;
  this.num = key;
  this.upload = upload;

  this.xhr = xhr = upload.createXhrRequest();
  xhr.onload = function() {
    upload.handler.onPartSuccess(upload, part);
  };
  xhr.onerror = function() {
    upload.handler.onError(upload, part);
  };
  xhr.upload.onprogress = _.throttle(function(e) {
    if (e.lengthComputable) {
      upload.inprogress[key] = e.loaded;
    }
  }, 1000);

};

UploadPart.prototype.activate = function() {
  var protocol = window.location.protocol;
  this.xhr.open('PUT', protocol+'//'+this.upload.bucket+'.s3.amazonaws.com/'+this.upload.object_name+'?partNumber='+this.num+'&uploadId='+this.upload.upload_id, true);
  this.xhr.setRequestHeader('x-amz-date', this.date);
  this.xhr.setRequestHeader('Authorization', this.auth);

  this.xhr.send(this.blob);
  this.status = "active";
};

UploadPart.prototype.pause = function() {
  this.xhr.abort();
  this.status = "paused";
};

return S3MP;

  }());

}(this));<|endoftext|>


<commit_before>jest.autoMockOff();
const IM = require('immutable');
const BinarySearchTree = require('../../../src/binary_trees/BinarySearchTree');
const BSTNode = require('../../../src/binary_trees/BSTNode');

describe('BinarySearchTree Operations', () => {

  describe('Instantiation', () => {
    let bst = new BinarySearchTree(),
        node = new BSTNode(0, "first node value", null, null),
        bstWithNode = new BinarySearchTree(null, IM.Seq.of(node));

    it('instantiates empty, checks size', () => {
      expect(bst.size).toBe(0);
    });

    it('instantiates with a root node, checks size', () => {
      expect(bstWithNode.size).toBe(1);
    });

  });

  describe('Internal Methods', () => {

  });

  describe('Basic Instance Methods', () => {

  });

  describe('Basic Getters', () => {

    it('gets size', () => {

    });

    it('gets min', () => {

    });

    it('gets max', () => {

    });

    it('gets keys', () => {

    });

    it('gets values', () => {

    });

  });

  describe('Static Methods', () => {

  });

});
<commit_msg>Add instantiation tests for BST<commit_after>jest.autoMockOff();
const IM = require('immutable');
const BinarySearchTree = require('../../../src/binary_trees/BinarySearchTree');
const BSTNode = require('../../../src/binary_trees/BSTNode');

describe('BinarySearchTree Operations', () => {

  describe('Instantiation', () => {
    let bst = new BinarySearchTree(),
        node = new BSTNode(0, "first node value", null, null, 1),
        bstWithNode = new BinarySearchTree(null, node);

    it('instantiates empty, checks size', () => {
      expect(bst.size).toBe(0);
    });

    it('instantiates with a root node, checks size', () => {
      expect(bstWithNode.size).toBe(1);
    });

    it('instantiates with a default comparator', () => {
      expect(bst.comparator).toBeDefined();
      expect(typeof bst.comparator).toBe('function');
    });

    it('instantiates with a custom comparator', () => {
      let comp = () => 0, compBST = new BinarySearchTree(comp);
      expect(compBST.comparator).toBe(comp);
      expect(typeof compBST.comparator).toBe('function');
    });

  });

  describe('Internal Methods', () => {

  });

  describe('Basic Instance Methods', () => {

  });

  describe('Basic Getters', () => {

    it('gets size', () => {

    });

    it('gets min', () => {

    });

    it('gets max', () => {

    });

    it('gets keys', () => {

    });

    it('gets values', () => {

    });

  });

  describe('Static Methods', () => {

  });

});
<|endoftext|>


<commit_before>/*global
    window, document, alert, XMLHttpRequest
 */

/* Nainwaklet module */
var Nainwaklet = (function () {
    'use strict';

    var nainwakOrigin = 'http://www.nainwak.com',
        nainwakGameUrl = nainwakOrigin + '/jeu/index.php',
        script = document.scripts[document.scripts.length - 1],
        scriptUrl = script.src,
        scriptBaseUrl = scriptUrl.substring(0, scriptUrl.lastIndexOf('/') + 1),
        scriptChannel = script.getAttribute('data-channel'),
        log = (window.console)
            ? Function.prototype.bind(window.console.log, window.console)
            : function () {
                return;
            };

    function extend(target, source) {
        Object.keys(source).forEach(function (key) {
            if (target[key] === undefined) {
                target[key] = source[key];
            }
        });
        return target;
    }

    function httpGet(url, processResponse) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);  // asynchronous
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {  // response received and loaded
                processResponse({
                    status: xhr.status,
                    body: xhr.responseText
                });
            }
        };
        xhr.send(null);
    }

    function buildQueryParams(params) {
        var pairs = [];
        Object.keys(params).forEach(function (key) {
            var value = params[key],
                encodedKey = encodeURIComponent(key),
                encodedValue = encodeURIComponent(value);
            pairs.push(encodedKey + '=' + encodedValue);
        });
        return pairs.join('&');
    }

    /*
    function parseUrl(url) {
        var a = document.createElement('a');
        a.href = url;
        return a;
    }
    */

    function generateRandomGuestName() {
        var id = Math.round(Math.random() * 1000);
        return 'guest' + id;
    }

    /* User factory */
    function createUser(name, avatar) {
        var defaultAvatar = scriptBaseUrl + 'avatar.png';
        return Object.freeze({
            name: name || generateRandomGuestName(),
            avatar: avatar || defaultAvatar
        });
    }

    /* Page factory */
    function createPage(name, analyze, loadParams) {
        var url = nainwakOrigin + '/jeu/' + name + '.php';

        function getUrl(IDS) {
            var params = {
                IDS: IDS
            };
            if (loadParams) {
                extend(params, loadParams);
            }
            return url + '?' + buildQueryParams(params);
        }

        function load(IDS, processResult) {
            var loadUrl = getUrl(IDS);
            httpGet(loadUrl, function (response) {
                var result = null;
                if (response.status === 200) {
                    result = analyze(response.body);
                }
                processResult(result);
            });
        }

        return Object.freeze({
            name: name,
            url: url,  // base URL without query parameters
            analyze: analyze,
            load: load
        });
    }

    /* detect page */
    var detect = (function () {
        function findLocalization(html) {
            // example:
            // Position (13,5) sur "Ronain Graou" |b95eb2f716c500db6|
            var re = /Position\s\((\d*),(\d*)\)\ssur\s"([^"]*)"/im,
                match = re.exec(html);
            if (match) {
                return {
                    position: [match[1], match[2]],
                    world: match[3]
                };
            }
        }

        function findDwarfs(html) {
            // example:
            // tabavat[1] = ["33966", "avatar_guilde/41ddb8ad2c2be408e27352accf1cc0b6559466bb.png", "Le PheniX", '[Gnouille] [#!]', "13794", "2", "Diablotin(e)", "0", "13", "5", ""Le PheniX est un oiseau qui symbolise l'immortalité et la résurrection." A quoi bon me tuer ?!?", "o", "", "0"];
            // (id, photo, nom, tag, barbe, classe, cote, distance, x, y, description, attaquer, gifler, estCible)
            var dwarfs = [];
            // TODO: not implemented
            return dwarfs;
        }

        function findObjects(html) {
            // example:
            // tabobjet[1] = [3609504, "objets/oreiller.gif", "Oreiller", 1, 12, 5, "ARME", 1262774];
            // (id, photo, nom, distance, x, y, poussiere, prendre)
            var objects = [];
            // TODO: not implemented
            return objects;
        }

        function analyze(html) {
            log('Analyzing detect...');
            return {
                localization: findLocalization(html),
                dwarfs: findDwarfs(html),
                objects: findObjects(html)
            };
        }

        return createPage('detect', analyze, {});
    }());

    /* pages */
    var pages = (function () {
        var list = [
                detect
                /*
                // Evénements (tous types d'evts sur 10 jours)
                createPage('even', log, {duree: 240, type: 'ALL'}),
                // Fiche de perso
                createPage('perso', log),
                // Inventaire
                createPage('invent', log),
                // Encyclopédie
                createPage('encyclo', log)
                */
            ],
            get = function (nameOrUrl) {
                var results = list.filter(function (page) {
                    return (page.name === nameOrUrl) || (page.url === nameOrUrl);
                });

                if (results) {
                    return results[0];
                }
            };

        return Object.freeze({
            get: get,
            list: list
        });
    }());

    /* Spy factory */
    function createSpy(frame) {  /*user, channel*/
        //IDS = parseQueryParams(frame.location).IDS,
        var infoLoaded = function () {
                var contentWindow = frame.contentWindow,
                    doc = contentWindow.document,
                    docEl = doc.documentElement,
                    html = docEl.outerHTML,  // no doctype but we don't mind
                    location = contentWindow.location,
                    url = location.origin + location.pathname,
                    page = pages.get(url),
                    result;

                if (page) {
                    result = page.analyze(html);
                    log(result);
                }
            },
            enabled = false,
            enable = function (value) {
                var oldEnabled = enabled;

                // update the status (and convert to boolean, just in case)
                enabled = !!value;

                if (oldEnabled === enabled) {  // nothing to do
                    return;
                }

                // register or unregister the load event handler
                if (enabled) {
                    frame.addEventListener('load', infoLoaded, false);
                } else {
                    frame.removeEventListener('load', infoLoaded, false);
                }
            };

        // start enabled
        enable(true);

        return Object.freeze({
            get enabled() {
                return enabled;
            },
            set enabled(value) {
                enable(value);
            }
        });
    }

    /* Hub factory */
    function createHub(container) {  /*, user, channel*/
        var containerContent = null,  // original content of the container
            ui = (function () {  // create the hub UI
                var iframe = document.createElement('iframe'),
                    hubUrl = scriptBaseUrl + 'hub.html';
                iframe.setAttribute('class', 'nainwaklet-hub');
                iframe.setAttribute('src', hubUrl);
                iframe.style.width = '100%';
                iframe.style.border = 0;
                return iframe;
            }()),
            /*
            cssLink = (function () {
                var link = document.createElement('link'),
                    cssUrl = scriptUrl.replace('.js', '.css');
                link.setAttribute('rel', 'stylesheet');
                link.setAttribute('type', 'text/css');
                link.setAttribute('href', cssUrl);
                return link;
            }()),
            */
            enabled = false,
            enable = function (value) {
                var oldEnabled = enabled;

                // update the status (and convert to boolean, just in case)
                enabled = !!value;

                if (enabled === oldEnabled) {  // nothing to do
                    return;
                }

                if (enabled) {
                    //var doc = container.ownerDocument;
                    //    head = doc.getElementsByTagName('head')[0]

                    // add the CSS element to the head
                    //head.appendChild(cssLink);

                    // backup the initial content
                    containerContent = container.innerHTML;

                    // replace by our UI
                    container.innerHTML = '';
                    container.appendChild(ui);
                } else {
                    // restore the initial content
                    container.innerHTML = containerContent;
                    containerContent = null;

                    // remove the CSS element
                    //cssLink.parentNode.removeChild(cssLink);
                }
            };

        // start enabled
        enable(true);

        return Object.freeze({
            get enabled() {
                return enabled;
            },
            set enabled(value) {
                enable(value);
            }
        });
    }

    /* Application factory */
    function createApplication(conf) {
        var _conf = conf || {},
            user = _conf.user || createUser(),
            channel = _conf.channel || scriptChannel || 'default',
            container = _conf.container || window.document.body,
            spyFrame = _conf.spyFrame,  // should be the frame element
            hub,
            spy;

        if (container) {
            hub = createHub(container, user, channel);
        }

        if (spyFrame) {
            spy = createSpy(spyFrame, user, channel);
        }

        function destroy() {
            if (spy) {
                spy.enabled = false;
            }

            if (hub) {
                hub.enabled = false;
            }
        }

        return Object.freeze({
            user: user,
            hub: hub,
            spy: spy,
            destroy: destroy
        });
    }

    /* Check if we are on the Nainwak game page */
    function isOnNainwakGame() {
        var loc = window.location,
            pageUrl = loc.origin + loc.pathname;
        return pageUrl === nainwakGameUrl;
    }

    /* Get the Nainwak User info from the menu frame */
    function getNainwakUser() {
        var frame = window.frames.menu,
            doc = frame.document,
            title = doc.querySelector('.news-titre'),
            name = title.querySelector('td:last-child').innerHTML,
            avatar = title.querySelector('td:first-child img').src;
        return createUser(name, avatar);
    }

    function augmentApiOnNainwakGame(api) {
        if (isOnNainwakGame()) {
            // augment the API with an application object tailored for the Nainwak game
            api.app = createApplication({
                user: getNainwakUser(),
                container: window.frames.pub.document.body,
                spyFrame: window.frames.info.frameElement
            });
        }
    }

    /********************/
    /* Bookmarklet part */
    /********************/
    function getInjectionUrl(channel) {
        var lines = [
            'javascript:(function () {',
            '    var w = window,',
            '        l = w.location,',
            '        u = l.origin + l.pathname,',
            '        d = document,',
            '        b = d.body,',
            '        n = "Nainwaklet",',
            '        i = n + "Script",',
            '        s = d.getElementById(i);',
            '    if (u === "' + nainwakGameUrl + '") {',
            '        if (s) {',
            '            w[n].app.destroy();',
            '            w[n] = null;',
            '            b.removeChild(s);',
            '        } else {',
            '            s = d.createElement("script");',
            '            s.id = i;',
            '            s.type = "text/javascript";',
            '            s.src = "' + scriptUrl + '";',
            '            s.async = false;',
            '            s.setAttribute("data-channel", "' + channel + '");',
            '            b.appendChild(s);',
            '        }',
            '    } else {',
            '        alert("Ne fonctionne que sur la page jeu de Nainwak");',
            '    }',
            '}())'
        ];
        return lines.join('\n').replace(/\s+/g, ' ');
    }

    function initializeButtons(buttons) {
        var _buttons = buttons || document.querySelectorAll('.nainwaklet-button');
        Array.prototype.forEach.call(_buttons, function (button) {
            var channel = button.getAttribute('data-channel'),
                href = getInjectionUrl(channel);
            button.setAttribute("href", href);
        });
    }

    // module public API
    var api = {
        pages: pages,
        createUser: createUser,
        createApplication: createApplication,
        initializeButtons: initializeButtons
    };

    // augment the API if we are on the Nainwak game page
    augmentApiOnNainwakGame(api);

    return Object.freeze(api);
}());
<commit_msg>Pass the unit test<commit_after>/*global
    window, document, alert, XMLHttpRequest
 */

/* Nainwaklet module */
var Nainwaklet = (function () {
    'use strict';

    var nainwakOrigin = 'http://www.nainwak.com',
        nainwakGameUrl = nainwakOrigin + '/jeu/index.php',
        script = document.scripts[document.scripts.length - 1],
        scriptUrl = script.src,
        scriptBaseUrl = scriptUrl.substring(0, scriptUrl.lastIndexOf('/') + 1),
        scriptChannel = script.getAttribute('data-channel'),
        log = (window.console)
            ? Function.prototype.bind(window.console.log, window.console)
            : function () {
                return;
            };

    function extend(target, source) {
        Object.keys(source).forEach(function (key) {
            if (target[key] === undefined) {
                target[key] = source[key];
            }
        });
        return target;
    }

    function httpGet(url, processResponse) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);  // asynchronous
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {  // response received and loaded
                processResponse({
                    status: xhr.status,
                    body: xhr.responseText
                });
            }
        };
        xhr.send(null);
    }

    function buildQueryParams(params) {
        var pairs = [];
        Object.keys(params).forEach(function (key) {
            var value = params[key],
                encodedKey = encodeURIComponent(key),
                encodedValue = encodeURIComponent(value);
            pairs.push(encodedKey + '=' + encodedValue);
        });
        return pairs.join('&');
    }

    /*
    function parseUrl(url) {
        var a = document.createElement('a');
        a.href = url;
        return a;
    }
    */

    function generateRandomGuestName() {
        var id = Math.round(Math.random() * 1000);
        return 'guest' + id;
    }

    /* User factory */
    function createUser(name, avatar) {
        var defaultAvatar = scriptBaseUrl + 'avatar.png';
        return Object.freeze({
            name: name || generateRandomGuestName(),
            avatar: avatar || defaultAvatar
        });
    }

    /* Page factory */
    function createPage(name, analyze, loadParams) {
        var url = nainwakOrigin + '/jeu/' + name + '.php';

        function getUrl(IDS) {
            var params = {
                IDS: IDS
            };
            if (loadParams) {
                extend(params, loadParams);
            }
            return url + '?' + buildQueryParams(params);
        }

        function load(IDS, processResult) {
            var loadUrl = getUrl(IDS);
            httpGet(loadUrl, function (response) {
                var result = null;
                if (response.status === 200) {
                    result = analyze(response.body);
                }
                processResult(result);
            });
        }

        return Object.freeze({
            name: name,
            url: url,  // base URL without query parameters
            analyze: analyze,
            load: load
        });
    }

    /* detect page */
    var detect = (function () {
        function findLocalization(html) {
            // example:
            // Position (13,5) sur "Ronain Graou" |b95eb2f716c500db6|
            var re = /Position\s\((\d+),(\d+)\)\ssur\s"([^"]*)"/i,
                match = re.exec(html);
            if (match) {
                return {
                    position: [parseInt(match[1], 10), parseInt(match[2], 10)],
                    world: match[3]
                };
            }
        }

        function findDwarfs(html) {
            // example:
            // tabavat[1] = ["33966", "avatar_guilde/41ddb8ad2c2be408e27352accf1cc0b6559466bb.png", "Le PheniX", '[Gnouille] [#!]', "13794", "2", "Diablotin(e)", "0", "13", "5", ""Le PheniX est un oiseau qui symbolise l'immortalité et la résurrection." A quoi bon me tuer ?!?", "o", "", "0"];
            // (id, photo, nom, tag, barbe, classe, cote, distance, x, y, description, attaquer, gifler, estCible)
            var dwarfs = [];
            // TODO: not implemented
            return dwarfs;
        }

        function findObjects(html) {
            // example:
            // tabobjet[1] = [3609504, "objets/oreiller.gif", "Oreiller", 1, 12, 5, "ARME", 1262774];
            // (id, photo, nom, distance, x, y, poussiere, prendre)
            var objects = [];
            // TODO: not implemented
            return objects;
        }

        function analyze(html) {
            log('Analyzing detect...');
            return {
                localization: findLocalization(html),
                dwarfs: findDwarfs(html),
                objects: findObjects(html)
            };
        }

        return createPage('detect', analyze, {});
    }());

    /* pages */
    var pages = (function () {
        var list = [
                detect
                /*
                // Evénements (tous types d'evts sur 10 jours)
                createPage('even', log, {duree: 240, type: 'ALL'}),
                // Fiche de perso
                createPage('perso', log),
                // Inventaire
                createPage('invent', log),
                // Encyclopédie
                createPage('encyclo', log)
                */
            ],
            get = function (nameOrUrl) {
                var results = list.filter(function (page) {
                    return (page.name === nameOrUrl) || (page.url === nameOrUrl);
                });

                if (results) {
                    return results[0];
                }
            };

        return Object.freeze({
            get: get,
            list: list
        });
    }());

    /* Spy factory */
    function createSpy(frame) {  /*user, channel*/
        //IDS = parseQueryParams(frame.location).IDS,
        var infoLoaded = function () {
                var contentWindow = frame.contentWindow,
                    doc = contentWindow.document,
                    docEl = doc.documentElement,
                    html = docEl.outerHTML,  // no doctype but we don't mind
                    location = contentWindow.location,
                    url = location.origin + location.pathname,
                    page = pages.get(url),
                    result;

                if (page) {
                    result = page.analyze(html);
                    log(result);
                }
            },
            enabled = false,
            enable = function (value) {
                var oldEnabled = enabled;

                // update the status (and convert to boolean, just in case)
                enabled = !!value;

                if (oldEnabled === enabled) {  // nothing to do
                    return;
                }

                // register or unregister the load event handler
                if (enabled) {
                    frame.addEventListener('load', infoLoaded, false);
                } else {
                    frame.removeEventListener('load', infoLoaded, false);
                }
            };

        // start enabled
        enable(true);

        return Object.freeze({
            get enabled() {
                return enabled;
            },
            set enabled(value) {
                enable(value);
            }
        });
    }

    /* Hub factory */
    function createHub(container) {  /*, user, channel*/
        var containerContent = null,  // original content of the container
            ui = (function () {  // create the hub UI
                var iframe = document.createElement('iframe'),
                    hubUrl = scriptBaseUrl + 'hub.html';
                iframe.setAttribute('class', 'nainwaklet-hub');
                iframe.setAttribute('src', hubUrl);
                iframe.style.width = '100%';
                iframe.style.border = 0;
                return iframe;
            }()),
            /*
            cssLink = (function () {
                var link = document.createElement('link'),
                    cssUrl = scriptUrl.replace('.js', '.css');
                link.setAttribute('rel', 'stylesheet');
                link.setAttribute('type', 'text/css');
                link.setAttribute('href', cssUrl);
                return link;
            }()),
            */
            enabled = false,
            enable = function (value) {
                var oldEnabled = enabled;

                // update the status (and convert to boolean, just in case)
                enabled = !!value;

                if (enabled === oldEnabled) {  // nothing to do
                    return;
                }

                if (enabled) {
                    //var doc = container.ownerDocument;
                    //    head = doc.getElementsByTagName('head')[0]

                    // add the CSS element to the head
                    //head.appendChild(cssLink);

                    // backup the initial content
                    containerContent = container.innerHTML;

                    // replace by our UI
                    container.innerHTML = '';
                    container.appendChild(ui);
                } else {
                    // restore the initial content
                    container.innerHTML = containerContent;
                    containerContent = null;

                    // remove the CSS element
                    //cssLink.parentNode.removeChild(cssLink);
                }
            };

        // start enabled
        enable(true);

        return Object.freeze({
            get enabled() {
                return enabled;
            },
            set enabled(value) {
                enable(value);
            }
        });
    }

    /* Application factory */
    function createApplication(conf) {
        var _conf = conf || {},
            user = _conf.user || createUser(),
            channel = _conf.channel || scriptChannel || 'default',
            container = _conf.container || window.document.body,
            spyFrame = _conf.spyFrame,  // should be the frame element
            hub,
            spy;

        if (container) {
            hub = createHub(container, user, channel);
        }

        if (spyFrame) {
            spy = createSpy(spyFrame, user, channel);
        }

        function destroy() {
            if (spy) {
                spy.enabled = false;
            }

            if (hub) {
                hub.enabled = false;
            }
        }

        return Object.freeze({
            user: user,
            hub: hub,
            spy: spy,
            destroy: destroy
        });
    }

    /* Check if we are on the Nainwak game page */
    function isOnNainwakGame() {
        var loc = window.location,
            pageUrl = loc.origin + loc.pathname;
        return pageUrl === nainwakGameUrl;
    }

    /* Get the Nainwak User info from the menu frame */
    function getNainwakUser() {
        var frame = window.frames.menu,
            doc = frame.document,
            title = doc.querySelector('.news-titre'),
            name = title.querySelector('td:last-child').innerHTML,
            avatar = title.querySelector('td:first-child img').src;
        return createUser(name, avatar);
    }

    function augmentApiOnNainwakGame(api) {
        if (isOnNainwakGame()) {
            // augment the API with an application object tailored for the Nainwak game
            api.app = createApplication({
                user: getNainwakUser(),
                container: window.frames.pub.document.body,
                spyFrame: window.frames.info.frameElement
            });
        }
    }

    /********************/
    /* Bookmarklet part */
    /********************/
    function getInjectionUrl(channel) {
        var lines = [
            'javascript:(function () {',
            '    var w = window,',
            '        l = w.location,',
            '        u = l.origin + l.pathname,',
            '        d = document,',
            '        b = d.body,',
            '        n = "Nainwaklet",',
            '        i = n + "Script",',
            '        s = d.getElementById(i);',
            '    if (u === "' + nainwakGameUrl + '") {',
            '        if (s) {',
            '            w[n].app.destroy();',
            '            w[n] = null;',
            '            b.removeChild(s);',
            '        } else {',
            '            s = d.createElement("script");',
            '            s.id = i;',
            '            s.type = "text/javascript";',
            '            s.src = "' + scriptUrl + '";',
            '            s.async = false;',
            '            s.setAttribute("data-channel", "' + channel + '");',
            '            b.appendChild(s);',
            '        }',
            '    } else {',
            '        alert("Ne fonctionne que sur la page jeu de Nainwak");',
            '    }',
            '}())'
        ];
        return lines.join('\n').replace(/\s+/g, ' ');
    }

    function initializeButtons(buttons) {
        var _buttons = buttons || document.querySelectorAll('.nainwaklet-button');
        Array.prototype.forEach.call(_buttons, function (button) {
            var channel = button.getAttribute('data-channel'),
                href = getInjectionUrl(channel);
            button.setAttribute("href", href);
        });
    }

    // module public API
    var api = {
        pages: pages,
        createUser: createUser,
        createApplication: createApplication,
        initializeButtons: initializeButtons
    };

    // augment the API if we are on the Nainwak game page
    augmentApiOnNainwakGame(api);

    return Object.freeze(api);
}());
<|endoftext|>


<commit_before>Ember.libraries.register('Ember Simple Token', '1.1.3');
<commit_msg>version upgrade<commit_after>Ember.libraries.register('Ember Simple Token', '1.1.4');
<|endoftext|>


<commit_before>/*
*
*	Impaler abilities
*
*/
abilities[5] =[

// 	First Ability: Electrified Hair
{
	//	Type : Can be "onQuery","onStartPhase","onDamage"
	trigger : "onDamage", //Just for display purpose the effect is in 2nd Ability

	// 	require() :
	require : function(){return this.testRequirements();},

	// 	require() :
	require : function(damage){
		this.setUsed(false); //Can be triggered as many times
		if( this.creature.electrifiedHair == 100 ) return false;
		return this.testRequirements();
	},

	//	activate() :
	activate : function(damage) {
		if(!(this.creature.electrifiedHair+1)) this.creature.electrifiedHair = 0;
		var capacity = 100-this.creature.electrifiedHair;
		if(damage.damages.shock){
			if(damage.damages.shock>0){
				this.creature.electrifiedHair += (damage.damages.shock/2>capacity)
				? capacity
				: damage.damages.shock/2;
				damage.damages.shock = (damage.damages.shock/2>capacity)
				? damage.damages.shock-capacity
				: damage.damages.shock/2;
			}
		}
		this.end();
		return damage; //Return Damage
	},
},

// 	Second Ability: Deadly Jab
{
	//	Type : Can be "onQuery","onStartPhase","onDamage"
	trigger : "onQuery",

	damages : {
		pierce : 20
	},

	// 	require() :
	require : function(){
		if( !this.testRequirements() ) return false;

		if( !this.atLeastOneTarget( G.grid.getHexMap(this.creature.x-3,this.creature.y-2,0,false,frontnback3hex),"ennemy" ) ){
			this.message = G.msg.abilities.notarget;
			return false;
		}
		return true;
	},

	// 	query() :
	query : function(){

		var ability = this;
		var creature = this.creature;

		G.grid.queryCreature({
			fnOnConfirm : ability.activate, //fnOnConfirm
			team : 0, //Team, 0 = ennemies
			id : creature.id,
			flipped : creature.flipped,
			hexs : G.grid.getHexMap(creature.x-3,creature.y-2,0,false,frontnback3hex),
			args : {creature:creature, ability: ability}
		});
	},

	//	activate() :
	activate : function(target,args) {
		var ability = args.ability;
		ability.end();

		var finalDmg = $j.extend({ poison:0, shock:0 },ability.damages); //Copy object

		//Poison Bonus
		args.creature.effects.each(function(){
			if(this.trigger == "poisonous_vine_perm"){
				finalDmg.poison += 1;
			}else if(this.trigger == "poisonous_vine"){
				finalDmg.poison += 20;
			}
		});

		//Jab Bonus
		finalDmg.pierce += target.size*2;

		//Electrified Hair Bonus
		if(ability.creature.electrifiedHair){
			if(ability.creature.electrifiedHair>25){
				finalDmg.shock += 25;
				ability.creature.electrifiedHair -= 25;
			}else if(ability.creature.electrifiedHair>0){
				finalDmg.shock += ability.creature.electrifiedHair;
				ability.creature.electrifiedHair = 0;
			}
		}

		var damage = new Damage(
			args.creature, //Attacker
			"target", //Attack Type
			finalDmg, //Damage Type
			1, //Area
			[] //Effects
		)
		target.takeDamage(damage);
	},
},

// 	Thirt Ability: Poisonous Vine
{
	//	Type : Can be "onQuery","onStartPhase","onDamage"
	trigger : "onQuery",

	// 	require() :
	require : function(){return this.testRequirements();},

	// 	query() :
	query : function(){
		this.activate()
	},

	//	activate() :
	activate : function() {
		this.end();
		var effect = new Effect("Poisonous Vine",this.creature,this.creature,"poisonous_vine",{
			turnLifetime : 1,
		});
		this.creature.addEffect(effect);

		var effect = new Effect("",this.creature,this.creature,"poisonous_vine_perm",{
		});
		this.creature.addEffect(effect);
		//TODO add animation
	},
},

// 	Fourth Ability: Super Slash
{
	//	Type : Can be "onQuery","onStartPhase","onDamage"
	trigger : "onQuery",

	// 	require() :
	require : function(){
		if( !this.testRequirements() ) return false;

		return true;
	},

	damages : {
		slash : 20,
		frost : 10
	},

	map : [  [0,0,0,0],
			[0,1,0,0],
			 [0,1,0,0],//origin line
			[0,1,0,0],
			 [0,0,0,0]],

	// 	query() :
	query : function(){
		var ability = this;

		G.grid.queryChoice({
			fnOnConfirm : ability.activate,
			team : 3,
			requireCreature : 0,
			id : this.creature.id,
			args : {creature:this.creature, ability:ability},
			flipped : this.creature.flipped,
			choices : [
				G.grid.getHexMap(this.creature.x,this.creature.y-2,0,false,this.map),
				G.grid.getHexMap(this.creature.x-this.creature.size+1,this.creature.y-2,0,true,this.map)
			],
		})
	},

	//	activate() :
	activate : function(hexs,args) {
		var ability = args.ability;
		ability.end();

		var targets = ability.getTargets(hexs);

		targets.each(function(){
			if(this != window){

				var finalDmg = $j.extend({ poison:0, shock:0 },ability.damages); //Copy object

				//Poison Bonus
				args.creature.effects.each(function(){
					if(this.trigger == "poisonous_vine_perm"){
						finalDmg.poison += 1;
					}else if(this.trigger == "poisonous_vine"){
						finalDmg.poison += 20;
					}
				});

				//Electrified Hair Bonus
				if(ability.creature.electrifiedHair){
					if(ability.creature.electrifiedHair>25){
						finalDmg.shock += 25;
						ability.creature.electrifiedHair -= 25;
					}else if(ability.creature.electrifiedHair>0){
						finalDmg.shock += ability.creature.electrifiedHair;
						ability.creature.electrifiedHair = 0;
					}
				}

				var damage = new Damage(
					args.creature, //Attacker
					"target", //Attack Type
					finalDmg, //Damage Type
					1, //Area
					[] //Effects
				)
				this.target.takeDamage(damage);
			}
		});
	},
}

];
<commit_msg>and more...<commit_after>/*
*
*	Impaler abilities
*
*/
abilities[5] =[

// 	First Ability: Electrified Hair
{
	//	Type : Can be "onQuery","onStartPhase","onDamage"
	trigger : "onDamage", //Just for display purpose the effect is in 2nd Ability

	// 	require() :
	require : function(){return this.testRequirements();},

	// 	require() :
	require : function(damage){
		this.setUsed(false); //Can be triggered as many times
		if( this.creature.electrifiedHair == 100 ) return false;
		return this.testRequirements();
	},

	//	activate() :
	activate : function(damage) {
		if(!(this.creature.electrifiedHair+1)) this.creature.electrifiedHair = 0;
		var capacity = 100-this.creature.electrifiedHair;
		if(damage.damages.shock){
			if(damage.damages.shock>0){
				this.creature.electrifiedHair += (damage.damages.shock/2>capacity)
				? capacity
				: damage.damages.shock/2;
				damage.damages.shock = (damage.damages.shock/2>capacity)
				? damage.damages.shock-capacity
				: damage.damages.shock/2;
			}
		}
		this.end();
		return damage; //Return Damage
	},
},

// 	Second Ability: Deadly Jab
{
	//	Type : Can be "onQuery","onStartPhase","onDamage"
	trigger : "onQuery",

	damages : {
		pierce : 20
	},

	// 	require() :
	require : function(){
		if( !this.testRequirements() ) return false;

		if( !this.atLeastOneTarget( G.grid.getHexMap(this.creature.x-3,this.creature.y-2,0,false,frontnback3hex),"ennemy" ) ){
			this.message = G.msg.abilities.notarget;
			return false;
		}
		return true;
	},

	// 	query() :
	query : function(){

		var ability = this;
		var creature = this.creature;

		G.grid.queryCreature({
			fnOnConfirm : ability.activate, //fnOnConfirm
			team : 0, //Team, 0 = ennemies
			id : creature.id,
			flipped : creature.flipped,
			hexs : G.grid.getHexMap(creature.x-3,creature.y-2,0,false,frontnback3hex),
			args : {creature:creature, ability: ability}
		});
	},

	//	activate() :
	activate : function(target,args) {
		var ability = args.ability;
		ability.end();

		var finalDmg = $j.extend({ poison:0, shock:0 },ability.damages); //Copy object

		//Poison Bonus
		args.creature.effects.each(function(){
			if(this.trigger == "poisonous_vine_perm"){
				finalDmg.poison += 1;
			}else if(this.trigger == "poisonous_vine"){
				finalDmg.poison += 20;
			}
		});

		//Jab Bonus
		finalDmg.pierce += ability.creature.travelDist*2;

		//Electrified Hair Bonus
		if(ability.creature.electrifiedHair){
			if(ability.creature.electrifiedHair>25){
				finalDmg.shock += 25;
				ability.creature.electrifiedHair -= 25;
			}else if(ability.creature.electrifiedHair>0){
				finalDmg.shock += ability.creature.electrifiedHair;
				ability.creature.electrifiedHair = 0;
			}
		}

		var damage = new Damage(
			args.creature, //Attacker
			"target", //Attack Type
			finalDmg, //Damage Type
			1, //Area
			[] //Effects
		)
		target.takeDamage(damage);
	},
},

// 	Thirt Ability: Poisonous Vine
{
	//	Type : Can be "onQuery","onStartPhase","onDamage"
	trigger : "onQuery",

	// 	require() :
	require : function(){return this.testRequirements();},

	// 	query() :
	query : function(){
		this.activate()
	},

	//	activate() :
	activate : function() {
		this.end();
		var effect = new Effect("Poisonous Vine",this.creature,this.creature,"poisonous_vine",{
			turnLifetime : 1,
		});
		this.creature.addEffect(effect);

		var effect = new Effect("",this.creature,this.creature,"poisonous_vine_perm",{
		});
		this.creature.addEffect(effect);
		//TODO add animation
	},
},

// 	Fourth Ability: Super Slash
{
	//	Type : Can be "onQuery","onStartPhase","onDamage"
	trigger : "onQuery",

	// 	require() :
	require : function(){
		if( !this.testRequirements() ) return false;

		return true;
	},

	damages : {
		slash : 20,
		frost : 10
	},

	map : [  [0,0,0,0],
			[0,1,0,0],
			 [0,1,0,0],//origin line
			[0,1,0,0],
			 [0,0,0,0]],

	// 	query() :
	query : function(){
		var ability = this;

		G.grid.queryChoice({
			fnOnConfirm : ability.activate,
			team : 3,
			requireCreature : 0,
			id : this.creature.id,
			args : {creature:this.creature, ability:ability},
			flipped : this.creature.flipped,
			choices : [
				G.grid.getHexMap(this.creature.x,this.creature.y-2,0,false,this.map),
				G.grid.getHexMap(this.creature.x-this.creature.size+1,this.creature.y-2,0,true,this.map)
			],
		})
	},

	//	activate() :
	activate : function(hexs,args) {
		var ability = args.ability;
		ability.end();

		var targets = ability.getTargets(hexs);

		targets.each(function(){
			if(this != window){

				var finalDmg = $j.extend({ poison:0, shock:0 },ability.damages); //Copy object

				//Poison Bonus
				args.creature.effects.each(function(){
					if(this.trigger == "poisonous_vine_perm"){
						finalDmg.poison += 1;
					}else if(this.trigger == "poisonous_vine"){
						finalDmg.poison += 20;
					}
				});

				//Electrified Hair Bonus
				if(ability.creature.electrifiedHair){
					if(ability.creature.electrifiedHair>25){
						finalDmg.shock += 25;
						ability.creature.electrifiedHair -= 25;
					}else if(ability.creature.electrifiedHair>0){
						finalDmg.shock += ability.creature.electrifiedHair;
						ability.creature.electrifiedHair = 0;
					}
				}

				var damage = new Damage(
					args.creature, //Attacker
					"target", //Attack Type
					finalDmg, //Damage Type
					1, //Area
					[] //Effects
				)
				this.target.takeDamage(damage);
			}
		});
	},
}

];
<|endoftext|>