<commit_before>Slingshot.S3Storage = {

  accessId: "AWSAccessKeyId",
  secretKey: "AWSSecretAccessKey",

  directiveMatch: {
    bucket: String,
    bucketUrl: Match.OneOf(String, Function),

    region: Match.Where(function (region) {
      check(region, String);

      return /^[a-z]{2}-\w+-\d+$/.test(region);
    }),

    AWSAccessKeyId: String,
    AWSSecretAccessKey: String,

    acl: Match.Optional(Match.Where(function (acl) {
      check(acl, String);

      return [
          "private",
          "public-read",
          "public-read-write",
          "authenticated-read",
          "bucket-owner-read",
          "bucket-owner-full-control",
          "log-delivery-write"
        ].indexOf(acl) >= 0;
    })),

    key: Match.OneOf(String, Function),

    expire: Match.Where(function (expire) {
      check(expire, Number);

      return expire > 0;
    }),

    cacheControl: Match.Optional(String),
    contentDisposition: Match.Optional(Match.OneOf(String, Function, null))
  },

  directiveDefault: _.chain(Meteor.settings)
    .pick("AWSAccessKeyId", "AWSSecretAccessKey")
    .extend({
      bucket: Meteor.settings.S3Bucket,
      bucketUrl: function (bucket, region) {
        var bucketDomain = "s3-" + region + ".amazonaws.com";
        if (region === "us-east-1")
          bucketDomain = "s3.amazonaws.com";

        if (bucket.indexOf(".") !== -1)
          return "https://" + bucketDomain + "/" + bucket;

        return "https://" + bucket + "." + bucketDomain;
      },
      region: Meteor.settings.AWSRegion || "us-east-1",
      expire: 5 * 60 * 1000 //in 5 minutes
    })
    .value(),

  getContentDisposition: function (method, directive, file, meta) {
    var getContentDisposition = directive.contentDisposition;

    if (!_.isFunction(getContentDisposition)) {
      getContentDisposition = function () {
        var filename = file.name && encodeURIComponent(file.name);

        return directive.contentDisposition || filename &&
          "inline; filename=\"" + filename + "\"; filename*=utf-8''" +
          filename;
      };
    }

    return getContentDisposition.call(method, file, meta);
  },

  /**
   *
   * @param {{userId: String}} method
   * @param {Directive} directive
   * @param {FileInfo} file
   * @param {Object} [meta]
   *
   * @returns {UploadInstructions}
   */

  upload: function (method, directive, file, meta) {
    var policy = new Slingshot.StoragePolicy()
          .expireIn(directive.expire)
          .contentLength(0, Math.min(file.size, directive.maxSize || Infinity)),

        payload = {
          key: _.isFunction(directive.key) ?
            directive.key.call(method, file, meta) : directive.key,

          bucket: directive.bucket,

          "Content-Type": file.type,
          "acl": directive.acl,

          "Cache-Control": directive.cacheControl,
          "Content-Disposition": this.getContentDisposition(method, directive,
            file, meta)
        },

        bucketUrl = _.isFunction(directive.bucketUrl) ?
          directive.bucketUrl(directive.bucket, directive.region) :
          directive.bucketUrl,

        downloadUrl = [
          (directive.cdn || bucketUrl),
          payload.key
        ].map(function (part) {
            return part.replace(/\/+$/, '');
          }).join("/");

    this.applySignature(payload, policy, directive);

    return {
      upload: bucketUrl,
      download: downloadUrl,
      postData: [{
        name: "key",
        value: payload.key
      }].concat(_.chain(payload).omit("key").map(function (value, name) {
          return !_.isUndefined(value) && {
              name: name,
              value: value
            };
        }).compact().value())
    };
  },

  /** Applies signature an upload payload
   *
   * @param {Object} payload - Data to be upload along with file
   * @param {Slingshot.StoragePolicy} policy
   * @param {Directive} directive
   */

  applySignature: function (payload, policy, directive) {
    var now =  new Date(),
        today = now.getUTCFullYear() + formatNumber(now.getUTCMonth() + 1, 2) +
          formatNumber(now.getUTCDate(), 2),
        service = "s3";

    _.extend(payload, {
      "x-amz-algorithm": "AWS4-HMAC-SHA256",
      "x-amz-credential": [
        directive[this.accessId],
        today,
        directive.region,
        service,
        "aws4_request"
      ].join("/"),
      "x-amz-date": today + "T000000Z"
    });

    payload.policy = policy.match(payload).stringify();
    payload["x-amz-signature"] = this.signAwsV4(payload.policy,
      directive[this.secretKey], today, directive.region, service);
  },

  /** Generate a AWS Signature Version 4
   *
   * @param {String} policy - Base64 encoded policy to sign.
   * @param {String} secretKey - AWSSecretAccessKey
   * @param {String} date - Signature date (yyyymmdd)
   * @param {String} region - AWS Data-Center region
   * @param {String} service - type of service to use
   * @returns {String} hex encoded HMAC-256 signature
   */

  signAwsV4: function (policy, secretKey, date, region, service) {
    var dateKey = hmac256("AWS4" + secretKey, date),
        dateRegionKey = hmac256(dateKey, region),
        dateRegionServiceKey= hmac256(dateRegionKey, service),
        signingKey = hmac256(dateRegionServiceKey, "aws4_request");

    return hmac256(signingKey, policy, "hex");
  }
};

Slingshot.S3Storage.TempCredentials = _.defaults({

  directiveMatch: _.chain(Slingshot.S3Storage.directiveMatch)
    .omit("AWSAccessKeyId", "AWSSecretAccessKey")
    .extend({
      temporaryCredentials: Function
    })
    .value(),

  directiveDefault: _.omit(Slingshot.S3Storage.directiveDefault,
    "AWSAccessKeyId", "AWSSecretAccessKey"),

  applySignature: function (payload, policy, directive) {
    var credentials = directive.temporaryCredentials(directive.expire);

    check(credentials, Match.ObjectIncluding({
      AccessKeyId: Slingshot.S3Storage.directiveMatch.AWSAccessKeyId,
      SecretAccessKey: Slingshot.S3Storage.directiveMatch.AWSSecretAccessKey,
      SessionToken: String
    }));

    payload["x-amz-security-token"] = credentials.SessionToken;

    return Slingshot.S3Storage.applySignature
      .call(this, payload, policy, _.defaults({
        AWSAccessKeyId: credentials.AccessKeyId,
        AWSSecretAccessKey: credentials.SecretAccessKey
      }, directive));
  }
}, Slingshot.S3Storage);

function formatNumber(num, digits) {
  var string = String(num);

  return Array(digits - string.length + 1).join("0").concat(string);
}

var crypto = Npm.require("crypto");

function hmac256(key, data, encoding) {
  /* global Buffer: false */
  return crypto
    .createHmac("sha256", key)
    .update(new Buffer(data, "utf-8"))
    .digest(encoding);
}
<commit_msg>Add S3 SSE support.<commit_after>Slingshot.S3Storage = {

  accessId: "AWSAccessKeyId",
  secretKey: "AWSSecretAccessKey",

  directiveMatch: {
    bucket: String,
    bucketUrl: Match.OneOf(String, Function),

    region: Match.Where(function (region) {
      check(region, String);

      return /^[a-z]{2}-\w+-\d+$/.test(region);
    }),

    AWSAccessKeyId: String,
    AWSSecretAccessKey: String,

    acl: Match.Optional(Match.Where(function (acl) {
      check(acl, String);

      return [
          "private",
          "public-read",
          "public-read-write",
          "authenticated-read",
          "bucket-owner-read",
          "bucket-owner-full-control",
          "log-delivery-write"
        ].indexOf(acl) >= 0;
    })),

    key: Match.OneOf(String, Function),

    expire: Match.Where(function (expire) {
      check(expire, Number);

      return expire > 0;
    }),

    cacheControl: Match.Optional(String),
    contentDisposition: Match.Optional(Match.OneOf(String, Function, null))
  },

  directiveDefault: _.chain(Meteor.settings)
    .pick("AWSAccessKeyId", "AWSSecretAccessKey")
    .extend({
      bucket: Meteor.settings.S3Bucket,
      bucketUrl: function (bucket, region) {
        var bucketDomain = "s3-" + region + ".amazonaws.com";
        if (region === "us-east-1")
          bucketDomain = "s3.amazonaws.com";

        if (bucket.indexOf(".") !== -1)
          return "https://" + bucketDomain + "/" + bucket;

        return "https://" + bucket + "." + bucketDomain;
      },
      region: Meteor.settings.AWSRegion || "us-east-1",
      expire: 5 * 60 * 1000 //in 5 minutes
    })
    .value(),

  getContentDisposition: function (method, directive, file, meta) {
    var getContentDisposition = directive.contentDisposition;

    if (!_.isFunction(getContentDisposition)) {
      getContentDisposition = function () {
        var filename = file.name && encodeURIComponent(file.name);

        return directive.contentDisposition || filename &&
          "inline; filename=\"" + filename + "\"; filename*=utf-8''" +
          filename;
      };
    }

    return getContentDisposition.call(method, file, meta);
  },

  /**
   *
   * @param {{userId: String}} method
   * @param {Directive} directive
   * @param {FileInfo} file
   * @param {Object} [meta]
   *
   * @returns {UploadInstructions}
   */

  upload: function (method, directive, file, meta) {
    var policy = new Slingshot.StoragePolicy()
          .expireIn(directive.expire)
          .contentLength(0, Math.min(file.size, directive.maxSize || Infinity)),

        payload = {
          key: _.isFunction(directive.key) ?
            directive.key.call(method, file, meta) : directive.key,

          bucket: directive.bucket,

          "Content-Type": file.type,
          "acl": directive.acl,

          "Cache-Control": directive.cacheControl,
          "Content-Disposition": this.getContentDisposition(method, directive,
            file, meta)
        },

        bucketUrl = _.isFunction(directive.bucketUrl) ?
          directive.bucketUrl(directive.bucket, directive.region) :
          directive.bucketUrl,

        downloadUrl = [
          (directive.cdn || bucketUrl),
          payload.key
        ].map(function (part) {
            return part.replace(/\/+$/, '');
          }).join("/");

    this.applyEncryption(payload, meta);

    this.applySignature(payload, policy, directive);

    return {
      upload: bucketUrl,
      download: downloadUrl,
      postData: [{
        name: "key",
        value: payload.key
      }].concat(_.chain(payload).omit("key").map(function (value, name) {
          return !_.isUndefined(value) && {
              name: name,
              value: value
            };
        }).compact().value())
    };
  },

  /** Applies signature an upload payload
   *
   * @param {Object} payload - Data to be upload along with file
   * @param {Slingshot.StoragePolicy} policy
   * @param {Directive} directive
   */

  applySignature: function (payload, policy, directive) {
    var now =  new Date(),
        today = now.getUTCFullYear() + formatNumber(now.getUTCMonth() + 1, 2) +
          formatNumber(now.getUTCDate(), 2),
        service = "s3";

    _.extend(payload, {
      "x-amz-algorithm": "AWS4-HMAC-SHA256",
      "x-amz-credential": [
        directive[this.accessId],
        today,
        directive.region,
        service,
        "aws4_request"
      ].join("/"),
      "x-amz-date": today + "T000000Z"
    });

    payload.policy = policy.match(payload).stringify();
    payload["x-amz-signature"] = this.signAwsV4(payload.policy,
      directive[this.secretKey], today, directive.region, service);
  },

  /** Generate a AWS Signature Version 4
   *
   * @param {String} policy - Base64 encoded policy to sign.
   * @param {String} secretKey - AWSSecretAccessKey
   * @param {String} date - Signature date (yyyymmdd)
   * @param {String} region - AWS Data-Center region
   * @param {String} service - type of service to use
   * @returns {String} hex encoded HMAC-256 signature
   */

  signAwsV4: function (policy, secretKey, date, region, service) {
    var dateKey = hmac256("AWS4" + secretKey, date),
        dateRegionKey = hmac256(dateKey, region),
        dateRegionServiceKey= hmac256(dateRegionKey, service),
        signingKey = hmac256(dateRegionServiceKey, "aws4_request");

    return hmac256(signingKey, policy, "hex");
  },

  /** Generate AWS Server-Side Encryption headers
   *
   * @param {Object} payload - Data to be upload along with file
   * @param {Object} meta - Includes SSE encryption settings in meta.sse
   */

  applyEncryption: function (payload, meta) {
    var encryptionData = {},
        sse;

    if (meta && meta.sse) {
      sse = meta.sse;

      if (sse.key) {
        encryptionData = {
          "x-amz-server-side-encryption-customer-algorithm": "AES256",
          "x-amz-server-side-encryption-customer-key": new Buffer(sse.key).toString("base64"),
          "x-amz-server-side-encryption-customer-key-MD5": md5(sse.key, "base64")
        };
      } else if (sse.kms && sse.kmsKeyId) {
        encryptionData = {
          "x-amz-server-side-encryption": "aws:kms",
          "x-amz-server-side-encryption-aws-kms-key-id": sse.kmsKeyId
        }
      } else if (sse.kms) {
        encryptionData = {
          "x-amz-server-side-encryption": "aws:kms"
        }
      } else if (sse) {
        encryptionData = {
          "x-amz-server-side-encryption": "AES256"
        }
      }

      _.extend(payload, encryptionData);
    }
  }
};

Slingshot.S3Storage.TempCredentials = _.defaults({

  directiveMatch: _.chain(Slingshot.S3Storage.directiveMatch)
    .omit("AWSAccessKeyId", "AWSSecretAccessKey")
    .extend({
      temporaryCredentials: Function
    })
    .value(),

  directiveDefault: _.omit(Slingshot.S3Storage.directiveDefault,
    "AWSAccessKeyId", "AWSSecretAccessKey"),

  applySignature: function (payload, policy, directive) {
    var credentials = directive.temporaryCredentials(directive.expire);

    check(credentials, Match.ObjectIncluding({
      AccessKeyId: Slingshot.S3Storage.directiveMatch.AWSAccessKeyId,
      SecretAccessKey: Slingshot.S3Storage.directiveMatch.AWSSecretAccessKey,
      SessionToken: String
    }));

    payload["x-amz-security-token"] = credentials.SessionToken;

    return Slingshot.S3Storage.applySignature
      .call(this, payload, policy, _.defaults({
        AWSAccessKeyId: credentials.AccessKeyId,
        AWSSecretAccessKey: credentials.SecretAccessKey
      }, directive));
  }
}, Slingshot.S3Storage);

function formatNumber(num, digits) {
  var string = String(num);

  return Array(digits - string.length + 1).join("0").concat(string);
}

var crypto = Npm.require("crypto");

function md5(data, encoding) {
  /* global Buffer: false */
  return crypto
      .createHash("md5")
      .update(new Buffer(data, "utf-8"))
      .digest(encoding);
}

function hmac256(key, data, encoding) {
  /* global Buffer: false */
  return crypto
    .createHmac("sha256", key)
    .update(new Buffer(data, "utf-8"))
    .digest(encoding);
}
<|endoftext|>


<commit_before>var vows = require('vows');
var assert = require('assert');
var suite = vows.describe('jStat.distribution');

require('../env.js');

suite.addBatch({
  'studentt pdf': {
    'topic': function() {
      return jStat;
    },
    //Checked against R dt(x,df)
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      var x = 0.5;
      var df = 40;
      assert.epsilon(tol, jStat.studentt.pdf(x, df), 0.3489195);
    },
    //Checked against R's pt(x, df)
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      var x = 0.5;
      var df = 40;
      assert.epsilon(tol, jStat.studentt.cdf(x, df), 0.6900926);
    },
    //Checked against R's qt(x, df)
    'check inv calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.studentt.inv(0.5, 40), 0);
      assert.epsilon(tol, jStat.studentt.inv(0.7, 20), 0.5328628);
      assert.epsilon(tol, jStat.studentt.inv(0.2, 10), -0.8790578);
    }
  },
});

suite.export(module);
<commit_msg>Tests for the student t distribution<commit_after>var vows = require('vows');
var assert = require('assert');
var suite = vows.describe('jStat.distribution');

require('../env.js');

suite.addBatch({
  'studentt pdf': {
    'topic': function() {
      return jStat;
    },
    //Checked against R dt(x,df)
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.studentt.pdf(0.5, 40), 0.3489195);
      assert.epsilon(tol, jStat.studentt.pdf(-1.0, 20), 0.2360456);
    },
    'check pdf calculation for large parameters': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.studentt.pdf(0.45, 400), 0.3602198);
      assert.epsilon(tol, jStat.studentt.pdf(2.95, 525), 0.005283244);
    },
    //Checked against R's pt(x, df)
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      var x = 0.5;
      var df = 40;
      assert.epsilon(tol, jStat.studentt.cdf(x, df), 0.6900926);
    },
    //Checked against R's qt(x, df)
    'check inv calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.studentt.inv(0.5, 40), 0);
      assert.epsilon(tol, jStat.studentt.inv(0.7, 20), 0.5328628);
      assert.epsilon(tol, jStat.studentt.inv(0.2, 10), -0.8790578);
    }
  },
});

suite.export(module);
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2014 Brian Jackson (iggy@theiggy.com)
 *
 * Anything not explicitly licensed some other way is released under the new BSD license
 * http://www.opensource.org/licenses/bsd-license.php
 */

// "namespaces"
var folders = {};
var msglist = {};
var dw = {}; // miscellaneous variables
dw.viewmsg = {};
dw.dialog = {};

$(document).ready(function() {
    console.log('doc ready');

    // refresh msg list periodically
    // TODO stuff a configurable time into the body or something
    refid = window.setInterval(function(){
        console.log('msglist refresh');
        $('#msglist .foldersel').change();
    }, 5*60*1000);

    dw.msgtable = $('#msglist').srvDatatable();

    // folder tree -- https://github.com/Erffun/JsonTree
    $.getJSON('json/folderlist2/?server=0&parent=', function(data) {
        console.log('fl2', data);
        $('#foldertree2').jsonTree(data, {
            mandatorySelect: true,
            lazyLoad: true,
            lazyRequestUrl: 'json/folderlist2/?server=0',
            lazySendParameterName: 'parent',
            onSelect: function(selid, selli, clickedli) {
                console.log("ft2 onSelect", selid, selli, clickedli);
                console.log(selli.attr('value'));
                $('.foldersel').val(selli.attr('value'));
                $('#msglist .foldersel').change();
            },
        });
    });

    // hide the spinner
    $('#spinner').hide();
});

/*
 * jQuery server side datatable plugin
 *
 * Copyright (c) 2009 Brian Jackson <iggy@theiggy.com>
 *
 * A datatable plugin that does all the searching, sorting, pagination, etc. on
 * the server. This was written mainly for Simone, which uses an IMAP
 * backend. The IMAP server will almost always be better at sorting, searching,
 * etc.
 *
 * Licensed under same license as Simone (i.e. BSD)
 */
;(function($) {
    $.fn.srvDatatable = function() {
        console.log('start srvDatatable', this, $(this));
        var $cnt = $(this);

        // html used for the navigation above the message list
        var navht = ' \
<form class="msgnav" action="msglist/" method="get" onSubmit="return false;"> \
    <input type="text" readonly="readonly" value="INBOX" class="foldersel ui-widget" /> \
    <button \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" \
        onclick="$(\'#msglist .pagesel\').firstPage();"> \
             \
    </button> \
    <button \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" \
        onclick="$(\'#msglist .pagesel\').prevPage(); return false;"> \
             \
    </button> \
    <select class="pagesel ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"> \
        <option>1</option> \
    </select> \
    <button \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" \
        onclick="$(\'#msglist .pagesel\').nextPage(); return false;"> \
             \
    </button> \
    <button id="lastPageButton" \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" \
        onclick="$(\'#msglist .pagesel\').lastPage(); return false;"> \
             \
    </button> \
    <select title="Messages To Show Per Page" \
        class="perpagesel ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"> \
        <option selected="selected">10</option> \
        <option>20</option> \
        <option>40</option> \
        <option>50</option> \
    </select> \
    <select title="Sort Direction" \
        class="sortordersel ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"> \
        <option>Asc</option> \
        <option selected="selected">Desc</option> \
    </select> \
    <input id="msgsearch" type="text" class="ui-widget" /> \
    <button id="searchsubmit" \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"> \
        Search \
    </button> \
</form> \
            ';
// TODO filters (unread, marked, etc)
        var tableht = ' \

 \

 \
        <input type="checkbox" />	 \
        Subject	 \
        From	 \
        Date	 \
        Size	\

 \

';

        $cnt.append(navht);

        $cnt.append(tableht);

        //$('#msglist th').addClass('ui-corner-all');

        var $foldersel = $cnt.find('.foldersel'),
            $pagesel = $cnt.find('.pagesel'),
            $perpagesel = $cnt.find('.perpagesel'),
            $sortordersel = $cnt.find('.sortordersel'),
            $tbl = $cnt.find('table'),
            $msgsearch = $cnt.find('#msgsearch'),
            $searchsubmit = $cnt.find('#searchsubmit');

        $foldersel.change(update);
        $pagesel.change(update);
        $perpagesel.change(update);
        $sortordersel.change(update);
        $searchsubmit.click(update);

        update();

        // setup the checkboxes
        $('#msglist th input[type=checkbox]').click(function(event) {
            // clicked the table header checkbox, so check all the visible boxes
            console.log('th checkbox click', event);
            $('#msglist input[type=checkbox]').val(event.currentTarget.checked);
        });

        function getUrl() {
            var url = $cnt.find('.msgnav').attr('action') + // form action specifies the base of the url
            '0/' + // server
            $foldersel.val() + '/' + // folder
            $pagesel.val() + '/' + // page
            $perpagesel.val() + '/' + // msgs per page
            'date/'+
            $sortordersel.val().charAt(0) + '/' + // sort order
            $msgsearch.val() + '/'; // search terms

            console.log('getURL()', url);
            return url;
        };

        function firstPage(e) {
            console.log("first page", this);
            e.preventDefault();
            $pagesel.val('1');
            update();
        };
        function prevPage(e) {
            console.log("prev page", this);
            e.preventDefault();
            $pagesel.val($pagesel.val()-1);
            update();
        };
        function nextPage(e) {
            console.log("next page", this);
            e.preventDefault();
            $pagesel.val($pagesel.val()+1);
            update();
        };
        function lastPage(e) {
            console.log("last page", this);
            e.preventDefault();
            $pagesel.val('1');
            update();
        };
        function update() {
            $.getJSON(getUrl(), function(data) {
                console.log('get msglist callback');
                j = eval(data);
                console.log('j = ', j);

                // fill pagesel
                $pagesel.empty();
                console.log(j['totalmsgs'], $perpagesel.val(),
                    Math.ceil(j['totalmsgs'] / $perpagesel.val()));
                $('#lastPageButton').click(function(event) {
                    console.log('lastPageButton click', event);
                    $pagesel.val(Math.max(Math.ceil(j['totalmsgs'] / $perpagesel.val()), 1));
                    $pagesel.change();
                    event.preventDefault();
                });
                for(var i = 1 ; i <= Math.max(Math.ceil(j['totalmsgs'] / $perpagesel.val()), 1) ; i++) {
                    var selht = '';
                    if($sortordersel.val().charAt(0) == "A" && i == Math.ceil(j['stop']/$perpagesel.val()))
                        selht = ' selected="selected"';
                    if($sortordersel.val().charAt(0) == "D" && i == Math.floor(j['totalmsgs']/$perpagesel.val() - j['stop']/$perpagesel.val()) + 1)
                        selht = ' selected="selected"';
                    $pagesel.append('<option'+selht+'>' + i + '</option>');
                }

                // fill the msglist table
                $tbl.find('tr:not(:first)').remove();
                console.log(j['msglist'].length);
                for(var uid in j['msglist']) {
                    msg = j['msglist'][uid];
                    console.log('168', uid, msg);
                    var rclass = 'odd ui-state-highlight';
                    if(uid % 2 == 0)
                        rclass = 'even';
                    // handle flags
                    fclass = '';
                    //console.log(msg['flags'].join());
                    if(msg['flags'].join().search("\\Seen") != -1)
                        rclass += ' msgseen';
                    else
                        rclass += ' msgunseen';
                    if(msg['flags'].join().search("\\Flagged") != -1)
                        fclass += ' msgimport';

                    // TODO finish pulling out the <*> and stuffing it into the alt tag maybe
                    r = new RegExp("<.*>");
                    console.log("regex5", r.exec(msg['from']));

                    $tbl.append(' \

 \
    <input type="checkbox" />	 \
    ' + msg['subject'] + '	 \
    ' + msg['from'] + '	 \
    ' + msg['date'] + '	 \
    ' + Math.round((msg['size']/1024)*10)/10 + 'K	 \

 \
                    ');
                }

                // msg listener
                $('tr.msg').click(function(e) {
                    console.log(this, e, this.id.replace('msg-', ''), $(window).width(), $(window).height());

                    if(e.which==2) {
                        // open message in new tab if message was middle clicked
                        server = '0';
                        folder = $('#msglist .foldersel').val();
                        uid = this.id.replace('msg-', '');
                        window.open('viewmsg/' + server + '/' + folder + '/' + uid + '/', '_blank');

                        return true;
                    }

                    // show a modal dialog with an email msg
                    server = '0';
                    folder = $('#msglist .foldersel').val();
                    uid = this.id.replace('msg-', '');
                    dlgw = $(window).width() - 30;
                    dlgh = $(window).height() - 50;
                    console.log(dlgw, dlgh);
                    options = {
                        title: 'View Message',
                        closeable: true,
                        modal: true,
                        position: 'center',
                        width: dlgw,
                        height: dlgh
                    };

                    dw.dialog.viewmsg = $('

').append('body');
                    $(dw.dialog.viewmsg).load('viewmsg/' + server + '/' + folder + '/' + uid + '/').dialog(options);

                    $(this).removeClass('msgunseen');
                    $(this).addClass('msgseen');
                });
                $('tr.msg').hover(
                    function() { $(this).addClass('msghover'); },
                    function() { $(this).removeClass('msghover'); }
                );
            });
        };
        return this;
    };
}(jQuery));

dw.viewmsg.markmsg = function(e, how, server, folder, uid) {
    console.log(e, how, server, folder, uid, this);
    e.preventDefault();
    $.getJSON('action/mark'+how+'/?server='+server+'&folder='+folder+'&uid='+uid, function(j) {
        console.log('markmsg json callback', this, j);
        $('#msglist .foldersel').change();
    });
};

dw.dialog.compose = function() {
    $('body').append('

');
    $('#compose').load('newmail/', function(j) {
        console.log('compose callback', this, j);

        $('#compose').dialog({'width':'auto'});
    });
};

dw.dialog.prefs = function() {
    $('body').append('

');
    $('#prefs').load('prefs/', function(j) {
        console.log('prefs dialog callback', this, j);

        $('#prefs').dialog({'width':'auto'});

        $('#tabs').tabs();
    });
};

dw.addSMTP = function(form) {

    console.log('addSMTP', form);
};

dw.sendMsg = function(form) {
    console.log('sendMsg', form);
};

dw.visfolders = [];
dw.updateFolderCounts = function(d) {
    console.log('updateFolderCounts', d);

    dw.visfolders = [];
    $('#foldertree2  > ul > li > span').each(function(d) {
        // get a list of folders
        // TODO subfolders aren't getting their parent
        console.log(d, this, $(this).text());
        dw.visfolders.push($(this).text());
        console.log(dw.visfolders);
        // send list to server, it sends back unread counts per folder

    });
};

<commit_msg>open message by clicking subject instead of whole row<commit_after>/*
 * Copyright 2007-2014 Brian Jackson (iggy@theiggy.com)
 *
 * Anything not explicitly licensed some other way is released under the new BSD license
 * http://www.opensource.org/licenses/bsd-license.php
 */

// "namespaces"
var folders = {};
var msglist = {};
var dw = {}; // miscellaneous variables
dw.viewmsg = {};
dw.dialog = {};

$(document).ready(function() {
    console.log('doc ready');

    // refresh msg list periodically
    // TODO stuff a configurable time into the body or something
    refid = window.setInterval(function(){
        console.log('msglist refresh');
        $('#msglist .foldersel').change();
    }, 5*60*1000);

    dw.msgtable = $('#msglist').srvDatatable();

    // folder tree -- https://github.com/Erffun/JsonTree
    $.getJSON('json/folderlist2/?server=0&parent=', function(data) {
        console.log('fl2', data);
        $('#foldertree2').jsonTree(data, {
            mandatorySelect: true,
            lazyLoad: true,
            lazyRequestUrl: 'json/folderlist2/?server=0',
            lazySendParameterName: 'parent',
            onSelect: function(selid, selli, clickedli) {
                console.log("ft2 onSelect", selid, selli, clickedli);
                console.log(selli.attr('value'));
                $('.foldersel').val(selli.attr('value'));
                $('#msglist .foldersel').change();
            },
        });
    });

    // hide the spinner
    $('#spinner').hide();
});

/*
 * jQuery server side datatable plugin
 *
 * Copyright (c) 2009 Brian Jackson <iggy@theiggy.com>
 *
 * A datatable plugin that does all the searching, sorting, pagination, etc. on
 * the server. This was written mainly for Simone, which uses an IMAP
 * backend. The IMAP server will almost always be better at sorting, searching,
 * etc.
 *
 * Licensed under same license as Simone (i.e. BSD)
 */
;(function($) {
    $.fn.srvDatatable = function() {
        console.log('start srvDatatable', this, $(this));
        var $cnt = $(this);

        // html used for the navigation above the message list
        var navht = ' \
<form class="msgnav" action="msglist/" method="get" onSubmit="return false;"> \
    <input type="text" readonly="readonly" value="INBOX" class="foldersel ui-widget" /> \
    <button \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" \
        onclick="$(\'#msglist .pagesel\').firstPage();"> \
             \
    </button> \
    <button \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" \
        onclick="$(\'#msglist .pagesel\').prevPage(); return false;"> \
             \
    </button> \
    <select class="pagesel ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"> \
        <option>1</option> \
    </select> \
    <button \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" \
        onclick="$(\'#msglist .pagesel\').nextPage(); return false;"> \
             \
    </button> \
    <button id="lastPageButton" \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" \
        onclick="$(\'#msglist .pagesel\').lastPage(); return false;"> \
             \
    </button> \
    <select title="Messages To Show Per Page" \
        class="perpagesel ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"> \
        <option selected="selected">10</option> \
        <option>20</option> \
        <option>40</option> \
        <option>50</option> \
    </select> \
    <select title="Sort Direction" \
        class="sortordersel ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"> \
        <option>Asc</option> \
        <option selected="selected">Desc</option> \
    </select> \
    <input id="msgsearch" type="text" class="ui-widget" /> \
    <button id="searchsubmit" \
        class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"> \
        Search \
    </button> \
</form> \
            ';
// TODO filters (unread, marked, etc)
        var tableht = ' \

 \

 \
        <input type="checkbox" />	 \
        Subject	 \
        From	 \
        Date	 \
        Size	\

 \

';

        $cnt.append(navht);

        $cnt.append(tableht);

        //$('#msglist th').addClass('ui-corner-all');

        var $foldersel = $cnt.find('.foldersel'),
            $pagesel = $cnt.find('.pagesel'),
            $perpagesel = $cnt.find('.perpagesel'),
            $sortordersel = $cnt.find('.sortordersel'),
            $tbl = $cnt.find('table'),
            $msgsearch = $cnt.find('#msgsearch'),
            $searchsubmit = $cnt.find('#searchsubmit');

        $foldersel.change(update);
        $pagesel.change(update);
        $perpagesel.change(update);
        $sortordersel.change(update);
        $searchsubmit.click(update);

        update();

        // setup the checkboxes
        $('#msglist th input[type=checkbox]').click(function(event) {
            // clicked the table header checkbox, so check all the visible boxes
            console.log('th checkbox click', event);
            $('#msglist input[type=checkbox]').val(event.currentTarget.checked);
        });

        function getUrl() {
            var url = $cnt.find('.msgnav').attr('action') + // form action specifies the base of the url
            '0/' + // server
            $foldersel.val() + '/' + // folder
            $pagesel.val() + '/' + // page
            $perpagesel.val() + '/' + // msgs per page
            'date/'+
            $sortordersel.val().charAt(0) + '/' + // sort order
            $msgsearch.val() + '/'; // search terms

            console.log('getURL()', url);
            return url;
        };

        function firstPage(e) {
            console.log("first page", this);
            e.preventDefault();
            $pagesel.val('1');
            update();
        };
        function prevPage(e) {
            console.log("prev page", this);
            e.preventDefault();
            $pagesel.val($pagesel.val()-1);
            update();
        };
        function nextPage(e) {
            console.log("next page", this);
            e.preventDefault();
            $pagesel.val($pagesel.val()+1);
            update();
        };
        function lastPage(e) {
            console.log("last page", this);
            e.preventDefault();
            $pagesel.val('1');
            update();
        };
        function update() {
            $.getJSON(getUrl(), function(data) {
                console.log('get msglist callback');
                j = eval(data);
                console.log('j = ', j);

                // fill pagesel
                $pagesel.empty();
                console.log(j['totalmsgs'], $perpagesel.val(),
                    Math.ceil(j['totalmsgs'] / $perpagesel.val()));
                $('#lastPageButton').click(function(event) {
                    console.log('lastPageButton click', event);
                    $pagesel.val(Math.max(Math.ceil(j['totalmsgs'] / $perpagesel.val()), 1));
                    $pagesel.change();
                    event.preventDefault();
                });
                for(var i = 1 ; i <= Math.max(Math.ceil(j['totalmsgs'] / $perpagesel.val()), 1) ; i++) {
                    var selht = '';
                    if($sortordersel.val().charAt(0) == "A" && i == Math.ceil(j['stop']/$perpagesel.val()))
                        selht = ' selected="selected"';
                    if($sortordersel.val().charAt(0) == "D" && i == Math.floor(j['totalmsgs']/$perpagesel.val() - j['stop']/$perpagesel.val()) + 1)
                        selht = ' selected="selected"';
                    $pagesel.append('<option'+selht+'>' + i + '</option>');
                }

                // fill the msglist table
                $tbl.find('tr:not(:first)').remove();
                console.log(j['msglist'].length);
                for(var uid in j['msglist']) {
                    msg = j['msglist'][uid];
                    console.log('168', uid, msg);
                    var rclass = 'odd ui-state-highlight';
                    if(uid % 2 == 0)
                        rclass = 'even';
                    // handle flags
                    fclass = '';
                    //console.log(msg['flags'].join());
                    if(msg['flags'].join().search("\\Seen") != -1)
                        rclass += ' msgseen';
                    else
                        rclass += ' msgunseen';
                    if(msg['flags'].join().search("\\Flagged") != -1)
                        fclass += ' msgimport';

                    // TODO finish pulling out the <*> and stuffing it into the alt tag maybe
                    r = new RegExp("<.*>");
                    console.log("regex5", r.exec(msg['from']));

                    $tbl.append(' \

 \
    <input type="checkbox" />	 \
    ' + msg['subject'] + '	 \
    ' + msg['from'] + '	 \
    ' + msg['date'] + '	 \
    ' + Math.round((msg['size']/1024)*10)/10 + 'K	 \

 \
                    ');
                }

                // msg listener
                $('tr.msg td.subject').click(function(e) {
                    console.log(this, e, this.id.replace('msg-', ''), $(window).width(), $(window).height());

                    if(e.which==2) {
                        // open message in new tab if message was middle clicked
                        server = '0';
                        folder = $('#msglist .foldersel').val();
                        uid = this.id.replace('msg-', '');
                        window.open('viewmsg/' + server + '/' + folder + '/' + uid + '/', '_blank');

                        return true;
                    }

                    // show a modal dialog with an email msg
                    server = '0';
                    folder = $('#msglist .foldersel').val();
                    uid = this.id.replace('msg-', '');
                    dlgw = $(window).width() - 30;
                    dlgh = $(window).height() - 50;
                    console.log(dlgw, dlgh);
                    options = {
                        title: 'View Message',
                        closeable: true,
                        modal: true,
                        position: 'center',
                        width: dlgw,
                        height: dlgh
                    };

                    dw.dialog.viewmsg = $('

').append('body');
                    $(dw.dialog.viewmsg).load('viewmsg/' + server + '/' + folder + '/' + uid + '/').dialog(options);

                    $(this).removeClass('msgunseen');
                    $(this).addClass('msgseen');
                });
                $('tr.msg').hover(
                    function() { $(this).addClass('msghover'); },
                    function() { $(this).removeClass('msghover'); }
                );
            });
        };
        return this;
    };
}(jQuery));

dw.viewmsg.markmsg = function(e, how, server, folder, uid) {
    console.log(e, how, server, folder, uid, this);
    e.preventDefault();
    $.getJSON('action/mark'+how+'/?server='+server+'&folder='+folder+'&uid='+uid, function(j) {
        console.log('markmsg json callback', this, j);
        $('#msglist .foldersel').change();
    });
};

dw.dialog.compose = function() {
    $('body').append('

');
    $('#compose').load('newmail/', function(j) {
        console.log('compose callback', this, j);

        $('#compose').dialog({'width':'auto'});
    });
};

dw.dialog.prefs = function() {
    $('body').append('

');
    $('#prefs').load('prefs/', function(j) {
        console.log('prefs dialog callback', this, j);

        $('#prefs').dialog({'width':'auto'});

        $('#tabs').tabs();
    });
};

dw.addSMTP = function(form) {

    console.log('addSMTP', form);
};

dw.sendMsg = function(form) {
    console.log('sendMsg', form);
};

dw.visfolders = [];
dw.updateFolderCounts = function(d) {
    console.log('updateFolderCounts', d);

    dw.visfolders = [];
    $('#foldertree2  > ul > li > span').each(function(d) {
        // get a list of folders
        // TODO subfolders aren't getting their parent
        console.log(d, this, $(this).text());
        dw.visfolders.push($(this).text());
        console.log(dw.visfolders);
        // send list to server, it sends back unread counts per folder

    });
};

<|endoftext|>


<commit_before>var fs = require('fs');
var isCiMasterBuild = process.env.TRAVIS_BRANCH === 'master' && process.env.TRAVIS_PULL_REQUEST === 'false';
var isLocalBuild = process.env.TRAVIS_BRANCH === undefined;

console.log('PR was: ' + process.env.TRAVIS_PULL_REQUEST);
console.log('Branch was: ' + process.env.TRAVIS_BRANCH);

if (isCiMasterBuild) {
  console.log('**** Master Branch Build ****');
  bump();
  createZip();
} else if (isLocalBuild) {
  createZip();
} else {
  console.log('**** Skip Publish ****');
}

function bump() {
  console.log('**** Bump Version ****');
  if (process.argv[2]) {
    var manifestPath = './manifest.json';
    var manifest = require(manifestPath);
    var version = manifest.version.split('.');
    // Update last digit to $TRAVIS_BUILD_NUMBER
    manifest.version = version[0] + '.' + version[1] + '.' + process.argv[2];
    console.log('Bumping version to ' + manifest.version);
    fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 4));
  }
}

function createZip() {
  console.log('**** Create ZIP ****');
  var archiver = require('archiver');
  var checkDir = fs.existsSync('build') || fs.mkdirSync('build');
  var output = fs.createWriteStream('build/JustDoIt.zip');
  var archive = archiver('zip');
  output.on('close', () => {
    console.log('ZIP Created - build/JustDoIt.zip');
    console.log('Wrote ' + archive.pointer() + ' bytes.');
  });
  archive.on('error', (err) => {
    throw err;
  });
  archive.pipe(output);
  archive.glob('!(build|node_modules)*');
  archive.glob('!(build|node_modules)/**/*');
  archive.finalize();
}
<commit_msg>Change end event<commit_after>var fs = require('fs');
var isCiMasterBuild = process.env.TRAVIS_BRANCH === 'master' && process.env.TRAVIS_PULL_REQUEST === 'false';
var isLocalBuild = process.env.TRAVIS_BRANCH === undefined;

console.log('PR was: ' + process.env.TRAVIS_PULL_REQUEST);
console.log('Branch was: ' + process.env.TRAVIS_BRANCH);

if (isCiMasterBuild) {
  console.log('**** Master Branch Build ****');
  bump();
  createZip();
} else if (isLocalBuild) {
  createZip();
} else {
  console.log('**** Skip Publish ****');
}

function bump() {
  console.log('**** Bump Version ****');
  if (process.argv[2]) {
    var manifestPath = './manifest.json';
    var manifest = require(manifestPath);
    var version = manifest.version.split('.');
    // Update last digit to $TRAVIS_BUILD_NUMBER
    manifest.version = version[0] + '.' + version[1] + '.' + process.argv[2];
    console.log('Bumping version to ' + manifest.version);
    fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 4));
  }
}

function createZip() {
  console.log('**** Create ZIP ****');
  var archiver = require('archiver');
  var checkDir = fs.existsSync('build') || fs.mkdirSync('build');
  var output = fs.createWriteStream('build/JustDoIt.zip');
  var archive = archiver('zip');
  output.on('end', () => {
    console.log('ZIP Created - build/JustDoIt.zip');
    console.log('Wrote ' + archive.pointer() + ' bytes.');
  });
  archive.on('error', (err) => {
    throw err;
  });
  archive.pipe(output);
  archive.glob('!(build|node_modules)*');
  archive.glob('!(build|node_modules)/**/*');
  archive.finalize();
}
<|endoftext|>


<commit_before>'use strict';
var EC = protractor.ExpectedConditions;

var common = require("../common.js");

var ImagesGridPage = function() {
    this.selectedClass = 'selected';
    this.getAllCells = function() {
        return element.all(by.css('.cell'));
    };
    this.getCell = function (index) {
        return this.getAllCells().get(index);
    };
    this.clickCell = function (index) {
        return this.getCell(index).click();
    };
    this.doubleClickCell = function (index) {
        return browser.actions().doubleClick(this.getCell(index)).perform();
    };
    this.getCellImageName = function(index) {
        return this.getCell(index).element(by.css('.tag.tag-default')).getText();
    };
    this.clickDeselectButton = function () {
        return element(by.id('deselect-images')).click();
    };
    this.clickDeleteButton = function () {
        return element(by.id('delete-images')).click();
    };
    this.clickCreateRelationsButton = function () {
        return element(by.id('create-relations-btn')).click();
    };
    this.clickConfirmDeleteButton = function () {
        return element(by.id('delete-images-confirm')).click();
    };
    this.clickCancelDeleteButton = function () {
        return element(by.id('delete-images-cancel')).click();
    };
    this.getDeleteConfirmationModal = function() {
        return element(by.css('.modal-dialog'));
    };
    this.getLinkModal = function() {
        return element(by.id('link-modal'));
    };
    this.typeInIdentifierInLinkModal = function(identifier) {
        return common.typeIn(this.getLinkModal().element(by.id('object-search')), identifier);
    };
    this.getSuggestedResourcesInLinkModalByIdentifier = function (identifier) {
        return this.getLinkModal().element(by.id('resource-'+identifier))
    };
    this.uploadImage = function (filePath) {
        return element(by.id('file')).sendKeys(filePath);
    };
    this.clickUploadArea = function() {
        return element(by.css('.droparea')).click();
    };
    this.get = function() {
        return browser.get('/#/images').
            then(function(){
                browser.wait(EC.presenceOf(element(by.css('.cell'))), 10000, 'Waiting for image cells.')
            });
    };
    this.chooseImageSubtype = function (index) {
        return element(by.id('choose-image-subtype-option-' + index)).click();
    };
};

module.exports = new ImagesGridPage();<commit_msg>Prevent dialog to open in tests.<commit_after>'use strict';
var EC = protractor.ExpectedConditions;

var common = require("../common.js");

var ImagesGridPage = function() {
    this.selectedClass = 'selected';
    this.getAllCells = function() {
        return element.all(by.css('.cell'));
    };
    this.getCell = function (index) {
        return this.getAllCells().get(index);
    };
    this.clickCell = function (index) {
        return this.getCell(index).click();
    };
    this.doubleClickCell = function (index) {
        return browser.actions().doubleClick(this.getCell(index)).perform();
    };
    this.getCellImageName = function(index) {
        return this.getCell(index).element(by.css('.tag.tag-default')).getText();
    };
    this.clickDeselectButton = function () {
        return element(by.id('deselect-images')).click();
    };
    this.clickDeleteButton = function () {
        return element(by.id('delete-images')).click();
    };
    this.clickCreateRelationsButton = function () {
        return element(by.id('create-relations-btn')).click();
    };
    this.clickConfirmDeleteButton = function () {
        return element(by.id('delete-images-confirm')).click();
    };
    this.clickCancelDeleteButton = function () {
        return element(by.id('delete-images-cancel')).click();
    };
    this.getDeleteConfirmationModal = function() {
        return element(by.css('.modal-dialog'));
    };
    this.getLinkModal = function() {
        return element(by.id('link-modal'));
    };
    this.typeInIdentifierInLinkModal = function(identifier) {
        return common.typeIn(this.getLinkModal().element(by.id('object-search')), identifier);
    };
    this.getSuggestedResourcesInLinkModalByIdentifier = function (identifier) {
        return this.getLinkModal().element(by.id('resource-'+identifier))
    };
    this.uploadImage = function (filePath) {
        return element(by.id('file')).sendKeys(filePath);
    };
    this.clickUploadArea = function() {
        return browser.actions()
            .mouseMove(element(by.css(".droparea")), {x: 10, y: 10})
            .click()
            .perform();
    };
    this.get = function() {
        return browser.get('/#/images').
            then(function(){
                browser.wait(EC.presenceOf(element(by.css('.cell'))), 10000, 'Waiting for image cells.')
            });
    };
    this.chooseImageSubtype = function (index) {
        return element(by.id('choose-image-subtype-option-' + index)).click();
    };
};

module.exports = new ImagesGridPage();<|endoftext|>


<commit_before>var path = require('path'),
    assert = require('assert'),
    events = require('events'),
    vows = require('vows'),
    resourceful = require('../lib/resourceful');

vows.describe('resourceful/hooks').addBatch({
  "save-able Resource": {
    topic: function () {
      return resourceful.define('resource', function () {
        this.property('title');
      });
    },
    "with 'before' hooks on `save`": {
      topic: function (A) {
        var that = this;
        this.hooked_save = 0;
        A.before('save', function (obj, next) {
          that.hooked_save += 1; next(null);
        });
        A.before('save', function (obj, next) {
          that.hooked_save *= 2; next(null);
        });
        return A;
      },
      "when calling save() on an instance of Article": {
        topic: function (A) {
          new(A)({ _id: '128', counter: 0, title: 'foobar' }).save(this.callback);
        },
        "should trigger both hooks in the right order": function (e, res) {
          assert.isNull(e);
          assert.equal(this.hooked_save, 2);
        }
      }
    }
  },
  "another save-able Resource": {
    topic: function () {
      return resourceful.define('resource2', function () {
        this.property('title');
      });
    },
    "with 'after' hooks on `save`": {
      topic: function (A) {
        var that = this;
        this.hooked = 0;
        A.after('save', function (e, obj, next) {
          that.hooked += 1; next(null);
          // TODO: test other stuff
        });
        A.after('save', function (e, obj, next) {
          that.hooked *= 2; next(null);
        });
        return A;
      },
      "should just have the two 'after' hooks registered": function (A) {
        assert.equal(A.hooks.after.save.length,  2);
        assert.equal(A.hooks.before.save.length, 0);
      },
      "when calling save() on an instance of Article": {
        topic: function (A) {
          new(A)({ _id: '65', counter: 0, title: 'hookbar' }).save(this.callback);
        },
        "should trigger both hooks in the right order": function (e, res) {
          assert.isNull(e);
          assert.equal(this.hooked, 2);
        }
      }
    }
  },
  "create-able Resource": {
    topic: function () {
      return resourceful.define('resourceCreate', function () {
        this.property('title');
      });
    },
    "with 'before' hooks on `create`": {
      topic: function (A) {
        var that = this;
        this.hooked_create = 0;
        A.before('create', function (obj, next) {
          that.hooked_create += 1; next(null);
        });
        A.before('create', function (obj, next) {
          that.hooked_create *= 2; next(null);
        });
        return A;
      },
      "when calling create() on an instance of Article": {
        topic: function (A) {
          A.create({ _id: '66', counter: 0, title: 'foobar' }, this.callback);
        },
        "should trigger both hooks in the right order": function (e, res) {
          assert.isNull(e);
          assert.equal(this.hooked_create, 2);
        }
      }
    },
  },
  "another create-able Resource": {
    topic: function () {
      return resourceful.define('resourceAfterCreate', function () {
        this.property('title');
      });
    },
    "with 'after' hooks on `create`": {
      topic: function (A) {
        var that = this;
        this.hooked = 0;
        A.after('save', function (e, obj, next) {
          that.hooked += 1; next(null);
          // TODO: test other stuff
        });
        A.after('save', function (e, obj, next) {
          that.hooked *= 2; next(null);
        });
        this.hooked_create = 0;
        A.after('create', function (e, obj, next) {
          // after.create preceeds after.save
          assert.equal(that.hooked, 0);
          that.hooked_create += 1; next(null);
          // TODO: test other stuff
        });
        A.after('create', function (e, obj, next) {
          that.hooked_create *= 2; next(null);
        });
        return A;
      },
      "should just have the two 'after' hooks registered": function (A) {
        assert.equal(A.hooks.after.create.length,  2);
        assert.equal(A.hooks.before.create.length, 0);
      },
      "when calling create() on an instance of Article": {
        topic: function (A) {
          A.create({ _id: '67', counter: 0, title: 'hookbar' }, this.callback);
        },
        "should trigger both hooks in the right order": function (e, res) {
          assert.isNull(e);
          assert.equal(this.hooked, 2);
          assert.equal(this.hooked_create, 2);
        }
      }
    }
  }
}).export(module);
<commit_msg>test id was colliding.<commit_after>var path = require('path'),
    assert = require('assert'),
    events = require('events'),
    vows = require('vows'),
    resourceful = require('../lib/resourceful');

vows.describe('resourceful/hooks').addBatch({
  "save-able Resource": {
    topic: function () {
      return resourceful.define('resource', function () {
        this.property('title');
      });
    },
    "with 'before' hooks on `save`": {
      topic: function (A) {
        var that = this;
        this.hooked_save = 0;
        A.before('save', function (obj, next) {
          that.hooked_save += 1; next(null);
        });
        A.before('save', function (obj, next) {
          that.hooked_save *= 2; next(null);
        });
        return A;
      },
      "when calling save() on an instance of Article": {
        topic: function (A) {
          new(A)({ _id: '128', counter: 0, title: 'foobar' }).save(this.callback);
        },
        "should trigger both hooks in the right order": function (e, res) {
          assert.isNull(e);
          assert.equal(this.hooked_save, 2);
        }
      }
    }
  },
  "another save-able Resource": {
    topic: function () {
      return resourceful.define('resource2', function () {
        this.property('title');
      });
    },
    "with 'after' hooks on `save`": {
      topic: function (A) {
        var that = this;
        this.hooked = 0;
        A.after('save', function (e, obj, next) {
          that.hooked += 1; next(null);
          // TODO: test other stuff
        });
        A.after('save', function (e, obj, next) {
          that.hooked *= 2; next(null);
        });
        return A;
      },
      "should just have the two 'after' hooks registered": function (A) {
        assert.equal(A.hooks.after.save.length,  2);
        assert.equal(A.hooks.before.save.length, 0);
      },
      "when calling save() on an instance of Article": {
        topic: function (A) {
          new(A)({ _id: '65', counter: 0, title: 'hookbar' }).save(this.callback);
        },
        "should trigger both hooks in the right order": function (e, res) {
          assert.isNull(e);
          assert.equal(this.hooked, 2);
        }
      }
    }
  },
  "create-able Resource": {
    topic: function () {
      return resourceful.define('resourceCreate', function () {
        this.property('title');
      });
    },
    "with 'before' hooks on `create`": {
      topic: function (A) {
        var that = this;
        this.hooked_create = 0;
        A.before('create', function (obj, next) {
          that.hooked_create += 1; next(null);
        });
        A.before('create', function (obj, next) {
          that.hooked_create *= 2; next(null);
        });
        return A;
      },
      "when calling create() on an instance of Article": {
        topic: function (A) {
          A.create({ _id: '69', counter: 0, title: 'foobar' }, this.callback);
        },
        "should trigger both hooks in the right order": function (e, res) {
          assert.isNull(e);
          assert.equal(this.hooked_create, 2);
        }
      }
    },
  },
  "another create-able Resource": {
    topic: function () {
      return resourceful.define('resourceAfterCreate', function () {
        this.property('title');
      });
    },
    "with 'after' hooks on `create`": {
      topic: function (A) {
        var that = this;
        this.hooked = 0;
        A.after('save', function (e, obj, next) {
          that.hooked += 1; next(null);
          // TODO: test other stuff
        });
        A.after('save', function (e, obj, next) {
          that.hooked *= 2; next(null);
        });
        this.hooked_create = 0;
        A.after('create', function (e, obj, next) {
          // after.create preceeds after.save
          assert.equal(that.hooked, 0);
          that.hooked_create += 1; next(null);
          // TODO: test other stuff
        });
        A.after('create', function (e, obj, next) {
          that.hooked_create *= 2; next(null);
        });
        return A;
      },
      "should just have the two 'after' hooks registered": function (A) {
        assert.equal(A.hooks.after.create.length,  2);
        assert.equal(A.hooks.before.create.length, 0);
      },
      "when calling create() on an instance of Article": {
        topic: function (A) {
          A.create({ _id: '67', counter: 0, title: 'hookbar' }, this.callback);
        },
        "should trigger both hooks in the right order": function (e, res) {
          assert.isNull(e);
          assert.equal(this.hooked, 2);
          assert.equal(this.hooked_create, 2);
        }
      }
    }
  }
}).export(module);
<|endoftext|>


<commit_before>import Ember from 'ember';
import AuthConfiguration from 'ember-simple-auth/configuration';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
import ShortcutsRoute from 'ghost-admin/mixins/shortcuts-route';
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
import windowProxy from 'ghost-admin/utils/window-proxy';

const {
    Route,
    inject: {service},
    run
} = Ember;

function K() {
    return this;
}

let shortcuts = {};

shortcuts.esc = {action: 'closeMenus', scope: 'all'};
shortcuts[`${ctrlOrCmd}+s`] = {action: 'save', scope: 'all'};

export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
    shortcuts,

    config: service(),
    feature: service(),
    dropdown: service(),
    notifications: service(),

    afterModel(model, transition) {
        this._super(...arguments);

        if (this.get('session.isAuthenticated')) {
            this.set('appLoadTransition', transition);
            transition.send('loadServerNotifications');

            // return the feature loading promise so that we block until settings
            // are loaded in order for synchronous access everywhere
            return this.get('feature').fetch();
        }
    },

    title(tokens) {
        return `${tokens.join(' - ')} - ${this.get('config.blogTitle')}`;
    },

    sessionAuthenticated() {
        if (this.get('session.skipAuthSuccessHandler')) {
            return;
        }

        // standard ESA post-sign-in redirect
        this._super(...arguments);

        // trigger post-sign-in background behaviour
        this.get('session.user').then((user) => {
            this.send('signedIn', user);
        });
    },

    sessionInvalidated() {
        let transition = this.get('appLoadTransition');

        if (transition) {
            transition.send('authorizationFailed');
        } else {
            run.scheduleOnce('routerTransitions', this, function () {
                this.send('authorizationFailed');
            });
        }
    },

    actions: {
        openMobileMenu() {
            this.controller.set('showMobileMenu', true);
        },

        openSettingsMenu() {
            this.controller.set('showSettingsMenu', true);
        },

        closeMenus() {
            this.get('dropdown').closeDropdowns();
            this.controller.setProperties({
                showSettingsMenu: false,
                showMobileMenu: false
            });
        },

        didTransition() {
            this.set('appLoadTransition', null);
            this.send('closeMenus');
        },

        signedIn() {
            this.get('notifications').clearAll();
            this.send('loadServerNotifications', true);
        },

        invalidateSession() {
            this.get('session').invalidate().catch((error) => {
                this.get('notifications').showAlert(error.message, {type: 'error', key: 'session.invalidate.failed'});
            });
        },

        authorizationFailed() {
            windowProxy.replaceLocation(AuthConfiguration.baseURL);
        },

        loadServerNotifications(isDelayed) {
            if (this.get('session.isAuthenticated')) {
                this.get('session.user').then((user) => {
                    if (!user.get('isAuthor') && !user.get('isEditor')) {
                        this.store.findAll('notification', {reload: true}).then((serverNotifications) => {
                            serverNotifications.forEach((notification) => {
                                this.get('notifications').handleNotification(notification, isDelayed);
                            });
                        });
                    }
                });
            }
        },

        toggleMarkdownHelpModal() {
            this.get('controller').toggleProperty('showMarkdownHelpModal');
        },

        // noop default for unhandled save (used from shortcuts)
        save: K
    }
});
<commit_msg>Check for Ghost Desktop manual update<commit_after>import Ember from 'ember';
import AuthConfiguration from 'ember-simple-auth/configuration';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
import ShortcutsRoute from 'ghost-admin/mixins/shortcuts-route';
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
import windowProxy from 'ghost-admin/utils/window-proxy';

const {
    Route,
    inject: {service},
    run
} = Ember;

function K() {
    return this;
}

let shortcuts = {};

shortcuts.esc = {action: 'closeMenus', scope: 'all'};
shortcuts[`${ctrlOrCmd}+s`] = {action: 'save', scope: 'all'};

export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
    shortcuts,

    config: service(),
    feature: service(),
    dropdown: service(),
    notifications: service(),

    afterModel(model, transition) {
        this._super(...arguments);

        if (this.get('session.isAuthenticated')) {
            this.set('appLoadTransition', transition);
            transition.send('loadServerNotifications');

            // return the feature loading promise so that we block until settings
            // are loaded in order for synchronous access everywhere
            return this.get('feature').fetch();
        }
    },

    title(tokens) {
        return `${tokens.join(' - ')} - ${this.get('config.blogTitle')}`;
    },

    sessionAuthenticated() {
        if (this.get('session.skipAuthSuccessHandler')) {
            return;
        }

        // standard ESA post-sign-in redirect
        this._super(...arguments);

        // trigger post-sign-in background behaviour
        this.get('session.user').then((user) => {
            this.send('signedIn', user);
        });
    },

    sessionInvalidated() {
        let transition = this.get('appLoadTransition');

        if (transition) {
            transition.send('authorizationFailed');
        } else {
            run.scheduleOnce('routerTransitions', this, function () {
                this.send('authorizationFailed');
            });
        }
    },

    actions: {
        openMobileMenu() {
            this.controller.set('showMobileMenu', true);
        },

        openSettingsMenu() {
            this.controller.set('showSettingsMenu', true);
        },

        closeMenus() {
            this.get('dropdown').closeDropdowns();
            this.controller.setProperties({
                showSettingsMenu: false,
                showMobileMenu: false
            });
        },

        didTransition() {
            this.set('appLoadTransition', null);
            this.send('closeMenus');
        },

        signedIn() {
            this.get('notifications').clearAll();
            this.send('loadServerNotifications', true);
            this.send('checkForOutdatedDesktopApp');
        },

        invalidateSession() {
            this.get('session').invalidate().catch((error) => {
                this.get('notifications').showAlert(error.message, {type: 'error', key: 'session.invalidate.failed'});
            });
        },

        authorizationFailed() {
            windowProxy.replaceLocation(AuthConfiguration.baseURL);
        },

        loadServerNotifications(isDelayed) {
            if (this.get('session.isAuthenticated')) {
                this.get('session.user').then((user) => {
                    if (!user.get('isAuthor') && !user.get('isEditor')) {
                        this.store.findAll('notification', {reload: true}).then((serverNotifications) => {
                            serverNotifications.forEach((notification) => {
                                this.get('notifications').handleNotification(notification, isDelayed);
                            });
                        });
                    }
                });
            }
        },

        checkForOutdatedDesktopApp() {
            // Check if the user is running an older version of Ghost Desktop
            // that needs to be manually updated
            // (yes, the desktop team is deeply ashamed of these lines 😢)
            let ua = navigator && navigator.userAgent ? navigator.userAgent : null;

            if (ua && ua.includes && ua.includes('ghost-desktop')) {
                let updateCheck = /ghost-desktop\/0\.((5\.0)|((4|2)\.0)|((3\.)(0|1)))/;
                let link = 'click here';
                let msg = `Your version of Ghost Desktop needs to be manually updated. Please ${link} to get started.`;

                if (updateCheck.test(ua)) {
                    this.get('notifications').showAlert(msg.htmlSafe(), {
                        type: 'upgrade',
                        key: 'desktop.manual.upgrade'
                    });
                }
            }
        },

        toggleMarkdownHelpModal() {
            this.get('controller').toggleProperty('showMarkdownHelpModal');
        },

        // noop default for unhandled save (used from shortcuts)
        save: K
    }
});
<|endoftext|>


<commit_before>/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import * as zrUtil from 'zrender/src/core/util';
import * as graphic from '../../util/graphic';
import Model from '../../model/Model';
import AxisView from './AxisView';
import AxisBuilder from './AxisBuilder';

var elementList = ['axisLine', 'axisLabel', 'axisTick', 'minorTick', 'splitLine', 'minorSplitLine', 'splitArea'];

function getAxisLineShape(polar, rExtent, angle) {
    rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse());
    var start = polar.coordToPoint([rExtent[0], angle]);
    var end = polar.coordToPoint([rExtent[1], angle]);

    return {
        x1: start[0],
        y1: start[1],
        x2: end[0],
        y2: end[1]
    };
}

function getRadiusIdx(polar) {
    var radiusAxis = polar.getRadiusAxis();
    return radiusAxis.inverse ? 0 : 1;
}

// Remove the last tick which will overlap the first tick
function fixAngleOverlap(list) {
    var firstItem = list[0];
    var lastItem = list[list.length - 1];
    if (firstItem
        && lastItem
        && Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4
    ) {
        list.pop();
    }
}

export default AxisView.extend({

    type: 'angleAxis',

    axisPointerClass: 'PolarAxisPointer',

    render: function (angleAxisModel, ecModel) {
        this.group.removeAll();
        if (!angleAxisModel.get('show')) {
            return;
        }

        var angleAxis = angleAxisModel.axis;
        var polar = angleAxis.polar;
        var radiusExtent = polar.getRadiusAxis().getExtent();

        var ticksAngles = angleAxis.getTicksCoords();
        var minorTickAngles = angleAxis.getMinorTicksCoords();

        var labels = zrUtil.map(angleAxis.getViewLabels(), function (labelItem) {
            var labelItem = zrUtil.clone(labelItem);
            labelItem.coord = angleAxis.dataToCoord(labelItem.tickValue);
            return labelItem;
        });

        fixAngleOverlap(labels);
        fixAngleOverlap(ticksAngles);

        zrUtil.each(elementList, function (name) {
            if (angleAxisModel.get(name + '.show')
                && (!angleAxis.scale.isBlank() || name === 'axisLine')
            ) {
                this['_' + name](angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels);
            }
        }, this);
    },

    /**
     * @private
     */
    _axisLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) {
        var lineStyleModel = angleAxisModel.getModel('axisLine.lineStyle');

        // extent id of the axis radius (r0 and r)
        var rId = getRadiusIdx(polar);
        var r0Id = rId ? 0 : 1;

        var shape;
        if (radiusExtent[r0Id] === 0) {
            shape = new graphic.Circle({
                shape: {
                    cx: polar.cx,
                    cy: polar.cy,
                    r: radiusExtent[rId]
                },
                style: lineStyleModel.getLineStyle(),
                z2: 1,
                silent: true
            });
        }
        else {
            shape = new graphic.Ring({
                shape: {
                    cx: polar.cx,
                    cy: polar.cy,
                    r: radiusExtent[rId],
                    r0: radiusExtent[r0Id]
                },
                style: lineStyleModel.getLineStyle(),
                z2: 1,
                silent: true
            });
        }
        shape.style.fill = null;
        this.group.add(shape);
    },

    /**
     * @private
     */
    _axisTick: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        var tickModel = angleAxisModel.getModel('axisTick');

        var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length');
        var radius = radiusExtent[getRadiusIdx(polar)];

        var lines = zrUtil.map(ticksAngles, function (tickAngleItem) {
            return new graphic.Line({
                shape: getAxisLineShape(polar, [radius, radius + tickLen], tickAngleItem.coord)
            });
        });
        this.group.add(graphic.mergePath(
            lines, {
                style: zrUtil.defaults(
                    tickModel.getModel('lineStyle').getLineStyle(),
                    {
                        stroke: angleAxisModel.get('axisLine.lineStyle.color')
                    }
                )
            }
        ));
    },

    /**
     * @private
     */
    _minorTick: function (angleAxisModel, polar, tickAngles, minorTickAngles, radiusExtent) {
        if (!minorTickAngles.length) {
            return;
        }

        var tickModel = angleAxisModel.getModel('axisTick');
        var minorTickModel = angleAxisModel.getModel('minorTick');

        var tickLen = (tickModel.get('inside') ? -1 : 1) * minorTickModel.get('length');
        var radius = radiusExtent[getRadiusIdx(polar)];

        var lines = [];

        for (var i = 0; i < minorTickAngles.length; i++) {
            for (var k = 0; k < minorTickAngles[i].length; k++) {
                lines.push(new graphic.Line({
                    shape: getAxisLineShape(polar, [radius, radius + tickLen], minorTickAngles[i][k].coord)
                }));
            }
        }

        this.group.add(graphic.mergePath(
            lines, {
                style: zrUtil.defaults(
                    minorTickModel.getModel('lineStyle').getLineStyle(),
                    zrUtil.defaults(
                        tickModel.getLineStyle(), {
                            stroke: angleAxisModel.get('axisLine.lineStyle.color')
                        }
                    )
                )
            }
        ));
    },

    /**
     * @private
     */
    _axisLabel: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels) {
        var rawCategoryData = angleAxisModel.getCategories(true);

        var commonLabelModel = angleAxisModel.getModel('axisLabel');

        var labelMargin = commonLabelModel.get('margin');
        var triggerEvent = angleAxisModel.get('triggerEvent');

        // Use length of ticksAngles because it may remove the last tick to avoid overlapping
        zrUtil.each(labels, function (labelItem, idx) {
            var labelModel = commonLabelModel;
            var tickValue = labelItem.tickValue;

            var r = radiusExtent[getRadiusIdx(polar)];
            var p = polar.coordToPoint([r + labelMargin, labelItem.coord]);
            var cx = polar.cx;
            var cy = polar.cy;

            var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3
                ? 'center' : (p[0] > cx ? 'left' : 'right');
            var labelTextVerticalAlign = Math.abs(p[1] - cy) / r < 0.3
                ? 'middle' : (p[1] > cy ? 'top' : 'bottom');

            if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) {
                labelModel = new Model(
                    rawCategoryData[tickValue].textStyle, commonLabelModel, commonLabelModel.ecModel
                );
            }

            var textEl = new graphic.Text({
                silent: AxisBuilder.isLabelSilent(angleAxisModel)
            });
            this.group.add(textEl);
            graphic.setTextStyle(textEl.style, labelModel, {
                x: p[0],
                y: p[1],
                textFill: labelModel.getTextColor() || angleAxisModel.get('axisLine.lineStyle.color'),
                text: labelItem.formattedLabel,
                textAlign: labelTextAlign,
                textVerticalAlign: labelTextVerticalAlign
            });

            // Pack data for mouse event
            if (triggerEvent) {
                textEl.eventData = AxisBuilder.makeAxisEventDataBase(angleAxisModel);
                textEl.eventData.targetType = 'axisLabel';
                textEl.eventData.value = labelItem.rawLabel;
            }

        }, this);
    },

    /**
     * @private
     */
    _splitLine: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        var splitLineModel = angleAxisModel.getModel('splitLine');
        var lineStyleModel = splitLineModel.getModel('lineStyle');
        var lineColors = lineStyleModel.get('color');
        var lineCount = 0;

        lineColors = lineColors instanceof Array ? lineColors : [lineColors];

        var splitLines = [];

        for (var i = 0; i < ticksAngles.length; i++) {
            var colorIndex = (lineCount++) % lineColors.length;
            splitLines[colorIndex] = splitLines[colorIndex] || [];
            splitLines[colorIndex].push(new graphic.Line({
                shape: getAxisLineShape(polar, radiusExtent, ticksAngles[i].coord)
            }));
        }

        // Simple optimization
        // Batching the lines if color are the same
        for (var i = 0; i < splitLines.length; i++) {
            this.group.add(graphic.mergePath(splitLines[i], {
                style: zrUtil.defaults({
                    stroke: lineColors[i % lineColors.length]
                }, lineStyleModel.getLineStyle()),
                silent: true,
                z: angleAxisModel.get('z')
            }));
        }
    },

    /**
     * @private
     */
    _minorSplitLine: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        if (!minorTickAngles.length) {
            return;
        }

        var minorSplitLineModel = angleAxisModel.getModel('minorSplitLine');
        var lineStyleModel = minorSplitLineModel.getModel('lineStyle');

        var lines = [];

        for (var i = 0; i < minorTickAngles.length; i++) {
            for (var k = 0; k < minorTickAngles[i].length; k++) {
                lines.push(new graphic.Line({
                    shape: getAxisLineShape(polar, radiusExtent, minorTickAngles[i][k].coord)
                }));
            }
        }

        this.group.add(graphic.mergePath(lines, {
            style: lineStyleModel.getLineStyle(),
            silent: true,
            z: angleAxisModel.get('z')
        }));
    },

    /**
     * @private
     */
    _splitArea: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        if (!ticksAngles.length) {
            return;
        }

        var splitAreaModel = angleAxisModel.getModel('splitArea');
        var areaStyleModel = splitAreaModel.getModel('areaStyle');
        var areaColors = areaStyleModel.get('color');
        var lineCount = 0;

        areaColors = areaColors instanceof Array ? areaColors : [areaColors];

        var splitAreas = [];

        var RADIAN = Math.PI / 180;
        var prevAngle = -ticksAngles[0].coord * RADIAN;
        var r0 = Math.min(radiusExtent[0], radiusExtent[1]);
        var r1 = Math.max(radiusExtent[0], radiusExtent[1]);

        var clockwise = angleAxisModel.get('clockwise');

        for (var i = 1; i < ticksAngles.length; i++) {
            var colorIndex = (lineCount++) % areaColors.length;
            splitAreas[colorIndex] = splitAreas[colorIndex] || [];
            splitAreas[colorIndex].push(new graphic.Sector({
                shape: {
                    cx: polar.cx,
                    cy: polar.cy,
                    r0: r0,
                    r: r1,
                    startAngle: prevAngle,
                    endAngle: -ticksAngles[i].coord * RADIAN,
                    clockwise: clockwise
                },
                silent: true
            }));
            prevAngle = -ticksAngles[i].coord * RADIAN;
        }

        // Simple optimization
        // Batching the lines if color are the same
        for (var i = 0; i < splitAreas.length; i++) {
            this.group.add(graphic.mergePath(splitAreas[i], {
                style: zrUtil.defaults({
                    fill: areaColors[i % areaColors.length]
                }, areaStyleModel.getAreaStyle()),
                silent: true
            }));
        }
    }
});<commit_msg>fix(polar): fix axis line not display in polar caused by #11705<commit_after>/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import * as zrUtil from 'zrender/src/core/util';
import * as graphic from '../../util/graphic';
import Model from '../../model/Model';
import AxisView from './AxisView';
import AxisBuilder from './AxisBuilder';

var elementList = ['axisLine', 'axisLabel', 'axisTick', 'minorTick', 'splitLine', 'minorSplitLine', 'splitArea'];

function getAxisLineShape(polar, rExtent, angle) {
    rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse());
    var start = polar.coordToPoint([rExtent[0], angle]);
    var end = polar.coordToPoint([rExtent[1], angle]);

    return {
        x1: start[0],
        y1: start[1],
        x2: end[0],
        y2: end[1]
    };
}

function getRadiusIdx(polar) {
    var radiusAxis = polar.getRadiusAxis();
    return radiusAxis.inverse ? 0 : 1;
}

// Remove the last tick which will overlap the first tick
function fixAngleOverlap(list) {
    var firstItem = list[0];
    var lastItem = list[list.length - 1];
    if (firstItem
        && lastItem
        && Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4
    ) {
        list.pop();
    }
}

export default AxisView.extend({

    type: 'angleAxis',

    axisPointerClass: 'PolarAxisPointer',

    render: function (angleAxisModel, ecModel) {
        this.group.removeAll();
        if (!angleAxisModel.get('show')) {
            return;
        }

        var angleAxis = angleAxisModel.axis;
        var polar = angleAxis.polar;
        var radiusExtent = polar.getRadiusAxis().getExtent();

        var ticksAngles = angleAxis.getTicksCoords();
        var minorTickAngles = angleAxis.getMinorTicksCoords();

        var labels = zrUtil.map(angleAxis.getViewLabels(), function (labelItem) {
            var labelItem = zrUtil.clone(labelItem);
            labelItem.coord = angleAxis.dataToCoord(labelItem.tickValue);
            return labelItem;
        });

        fixAngleOverlap(labels);
        fixAngleOverlap(ticksAngles);

        zrUtil.each(elementList, function (name) {
            if (angleAxisModel.get(name + '.show')
                && (!angleAxis.scale.isBlank() || name === 'axisLine')
            ) {
                this['_' + name](angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels);
            }
        }, this);
    },

    /**
     * @private
     */
    _axisLine: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        var lineStyleModel = angleAxisModel.getModel('axisLine.lineStyle');

        // extent id of the axis radius (r0 and r)
        var rId = getRadiusIdx(polar);
        var r0Id = rId ? 0 : 1;

        var shape;
        if (radiusExtent[r0Id] === 0) {
            shape = new graphic.Circle({
                shape: {
                    cx: polar.cx,
                    cy: polar.cy,
                    r: radiusExtent[rId]
                },
                style: lineStyleModel.getLineStyle(),
                z2: 1,
                silent: true
            });
        }
        else {
            shape = new graphic.Ring({
                shape: {
                    cx: polar.cx,
                    cy: polar.cy,
                    r: radiusExtent[rId],
                    r0: radiusExtent[r0Id]
                },
                style: lineStyleModel.getLineStyle(),
                z2: 1,
                silent: true
            });
        }
        shape.style.fill = null;
        this.group.add(shape);
    },

    /**
     * @private
     */
    _axisTick: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        var tickModel = angleAxisModel.getModel('axisTick');

        var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length');
        var radius = radiusExtent[getRadiusIdx(polar)];

        var lines = zrUtil.map(ticksAngles, function (tickAngleItem) {
            return new graphic.Line({
                shape: getAxisLineShape(polar, [radius, radius + tickLen], tickAngleItem.coord)
            });
        });
        this.group.add(graphic.mergePath(
            lines, {
                style: zrUtil.defaults(
                    tickModel.getModel('lineStyle').getLineStyle(),
                    {
                        stroke: angleAxisModel.get('axisLine.lineStyle.color')
                    }
                )
            }
        ));
    },

    /**
     * @private
     */
    _minorTick: function (angleAxisModel, polar, tickAngles, minorTickAngles, radiusExtent) {
        if (!minorTickAngles.length) {
            return;
        }

        var tickModel = angleAxisModel.getModel('axisTick');
        var minorTickModel = angleAxisModel.getModel('minorTick');

        var tickLen = (tickModel.get('inside') ? -1 : 1) * minorTickModel.get('length');
        var radius = radiusExtent[getRadiusIdx(polar)];

        var lines = [];

        for (var i = 0; i < minorTickAngles.length; i++) {
            for (var k = 0; k < minorTickAngles[i].length; k++) {
                lines.push(new graphic.Line({
                    shape: getAxisLineShape(polar, [radius, radius + tickLen], minorTickAngles[i][k].coord)
                }));
            }
        }

        this.group.add(graphic.mergePath(
            lines, {
                style: zrUtil.defaults(
                    minorTickModel.getModel('lineStyle').getLineStyle(),
                    zrUtil.defaults(
                        tickModel.getLineStyle(), {
                            stroke: angleAxisModel.get('axisLine.lineStyle.color')
                        }
                    )
                )
            }
        ));
    },

    /**
     * @private
     */
    _axisLabel: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels) {
        var rawCategoryData = angleAxisModel.getCategories(true);

        var commonLabelModel = angleAxisModel.getModel('axisLabel');

        var labelMargin = commonLabelModel.get('margin');
        var triggerEvent = angleAxisModel.get('triggerEvent');

        // Use length of ticksAngles because it may remove the last tick to avoid overlapping
        zrUtil.each(labels, function (labelItem, idx) {
            var labelModel = commonLabelModel;
            var tickValue = labelItem.tickValue;

            var r = radiusExtent[getRadiusIdx(polar)];
            var p = polar.coordToPoint([r + labelMargin, labelItem.coord]);
            var cx = polar.cx;
            var cy = polar.cy;

            var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3
                ? 'center' : (p[0] > cx ? 'left' : 'right');
            var labelTextVerticalAlign = Math.abs(p[1] - cy) / r < 0.3
                ? 'middle' : (p[1] > cy ? 'top' : 'bottom');

            if (rawCategoryData && rawCategoryData[tickValue] && rawCategoryData[tickValue].textStyle) {
                labelModel = new Model(
                    rawCategoryData[tickValue].textStyle, commonLabelModel, commonLabelModel.ecModel
                );
            }

            var textEl = new graphic.Text({
                silent: AxisBuilder.isLabelSilent(angleAxisModel)
            });
            this.group.add(textEl);
            graphic.setTextStyle(textEl.style, labelModel, {
                x: p[0],
                y: p[1],
                textFill: labelModel.getTextColor() || angleAxisModel.get('axisLine.lineStyle.color'),
                text: labelItem.formattedLabel,
                textAlign: labelTextAlign,
                textVerticalAlign: labelTextVerticalAlign
            });

            // Pack data for mouse event
            if (triggerEvent) {
                textEl.eventData = AxisBuilder.makeAxisEventDataBase(angleAxisModel);
                textEl.eventData.targetType = 'axisLabel';
                textEl.eventData.value = labelItem.rawLabel;
            }

        }, this);
    },

    /**
     * @private
     */
    _splitLine: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        var splitLineModel = angleAxisModel.getModel('splitLine');
        var lineStyleModel = splitLineModel.getModel('lineStyle');
        var lineColors = lineStyleModel.get('color');
        var lineCount = 0;

        lineColors = lineColors instanceof Array ? lineColors : [lineColors];

        var splitLines = [];

        for (var i = 0; i < ticksAngles.length; i++) {
            var colorIndex = (lineCount++) % lineColors.length;
            splitLines[colorIndex] = splitLines[colorIndex] || [];
            splitLines[colorIndex].push(new graphic.Line({
                shape: getAxisLineShape(polar, radiusExtent, ticksAngles[i].coord)
            }));
        }

        // Simple optimization
        // Batching the lines if color are the same
        for (var i = 0; i < splitLines.length; i++) {
            this.group.add(graphic.mergePath(splitLines[i], {
                style: zrUtil.defaults({
                    stroke: lineColors[i % lineColors.length]
                }, lineStyleModel.getLineStyle()),
                silent: true,
                z: angleAxisModel.get('z')
            }));
        }
    },

    /**
     * @private
     */
    _minorSplitLine: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        if (!minorTickAngles.length) {
            return;
        }

        var minorSplitLineModel = angleAxisModel.getModel('minorSplitLine');
        var lineStyleModel = minorSplitLineModel.getModel('lineStyle');

        var lines = [];

        for (var i = 0; i < minorTickAngles.length; i++) {
            for (var k = 0; k < minorTickAngles[i].length; k++) {
                lines.push(new graphic.Line({
                    shape: getAxisLineShape(polar, radiusExtent, minorTickAngles[i][k].coord)
                }));
            }
        }

        this.group.add(graphic.mergePath(lines, {
            style: lineStyleModel.getLineStyle(),
            silent: true,
            z: angleAxisModel.get('z')
        }));
    },

    /**
     * @private
     */
    _splitArea: function (angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) {
        if (!ticksAngles.length) {
            return;
        }

        var splitAreaModel = angleAxisModel.getModel('splitArea');
        var areaStyleModel = splitAreaModel.getModel('areaStyle');
        var areaColors = areaStyleModel.get('color');
        var lineCount = 0;

        areaColors = areaColors instanceof Array ? areaColors : [areaColors];

        var splitAreas = [];

        var RADIAN = Math.PI / 180;
        var prevAngle = -ticksAngles[0].coord * RADIAN;
        var r0 = Math.min(radiusExtent[0], radiusExtent[1]);
        var r1 = Math.max(radiusExtent[0], radiusExtent[1]);

        var clockwise = angleAxisModel.get('clockwise');

        for (var i = 1; i < ticksAngles.length; i++) {
            var colorIndex = (lineCount++) % areaColors.length;
            splitAreas[colorIndex] = splitAreas[colorIndex] || [];
            splitAreas[colorIndex].push(new graphic.Sector({
                shape: {
                    cx: polar.cx,
                    cy: polar.cy,
                    r0: r0,
                    r: r1,
                    startAngle: prevAngle,
                    endAngle: -ticksAngles[i].coord * RADIAN,
                    clockwise: clockwise
                },
                silent: true
            }));
            prevAngle = -ticksAngles[i].coord * RADIAN;
        }

        // Simple optimization
        // Batching the lines if color are the same
        for (var i = 0; i < splitAreas.length; i++) {
            this.group.add(graphic.mergePath(splitAreas[i], {
                style: zrUtil.defaults({
                    fill: areaColors[i % areaColors.length]
                }, areaStyleModel.getAreaStyle()),
                silent: true
            }));
        }
    }
});<|endoftext|>


<commit_before>var files = [
    'lines.png',
    'interface.js',
    'browser.js',
    'browser.css',
    'skydome.jpg',

    'presentations/presentations.js',
    'presentations/htmlLoader.js',

    'autopilot/autopilot.js',

    'rois/roi.js',
    'rois/rois.js',
    'rois/processor.js',
    'rois/loader.js',
    'rois/pano/pano.js',
    'rois/pano/tile.js',
    'rois/location/location.js',
    'rois/photo/photo.js',

    'ui/ui.js',
    'ui/control/compass.js',
    'ui/control/credits.js',
    'ui/control/explore-bar.js',
    'ui/control/fallback.js',
    'ui/control/holder.js',
    'ui/control/logo.js',
    'ui/control/map.js',
    'ui/control/zoom.js',
    'ui/element/draggable.js',
    'ui/element/element.js',
    'ui/element/event.js',
    'ui/element/events.js',

    'control-mode/control-mode.js',
    'control-mode/map-observer.js',
    'control-mode/disabled.js',
    'control-mode/pano.js',

    'utility/dom.js',
    'utility/utility.js',
];

addSiteModule("melown-api-devel", files, {});
<commit_msg>Updated module.jake<commit_after>var files = [
    'lines.png',
    'interface.js',
    'browser.js',
    'browser.css',
    'skydome.jpg',

    'presentation/presentation.js',
    'presentation/htmlLoader.js',

    'autopilot/autopilot.js',

    'rois/roi.js',
    'rois/rois.js',
    'rois/processor.js',
    'rois/loader.js',
    'rois/pano/pano.js',
    'rois/pano/tile.js',
    'rois/location/location.js',
    'rois/photo/photo.js',

    'ui/ui.js',
    'ui/control/compass.js',
    'ui/control/credits.js',
    'ui/control/explore-bar.js',
    'ui/control/fallback.js',
    'ui/control/holder.js',
    'ui/control/logo.js',
    'ui/control/map.js',
    'ui/control/zoom.js',
    'ui/element/draggable.js',
    'ui/element/element.js',
    'ui/element/event.js',
    'ui/element/events.js',

    'control-mode/control-mode.js',
    'control-mode/map-observer.js',
    'control-mode/disabled.js',
    'control-mode/pano.js',

    'utility/dom.js',
    'utility/utility.js',
];

addSiteModule("melown-api-devel", files, {});
<|endoftext|>


<commit_before>var spawn = require('win-spawn')

var ps = spawn('cmake-js', [
  'rebuild',
  '-r', process.env.npm_config_runtime || 'node',
  '-v', process.env.npm_config_target || process.versions.node,
  '--abi', process.env.npm_config_abi || process.versions.modules
])
ps.stdout.pipe(process.stdout)
ps.stderr.pipe(process.stderr)
ps.on('exit', function (code) {
  process.exit(code)
})
<commit_msg>add debug output to build script<commit_after>var spawn = require('win-spawn')

var runtime = process.env.npm_config_runtime || 'node'
var target = process.env.npm_config_target || process.versions.node
var abi = process.env.npm_config_abi || process.versions.modules

console.log('BUILD for %s@%s (abi=%s)', runtime, target, abi)

var ps = spawn('cmake-js', [
  'rebuild',
  '-r', runtime,
  '-v', target,
  '--abi', abi
])
ps.stdout.pipe(process.stdout)
ps.stderr.pipe(process.stderr)
ps.on('exit', function (code) {
  process.exit(code)
})
<|endoftext|>