<commit_before>//     uuid.js
//
//     Copyright (c) 2010-2012 Robert Kieffer
//     MIT License - http://opensource.org/licenses/mit-license.php

(function() {
  var _global = this;

  // Unique ID creation requires a high quality random # generator.  We feature
  // detect to determine the best RNG source, normalizing to a function that
  // returns 128-bits of randomness, since that's what's usually required
  var _rng;

  // Node.js crypto-based RNG - http://nodejs.org/docs/v0.6.2/api/crypto.html
  //
  // Moderately fast, high quality
  if (typeof(require) == 'function') {
    try {
      var _rb = require('crypto').randomBytes;
      _rng = _rb && function() {return _rb(16);};
    } catch(e) {}
  }

  if (!_rng && _global.crypto && crypto.getRandomValues) {
    // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
    //
    // Moderately fast, high quality
    var _rnds8 = new Uint8Array(16);
    _rng = function whatwgRNG() {
      crypto.getRandomValues(_rnds8);
      return _rnds8;
    };
  }

  if (!_rng) {
    // Math.random()-based (RNG)
    //
    // If all else fails, use Math.random().  It's fast, but is of unspecified
    // quality.
    var  _rnds = new Array(16);
    _rng = function() {
      for (var i = 0, r; i < 16; i++) {
        if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
        _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
      }

      return _rnds;
    };
  }

  // Buffer class to use
  var BufferClass = typeof(Buffer) == 'function' ? Buffer : Array;

  // Maps for number <-> hex string conversion
  var _byteToHex = [];
  var _hexToByte = {};
  for (var i = 0; i < 256; i++) {
    _byteToHex[i] = (i + 0x100).toString(16).substr(1);
    _hexToByte[_byteToHex[i]] = i;
  }

  // **`parse()` - Parse a UUID into it's component bytes**
  function parse(s, buf, offset) {
    var i = (buf && offset) || 0, ii = 0;

    buf = buf || [];
    s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
      if (ii < 16) { // Don't overflow!
        buf[i + ii++] = _hexToByte[oct];
      }
    });

    // Zero out remaining bytes if string was short
    while (ii < 16) {
      buf[i + ii++] = 0;
    }

    return buf;
  }

  // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
  function unparse(buf, offset) {
    var i = offset || 0, bth = _byteToHex;
    return  bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]];
  }

  // **`v1()` - Generate time-based UUID**
  //
  // Inspired by https://github.com/LiosK/UUID.js
  // and http://docs.python.org/library/uuid.html

  // random #'s we need to init node and clockseq
  var _seedBytes = _rng();

  // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
  var _nodeId = [
    _seedBytes[0] | 0x01,
    _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
  ];

  // Per 4.2.2, randomize (14 bit) clockseq
  var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;

  // Previous uuid creation time
  var _lastMSecs = 0, _lastNSecs = 0;

  // See https://github.com/broofa/node-uuid for API details
  function v1(options, buf, offset) {
    var i = buf && offset || 0;
    var b = buf || [];

    options = options || {};

    var clockseq = options.clockseq != null ? options.clockseq : _clockseq;

    // UUID timestamps are 100 nano-second units since the Gregorian epoch,
    // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so
    // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
    // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
    var msecs = options.msecs != null ? options.msecs : new Date().getTime();

    // Per 4.2.1.2, use count of uuid's generated during the current clock
    // cycle to simulate higher resolution clock
    var nsecs = options.nsecs != null ? options.nsecs : _lastNSecs + 1;

    // Time since last uuid creation (in msecs)
    var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;

    // Per 4.2.1.2, Bump clockseq on clock regression
    if (dt < 0 && options.clockseq == null) {
      clockseq = clockseq + 1 & 0x3fff;
    }

    // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
    // time interval
    if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
      nsecs = 0;
    }

    // Per 4.2.1.2 Throw error if too many uuids are requested
    if (nsecs >= 10000) {
      throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
    }

    _lastMSecs = msecs;
    _lastNSecs = nsecs;
    _clockseq = clockseq;

    // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
    msecs += 12219292800000;

    // `time_low`
    var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
    b[i++] = tl >>> 24 & 0xff;
    b[i++] = tl >>> 16 & 0xff;
    b[i++] = tl >>> 8 & 0xff;
    b[i++] = tl & 0xff;

    // `time_mid`
    var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
    b[i++] = tmh >>> 8 & 0xff;
    b[i++] = tmh & 0xff;

    // `time_high_and_version`
    b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
    b[i++] = tmh >>> 16 & 0xff;

    // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
    b[i++] = clockseq >>> 8 | 0x80;

    // `clock_seq_low`
    b[i++] = clockseq & 0xff;

    // `node`
    var node = options.node || _nodeId;
    for (var n = 0; n < 6; n++) {
      b[i + n] = node[n];
    }

    return buf ? buf : unparse(b);
  }

  // **`v4()` - Generate random UUID**

  // See https://github.com/broofa/node-uuid for API details
  function v4(options, buf, offset) {
    // Deprecated - 'format' argument, as supported in v1.2
    var i = buf && offset || 0;

    if (typeof(options) == 'string') {
      buf = options == 'binary' ? new BufferClass(16) : null;
      options = null;
    }
    options = options || {};

    var rnds = options.random || (options.rng || _rng)();

    // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
    rnds[6] = (rnds[6] & 0x0f) | 0x40;
    rnds[8] = (rnds[8] & 0x3f) | 0x80;

    // Copy bytes to buffer, if provided
    if (buf) {
      for (var ii = 0; ii < 16; ii++) {
        buf[i + ii] = rnds[ii];
      }
    }

    return buf || unparse(rnds);
  }

  // Export public API
  var uuid = v4;
  uuid.v1 = v1;
  uuid.v4 = v4;
  uuid.parse = parse;
  uuid.unparse = unparse;
  uuid.BufferClass = BufferClass;

  if (_global.define && define.amd) {
    // Publish as AMD module
    define(function() {return uuid;});
  } else if (typeof(module) != 'undefined' && module.exports) {
    // Publish as node.js module
    module.exports = uuid;
  } else {
    // Publish as global (in browsers)
    var _previousRoot = _global.uuid;

    // **`noConflict()` - (browser only) to reset global 'uuid' var**
    uuid.noConflict = function() {
      _global.uuid = _previousRoot;
      return uuid;
    };

    _global.uuid = uuid;
  }
}());
<commit_msg>Add escape for non-browser and non-Node.js environments<commit_after>//     uuid.js
//
//     Copyright (c) 2010-2012 Robert Kieffer
//     MIT License - http://opensource.org/licenses/mit-license.php

(function() {
  var _global = this;

  // Unique ID creation requires a high quality random # generator.  We feature
  // detect to determine the best RNG source, normalizing to a function that
  // returns 128-bits of randomness, since that's what's usually required
  var _rng;

  // Node.js crypto-based RNG - http://nodejs.org/docs/v0.6.2/api/crypto.html
  //
  // Moderately fast, high quality
  if (typeof(require) == 'function') {
    try {
      var _rb = require('crypto').randomBytes;
      _rng = _rb && function() {return _rb(16);};
    } catch(e) {}
  }

  if (!_rng && _global.crypto && crypto.getRandomValues) {
    // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
    //
    // Moderately fast, high quality
    var _rnds8 = new Uint8Array(16);
    _rng = function whatwgRNG() {
      crypto.getRandomValues(_rnds8);
      return _rnds8;
    };
  }

  if (!_rng) {
    // Math.random()-based (RNG)
    //
    // If all else fails, use Math.random().  It's fast, but is of unspecified
    // quality.
    var  _rnds = new Array(16);
    _rng = function() {
      for (var i = 0, r; i < 16; i++) {
        if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
        _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
      }

      return _rnds;
    };
  }

  // Buffer class to use
  var BufferClass = typeof(Buffer) == 'function' ? Buffer : Array;

  // Maps for number <-> hex string conversion
  var _byteToHex = [];
  var _hexToByte = {};
  for (var i = 0; i < 256; i++) {
    _byteToHex[i] = (i + 0x100).toString(16).substr(1);
    _hexToByte[_byteToHex[i]] = i;
  }

  // **`parse()` - Parse a UUID into it's component bytes**
  function parse(s, buf, offset) {
    var i = (buf && offset) || 0, ii = 0;

    buf = buf || [];
    s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
      if (ii < 16) { // Don't overflow!
        buf[i + ii++] = _hexToByte[oct];
      }
    });

    // Zero out remaining bytes if string was short
    while (ii < 16) {
      buf[i + ii++] = 0;
    }

    return buf;
  }

  // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
  function unparse(buf, offset) {
    var i = offset || 0, bth = _byteToHex;
    return  bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]];
  }

  // **`v1()` - Generate time-based UUID**
  //
  // Inspired by https://github.com/LiosK/UUID.js
  // and http://docs.python.org/library/uuid.html

  // random #'s we need to init node and clockseq
  var _seedBytes = _rng();

  // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
  var _nodeId = [
    _seedBytes[0] | 0x01,
    _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
  ];

  // Per 4.2.2, randomize (14 bit) clockseq
  var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;

  // Previous uuid creation time
  var _lastMSecs = 0, _lastNSecs = 0;

  // See https://github.com/broofa/node-uuid for API details
  function v1(options, buf, offset) {
    var i = buf && offset || 0;
    var b = buf || [];

    options = options || {};

    var clockseq = options.clockseq != null ? options.clockseq : _clockseq;

    // UUID timestamps are 100 nano-second units since the Gregorian epoch,
    // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so
    // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
    // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
    var msecs = options.msecs != null ? options.msecs : new Date().getTime();

    // Per 4.2.1.2, use count of uuid's generated during the current clock
    // cycle to simulate higher resolution clock
    var nsecs = options.nsecs != null ? options.nsecs : _lastNSecs + 1;

    // Time since last uuid creation (in msecs)
    var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;

    // Per 4.2.1.2, Bump clockseq on clock regression
    if (dt < 0 && options.clockseq == null) {
      clockseq = clockseq + 1 & 0x3fff;
    }

    // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
    // time interval
    if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
      nsecs = 0;
    }

    // Per 4.2.1.2 Throw error if too many uuids are requested
    if (nsecs >= 10000) {
      throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
    }

    _lastMSecs = msecs;
    _lastNSecs = nsecs;
    _clockseq = clockseq;

    // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
    msecs += 12219292800000;

    // `time_low`
    var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
    b[i++] = tl >>> 24 & 0xff;
    b[i++] = tl >>> 16 & 0xff;
    b[i++] = tl >>> 8 & 0xff;
    b[i++] = tl & 0xff;

    // `time_mid`
    var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
    b[i++] = tmh >>> 8 & 0xff;
    b[i++] = tmh & 0xff;

    // `time_high_and_version`
    b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
    b[i++] = tmh >>> 16 & 0xff;

    // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
    b[i++] = clockseq >>> 8 | 0x80;

    // `clock_seq_low`
    b[i++] = clockseq & 0xff;

    // `node`
    var node = options.node || _nodeId;
    for (var n = 0; n < 6; n++) {
      b[i + n] = node[n];
    }

    return buf ? buf : unparse(b);
  }

  // **`v4()` - Generate random UUID**

  // See https://github.com/broofa/node-uuid for API details
  function v4(options, buf, offset) {
    // Deprecated - 'format' argument, as supported in v1.2
    var i = buf && offset || 0;

    if (typeof(options) == 'string') {
      buf = options == 'binary' ? new BufferClass(16) : null;
      options = null;
    }
    options = options || {};

    var rnds = options.random || (options.rng || _rng)();

    // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
    rnds[6] = (rnds[6] & 0x0f) | 0x40;
    rnds[8] = (rnds[8] & 0x3f) | 0x80;

    // Copy bytes to buffer, if provided
    if (buf) {
      for (var ii = 0; ii < 16; ii++) {
        buf[i + ii] = rnds[ii];
      }
    }

    return buf || unparse(rnds);
  }

  // Export public API
  var uuid = v4;
  uuid.v1 = v1;
  uuid.v4 = v4;
  uuid.parse = parse;
  uuid.unparse = unparse;
  uuid.BufferClass = BufferClass;

  if (_global.define && define.amd) {
    // Publish as AMD module
    define(function() {return uuid;});
  } else if (typeof(module) != 'undefined' && module.exports) {
    // Publish as node.js module
    module.exports = uuid;
  } else {
    // Publish as global (in browsers)
    var _previousRoot = _global.uuid;

    // **`noConflict()` - (browser only) to reset global 'uuid' var**
    uuid.noConflict = function() {
      _global.uuid = _previousRoot;
      return uuid;
    };

    _global.uuid = uuid;
  }
}).call(this);
<|endoftext|>


<commit_before>
import LikesList, { getImage } from "../";
import { mount } from "enzyme";
import { mountToJson } from "enzyme-to-json";

jest.mock("../../../@primitives/UI/cards", () => ({
  MiniCard: (props) => <div />,
}));

describe("getImage", () => {
  let mockImages = [
    { fileLabel: "1:1", url: "harambe"},
    { fileLabel: "2:1", url: "cincinnati zoo"},
  ];

  it("should return empty string with null input", () => {
    expect(getImage()).toEqual("");
  });
  it("should find image by matching with label", () => {
    expect(getImage(mockImages, "1:1")).toEqual("harambe");
  });
  it("should default to 2:1 if no label passed", () => {
    expect(getImage(mockImages)).toEqual("cincinnati zoo");
  });
});

describe("likes list", () => {

  const mockLikes = [
    {__typename: "Group", id: "12345", name: "hello", photo: "zookeeper"},
    {
      __typename: "Content",
      channelName: "study_entries",
      content: {images: [{ fileLabel: "1:1", url: "harambe"}]},
      id: "12345",
      parent: null,
      title: "harambe"
    }
  ];

  const generateComponent = (additionalProps) =>
    <LikesList {...additionalProps} />

  it("should render with no likes", () => {
    const component = mount(generateComponent());
    expect(component.find("div").length).toEqual(1);
    expect(mountToJson(component)).toMatchSnapshot();
  });

  it("should render likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").length).toEqual(2);
    expect(mountToJson(component)).toMatchSnapshot();
  });

  it("should properly parse category for likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").get(0).props.category).toEqual("Groups");
    expect(component.find("MiniCard").get(1).props.category).toEqual("Devotionals");
  });

  it("should properly parse title for likes", () => {
    const component = mount(generateComponent({
      likes: [...mockLikes, {
        __typename: "Content",
        channelName: "study_entries",
        content: {images: []},
        id: "12345",
        parent: {content: {images: [{ fileLabel: "1:1", url: "harambe"}]}},
        title: "harambe's dad"
      }],
    }));
    expect(component.find("MiniCard").get(0).props.title).toEqual("hello");
    expect(component.find("MiniCard").get(1).props.title).toEqual("harambe");
    expect(component.find("MiniCard").get(2).props.title).toEqual("harambe's dad");
  });

  it("should properly parse image for likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").get(0).props.image).toEqual("zookeeper");
    expect(component.find("MiniCard").get(1).props.image).toEqual("harambe");
  });

  it("should properly parse icon for likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").get(0).props.icon).toEqual("icon-groups");
    expect(component.find("MiniCard").get(1).props.icon).toEqual("icon-category-text");
  });

  it("should properly parse link for likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").get(0).props.link).toEqual("/groups/12345");
    expect(component.find("MiniCard").get(1).props.link).toEqual("/studies/null/entry/12345");
  });

});
<commit_msg>fixed test to be in the right place.<commit_after>
import LikesList, { getImage } from "../";
import { mount } from "enzyme";
import { mountToJson } from "enzyme-to-json";

jest.mock("../../../@primitives/UI/cards", () => ({
  MiniCard: (props) => <div />,
}));

describe("getImage", () => {
  let mockImages = [
    { fileLabel: "1:1", url: "harambe"},
    { fileLabel: "2:1", url: "cincinnati zoo"},
  ];

  it("should return empty string with null input", () => {
    expect(getImage()).toEqual("");
  });
  it("should find image by matching with label", () => {
    expect(getImage(mockImages, "1:1")).toEqual("harambe");
  });
  it("should default to 2:1 if no label passed", () => {
    expect(getImage(mockImages)).toEqual("cincinnati zoo");
  });
});

describe("likes list", () => {

  const mockLikes = [
    {__typename: "Group", id: "12345", name: "hello", photo: "zookeeper"},
    {
      __typename: "Content",
      channelName: "study_entries",
      content: {images: [{ fileLabel: "1:1", url: "harambe"}]},
      id: "12345",
      parent: null,
      title: "harambe"
    }
  ];

  const generateComponent = (additionalProps) =>
    <LikesList {...additionalProps} />

  it("should render with no likes", () => {
    const component = mount(generateComponent());
    expect(component.find("div").length).toEqual(1);
    expect(mountToJson(component)).toMatchSnapshot();
  });

  it("should render likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").length).toEqual(2);
    expect(mountToJson(component)).toMatchSnapshot();
  });

  it("should properly parse category for likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").get(0).props.category).toEqual("Groups");
    expect(component.find("MiniCard").get(1).props.category).toEqual("Devotionals");
  });

  it("should properly parse title for likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").get(0).props.title).toEqual("hello");
    expect(component.find("MiniCard").get(1).props.title).toEqual("harambe");
  });

  it("should properly parse image for likes", () => {
    const component = mount(generateComponent({
      likes: [...mockLikes, {
        __typename: "Content",
        channelName: "study_entries",
        content: {images: []},
        id: "12345",
        parent: {content: {images: [{ fileLabel: "1:1", url: "harambe's dad"}]}},
        title: "harambe's dad"
      }],
    }));
    expect(component.find("MiniCard").get(0).props.image).toEqual("zookeeper");
    expect(component.find("MiniCard").get(1).props.image).toEqual("harambe");
    expect(component.find("MiniCard").get(2).props.image).toEqual("harambe's dad");
  });

  it("should properly parse icon for likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").get(0).props.icon).toEqual("icon-groups");
    expect(component.find("MiniCard").get(1).props.icon).toEqual("icon-category-text");
  });

  it("should properly parse link for likes", () => {
    const component = mount(generateComponent({
      likes: mockLikes,
    }));
    expect(component.find("MiniCard").get(0).props.link).toEqual("/groups/12345");
    expect(component.find("MiniCard").get(1).props.link).toEqual("/studies/null/entry/12345");
  });

});
<|endoftext|>


<commit_before>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

(function() {

  describe('unit detail viewlet', function() {
    var db, service, unit, unitDetails, Y, updateAddress, ViewletManager,
        manager;
    var requirements = [
      'juju-models', 'unit-details-view', 'juju-viewlet-manager'];

    before(function(done) {
      Y = YUI(GlobalConfig).use(requirements, function(Y) {
        var models = Y.namespace('juju.models');
        var viewlets = Y.namespace('juju.viewlets');
        updateAddress = viewlets.updateUnitAddress;
        ViewletManager = viewlets.ViewletManager;
        db = new models.Database();
        service = db.services.add({id: 'haproxy'});
        unit = db.addUnits({
          id: 'haproxy/42',
          service: 'haproxy',
          annotations: {'landscape-computer': '+unit:haproxy-42'},
          agent_state: 'peachy',
          agent_state_info: 'keen',
          public_address: 'public-address',
          private_address: 'private-address',
          open_ports: [80, 443]
        });
        unitDetails = new viewlets.UnitDetails();
        done();
      });
    });

    after(function() {
      if (manager) {
        manager.destroy();
      }
    });

    it('correctly generates the viewlet template context', function() {
      var context = unitDetails.getContext(db, service, unit);
      var expectedKeys = ['unit', 'relations'];
      assert.deepEqual(Y.Object.keys(context), expectedKeys);
      assert.deepEqual(context.unit, unit);
    });

    it('mutates a node without an address or ports', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node);
      assert.strictEqual(node.getHTML(), '');
    });

    it('mutates a node without an address', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, null, [80]);
      assert.strictEqual(node.getHTML(), '');
    });

    it('mutates a node with an address', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1');
      assert.strictEqual(node.getHTML(), '10.0.0.1');
    });

    it('mutates a node with an address and non-http[s] port', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', [8080]);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 8080/tcp');
      assert.strictEqual(node.all('a').size(), 1);
      assert.strictEqual(node.one('a').get('href'), 'http://10.0.0.1:8080/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '8080/tcp');
    });

    it('mutates a node with an address and a port/tcp pair', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', ['8080/tcp']);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 8080/tcp');
      assert.strictEqual(node.all('a').size(), 1);
      assert.strictEqual(node.one('a').get('href'), 'http://10.0.0.1:8080/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '8080/tcp');
    });

    it('mutates a node with an address and a port/udp pair', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', ['8080/udp']);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 8080/udp');
      assert.strictEqual(node.all('a').size(), 0);
    });

    it('mutates a node with an address and http port', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', [80]);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 80/tcp');
      assert.strictEqual(node.all('a').size(), 2);
      assert.strictEqual(node.one('a').get('href'), 'http://10.0.0.1/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '10.0.0.1');
    });

    it('mutates a node with an address and https port', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', [443]);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 443/tcp');
      assert.strictEqual(node.all('a').size(), 2);
      assert.strictEqual(node.one('a').get('href'), 'https://10.0.0.1/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '10.0.0.1');
    });

    it('mutates a node with an address and multiple ports', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', [443, 8080]);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '),
          '10.0.0.1 | Ports 443/tcp, 8080/tcp');
      assert.strictEqual(node.all('a').size(), 3);
      assert.strictEqual(node.one('a').get('href'), 'https://10.0.0.1/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '10.0.0.1');
    });

    it('mutates a node with multiple port/protocol pairs', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', ['42/tcp', '47/udp']);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '),
          '10.0.0.1 | Ports 42/tcp, 47/udp');
      assert.strictEqual(node.all('a').size(), 1);
      assert.strictEqual(node.one('a').get('href'), 'http://10.0.0.1:42/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '42/tcp');
    });

    it('instantiates correctly when bound', function() {
      db.environment.set('annotations', {
        'landscape-url': 'http://landscape.example.com',
        'landscape-computers': '/computers/criteria/environment:test'
      });
      manager = new ViewletManager(
          { interval: 0,
            enableDatabinding: true,
            template: '

',
            container: Y.Node.create(
                '

' +
                '

'),
            model: service,
            views: {'unitDetails': unitDetails},
            viewletContainer: '.viewlet',
            db: db
          });
      manager.slots['left-hand-panel'] = {
        selector: '.leftSlot',
        scope: 'container'
      };
      manager.render();
      manager.showViewlet('unitDetails', db.units.revive(unit));
      var node = manager.views.unitDetails.get('container');
      assert.strictEqual(
          node.one('[data-bind="displayName"]').get('text'), 'haproxy/42');
      assert.strictEqual(
          node.one('[data-bind="agent_state"]').get('text'), 'peachy');
      assert.strictEqual(
          node.one('[data-bind="agent_state_info"]').get('text'),
          'Status Info: keen');
      assert.strictEqual(
          node.one('[data-bind="public_address"] a').get('text'),
          'public-address');
      assert.strictEqual(
          node.one('[data-bind="private_address"] a').get('text'),
          'private-address');
      assert.strictEqual(
          node.one(
              '[data-bind="annotations.landscape-computer"] a').get('href'),
          'http://landscape.example.com/computers/criteria/environment' +
          ':test+unit:haproxy-42/');
    });

    it.skip('includes the service relations in the template context', function() {
      db.relations.add({
        relation_id: 'haproxy:reverseproxy wordpress:website',
        endpoints: [
          ['haproxy', {role: 'requirer', name: 'reverseproxy'}],
          ['wordpress', {role: 'provider', name: 'website'}]
        ]
      });
      var context = unitDetails.getContext(db, service, unit);
      assert.strictEqual(context.relations.length, 1);
      var relation = context.relations[0];
      var expectedNear = {
        name: 'reverseproxy',
        role: 'requirer',
        service: 'haproxy'
      };
      var expectedFar = {
        name: 'website',
        role: 'provider',
        service: 'wordpress'
      };
      assert.deepEqual(relation.near, expectedNear);
      assert.deepEqual(relation.far, expectedFar);
    });

  });

})();
<commit_msg>Fixed lint.<commit_after>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

(function() {

  describe('unit detail viewlet', function() {
    var db, service, unit, unitDetails, Y, updateAddress, ViewletManager,
        manager;
    var requirements = [
      'juju-models', 'unit-details-view', 'juju-viewlet-manager'];

    before(function(done) {
      Y = YUI(GlobalConfig).use(requirements, function(Y) {
        var models = Y.namespace('juju.models');
        var viewlets = Y.namespace('juju.viewlets');
        updateAddress = viewlets.updateUnitAddress;
        ViewletManager = viewlets.ViewletManager;
        db = new models.Database();
        service = db.services.add({id: 'haproxy'});
        unit = db.addUnits({
          id: 'haproxy/42',
          service: 'haproxy',
          annotations: {'landscape-computer': '+unit:haproxy-42'},
          agent_state: 'peachy',
          agent_state_info: 'keen',
          public_address: 'public-address',
          private_address: 'private-address',
          open_ports: [80, 443]
        });
        unitDetails = new viewlets.UnitDetails();
        done();
      });
    });

    after(function() {
      if (manager) {
        manager.destroy();
      }
    });

    it('correctly generates the viewlet template context', function() {
      var context = unitDetails.getContext(db, service, unit);
      var expectedKeys = ['unit', 'relations'];
      assert.deepEqual(Y.Object.keys(context), expectedKeys);
      assert.deepEqual(context.unit, unit);
    });

    it('mutates a node without an address or ports', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node);
      assert.strictEqual(node.getHTML(), '');
    });

    it('mutates a node without an address', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, null, [80]);
      assert.strictEqual(node.getHTML(), '');
    });

    it('mutates a node with an address', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1');
      assert.strictEqual(node.getHTML(), '10.0.0.1');
    });

    it('mutates a node with an address and non-http[s] port', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', [8080]);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 8080/tcp');
      assert.strictEqual(node.all('a').size(), 1);
      assert.strictEqual(node.one('a').get('href'), 'http://10.0.0.1:8080/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '8080/tcp');
    });

    it('mutates a node with an address and a port/tcp pair', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', ['8080/tcp']);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 8080/tcp');
      assert.strictEqual(node.all('a').size(), 1);
      assert.strictEqual(node.one('a').get('href'), 'http://10.0.0.1:8080/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '8080/tcp');
    });

    it('mutates a node with an address and a port/udp pair', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', ['8080/udp']);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 8080/udp');
      assert.strictEqual(node.all('a').size(), 0);
    });

    it('mutates a node with an address and http port', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', [80]);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 80/tcp');
      assert.strictEqual(node.all('a').size(), 2);
      assert.strictEqual(node.one('a').get('href'), 'http://10.0.0.1/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '10.0.0.1');
    });

    it('mutates a node with an address and https port', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', [443]);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '), '10.0.0.1 | Ports 443/tcp');
      assert.strictEqual(node.all('a').size(), 2);
      assert.strictEqual(node.one('a').get('href'), 'https://10.0.0.1/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '10.0.0.1');
    });

    it('mutates a node with an address and multiple ports', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', [443, 8080]);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '),
          '10.0.0.1 | Ports 443/tcp, 8080/tcp');
      assert.strictEqual(node.all('a').size(), 3);
      assert.strictEqual(node.one('a').get('href'), 'https://10.0.0.1/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '10.0.0.1');
    });

    it('mutates a node with multiple port/protocol pairs', function() {
      var node = Y.Node.create('Delete me');
      updateAddress(node, '10.0.0.1', ['42/tcp', '47/udp']);
      assert.strictEqual(
          node.get('text').replace(/\s/g, ' '),
          '10.0.0.1 | Ports 42/tcp, 47/udp');
      assert.strictEqual(node.all('a').size(), 1);
      assert.strictEqual(node.one('a').get('href'), 'http://10.0.0.1:42/');
      assert.strictEqual(node.one('a').get('target'), '_blank');
      assert.strictEqual(node.one('a').get('text'), '42/tcp');
    });

    it('instantiates correctly when bound', function() {
      db.environment.set('annotations', {
        'landscape-url': 'http://landscape.example.com',
        'landscape-computers': '/computers/criteria/environment:test'
      });
      manager = new ViewletManager(
          { interval: 0,
            enableDatabinding: true,
            template: '

',
            container: Y.Node.create(
                '

' +
                '

'),
            model: service,
            views: {'unitDetails': unitDetails},
            viewletContainer: '.viewlet',
            db: db
          });
      manager.slots['left-hand-panel'] = {
        selector: '.leftSlot',
        scope: 'container'
      };
      manager.render();
      manager.showViewlet('unitDetails', db.units.revive(unit));
      var node = manager.views.unitDetails.get('container');
      assert.strictEqual(
          node.one('[data-bind="displayName"]').get('text'), 'haproxy/42');
      assert.strictEqual(
          node.one('[data-bind="agent_state"]').get('text'), 'peachy');
      assert.strictEqual(
          node.one('[data-bind="agent_state_info"]').get('text'),
          'Status Info: keen');
      assert.strictEqual(
          node.one('[data-bind="public_address"] a').get('text'),
          'public-address');
      assert.strictEqual(
          node.one('[data-bind="private_address"] a').get('text'),
          'private-address');
      assert.strictEqual(
          node.one(
              '[data-bind="annotations.landscape-computer"] a').get('href'),
          'http://landscape.example.com/computers/criteria/environment' +
          ':test+unit:haproxy-42/');
    });

    it.skip('includes service relations in the template context', function() {
      db.relations.add({
        relation_id: 'haproxy:reverseproxy wordpress:website',
        endpoints: [
          ['haproxy', {role: 'requirer', name: 'reverseproxy'}],
          ['wordpress', {role: 'provider', name: 'website'}]
        ]
      });
      var context = unitDetails.getContext(db, service, unit);
      assert.strictEqual(context.relations.length, 1);
      var relation = context.relations[0];
      var expectedNear = {
        name: 'reverseproxy',
        role: 'requirer',
        service: 'haproxy'
      };
      var expectedFar = {
        name: 'website',
        role: 'provider',
        service: 'wordpress'
      };
      assert.deepEqual(relation.near, expectedNear);
      assert.deepEqual(relation.far, expectedFar);
    });

  });

})();
<|endoftext|>


<commit_before>var path = require('path');
var AssetsPlugin = require('assets-webpack-plugin')
var assetsPluginInstance = new AssetsPlugin({
  filename: 'static/prd/assets.js',
  processOutput: function (assets) {
    return 'window.WEBPACK_ASSETS = ' + JSON.stringify(assets);
  }
})
module.exports = {
  plugins: [{
    name: 'antd',
    options: {
      modifyQuery: function (defaultQuery) { // 可查看和编辑 defaultQuery
        defaultQuery.plugins.push('transform-decorators-legacy');
        return defaultQuery;
      }
    }
  }],
  // devtool:  'cheap-source-map',
  config: function (ykit) {
    return {
      exports: [
        './index.js'
      ],
      modifyWebpackConfig: function (baseConfig) {

        baseConfig.devtool = 'cheap-module-eval-source-map'
        baseConfig.context = path.resolve(__dirname, "client");

        baseConfig.output.prd.path = 'static/prd';
        baseConfig.output.prd.publicPath = '';
        baseConfig.output.prd.filename = '[name]@[chunkhash][ext]'
        baseConfig.plugins.push(assetsPluginInstance)

        baseConfig.module.loaders.push({
          test: /\.(sass|scss)$/,
          loader: ykit.ExtractTextPlugin.extract(
            require.resolve('css-loader')
            + '?sourceMap!'
            + require.resolve('fast-sass-loader') + '?sourceMap'
          )
        })
        baseConfig.module.preLoaders.push({
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            loader: "eslint-loader"
        });

        return baseConfig;
      }
    }
  },
  server: {
    // true/false，默认 false，效果相当于 ykit server --hot
    hot: true
    // true/false，默认 false，开启后可在当前打开的页面提示打包错误
    // overlay: true
  },
  hooks: {},
  commands: []
};
<commit_msg>fix: ykit  prd<commit_after>var path = require('path');
var AssetsPlugin = require('assets-webpack-plugin')
var assetsPluginInstance = new AssetsPlugin({
  filename: 'static/prd/assets.js',
  processOutput: function (assets) {
    return 'window.WEBPACK_ASSETS = ' + JSON.stringify(assets);
  }
})
module.exports = {
  plugins: [{
    name: 'antd',
    options: {
      modifyQuery: function (defaultQuery) { // 可查看和编辑 defaultQuery
        defaultQuery.plugins.push('transform-decorators-legacy');
        return defaultQuery;
      }
    }
  }],
  // devtool:  'cheap-source-map',
  config: function (ykit) {
    return {
      exports: [
        './index.js'
      ],
      modifyWebpackConfig: function (baseConfig) {

        baseConfig.devtool = 'cheap-module-eval-source-map'
        baseConfig.context = path.resolve(__dirname, "client");
        baseConfig.output.prd.path = 'static/prd';
        baseConfig.output.prd.publicPath = '';
        baseConfig.output.prd.filename = '[name]@[chunkhash][ext]'
        baseConfig.module.loaders.push({
          test: /\.(sass|scss)$/,
          loader: ykit.ExtractTextPlugin.extract(
            require.resolve('css-loader')
            + '?sourceMap!'
            + require.resolve('fast-sass-loader') + '?sourceMap'
          )
        })
        baseConfig.module.preLoaders.push({
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          loader: "eslint-loader"
        });

        if (this.env == 'prd') {
          baseConfig.plugins.push(assetsPluginInstance)
        }
        return baseConfig;
      }
    }
  },
  server: {
    // true/false，默认 false，效果相当于 ykit server --hot
    hot: true
    // true/false，默认 false，开启后可在当前打开的页面提示打包错误
    // overlay: true
  },
  hooks: {},
  commands: []
};
<|endoftext|>


<commit_before>/*
Zort plugin - Sort/filter tables
*/
(function ($) {
    $.fn.zort = function() {
        return this.each(function () {

            var backup = $(this).html()
            var total = $(this).find("tr").length - 1
            var table = $(this);
            table.css("position", "relative");

            function addFilterBox() {
                var search = $('
');
                var input = $('<input>');
                input.attr("placeholder", "Search table...");

                input.keyup(function(ev) {
                    filter(this.value);
                });

                search.append(input);
                table.after(search);
            }

            function setHandlers() {
                table.find("th").click(function(ev) {
                    doSorting(this, ev);
                }).contextmenu(function(ev) {
                    showMenu(this, ev);
                    return false;
                }).css("cursor", "pointer").addClass("zort");
            }

            function showMenu(th, ev) {
                $('.zort-menu').remove();

                // menu
                var menu = $('
');
                menu.css({
                    position: "absolute",
                    top: ev.clientY + "px",
                    left: ev.clientX + "px",
                    background: "#ccc",
                    border: "1px solid #000",
                    padding: "4px"
                });

                // content
                table.find("th").each(function(i, el) {
                    var val = $(el).hasClass("zort-toggled") ? "" : "checked";
                    var link = $('<input ' + val + ' id="' + i + '" type="checkbox">' + el.innerHTML + '
');
                    link.css("cursor", "pointer");
                    link.find('input').click(function(ev) {
                        toggle(this.id);
                    });
                    menu.append(link);
                });

                menu.append("
");
                var input = $('<input>');
                input.attr("placeholder", "Search column...");
                input.keyup(function(ev) {
                    filter(this.value, $(th).index());
                });
                menu.append(input);

                close = $('

<button>Close</button>');
                close.click(function() { $('.zort-menu').remove(); });
                menu.append(close);

                $("body").append(menu);
            }

            function toggle(col) {
                var $th = $(table.find("th")[col]);

                if ($th.hasClass("zort-toggled")) {
                    $th.removeClass("zort-toggled");
                } else {
                    $th.addClass("zort-toggled");
                }

                var trs = table.find("tr");

                trs.each(function(i, tr) {
                    var tds = $(tr).find("td, th");

                    tds.each(function(j, td) {
                        if (j == col) {
                            $(td).toggle();
                        }
                    });
                });
            }

            function doSorting(th, ev) {
                var $th = $(th);
                var index = $(th).index();
                $th.siblings().removeClass('asc desc');

                if ($th.hasClass("asc")) { //already sorted asc, sort desc
                    $th.removeClass("asc");
                    $th.addClass("desc");
                    sort(index, "desc");
                } else if ($th.hasClass("desc")) { //sorted desc, reset table
                    reset();
                } else { //not sorted, sort asc
                    $th.addClass("asc");
                    sort(index, "asc");
                }
            }

            function sort(col, order) {
                for (j = 0; j < total; ++j) {
                    for (i = 1; i < total - j; ++i) {
                        var row1 = $(table.find("tr")[i]);
                        var row2 = $(table.find("tr")[i + 1]);
                        var val1 = row1.find("td")[col].innerHTML;
                        var val2 = row2.find("td")[col].innerHTML;

                        if (val1 != "" && !isNaN(val1)) val1 = val1 * 1;
                        if (val2 != "" && !isNaN(val2)) val2 = val2 * 1;

                        if (order == "asc") {
                            if (val1 > val2) {
                                row1.before(row2);
                            }
                        } else if (order == "desc"){
                            if (val1 < val2) {
                                row1.before(row2);
                            }
                        }
                    }
                }
            }

            function filter(str, col) {
                str = str.toLowerCase();
                str = str.replace("<", "<").replace(">", ">");

                for (i = 1; i <= total; ++i) {
                    var row = $(table.find("tr")[i]);
                    var cells = row.find("td");
                    var visible = false;

                    for (j = 0; j < cells.length; ++j) {
                        if (col == undefined || col == j) {
                            var text = cells[j].innerHTML.toLowerCase();

                            if (text.indexOf(str) > -1) {
                                visible = true;
                            }
                        }
                    }

                    if (!visible) {
                        row.hide();
                    } else {
                        row.show();
                    }
                }
            }

            function reset() {
                table.html(backup);
                setHandlers();
            }

            // Init
            setHandlers();
            addFilterBox();
        });
    };
}(jQuery));
<commit_msg>Reseting sort copies the backup data only.<commit_after>/*
Zort plugin - Sort/filter tables
*/
(function ($) {
    $.fn.zort = function() {
        return this.each(function () {

            var backup = $(this).html()
            var total = $(this).find("tr").length - 1
            var table = $(this);
            table.css("position", "relative");

            function addFilterBox() {
                var search = $('
');
                var input = $('<input>');
                input.attr("placeholder", "Search table...");

                input.keyup(function(ev) {
                    filter(this.value);
                });

                search.append(input);
                table.after(search);
            }

            function setHandlers() {
                table.find("th").click(function(ev) {
                    doSorting(this, ev);
                }).contextmenu(function(ev) {
                    showMenu(this, ev);
                    return false;
                }).css("cursor", "pointer").addClass("zort");
            }

            function showMenu(th, ev) {
                $('.zort-menu').remove();

                // menu
                var menu = $('
');
                menu.css({
                    position: "absolute",
                    top: ev.clientY + "px",
                    left: ev.clientX + "px",
                    background: "#ccc",
                    border: "1px solid #000",
                    padding: "4px"
                });

                // content
                table.find("th").each(function(i, el) {
                    var val = $(el).hasClass("zort-toggled") ? "" : "checked";
                    var link = $('<input ' + val + ' id="' + i + '" type="checkbox">' + el.innerHTML + '
');
                    link.css("cursor", "pointer");
                    link.find('input').click(function(ev) {
                        toggle(this.id);
                    });
                    menu.append(link);
                });

                menu.append("
");
                var input = $('<input>');
                input.attr("placeholder", "Search column...");
                input.keyup(function(ev) {
                    filter(this.value, $(th).index());
                });
                menu.append(input);

                close = $('

<button>Close</button>');
                close.click(function() { $('.zort-menu').remove(); });
                menu.append(close);

                $("body").append(menu);
            }

            function toggle(col) {
                var $th = $(table.find("th")[col]);

                if ($th.hasClass("zort-toggled")) {
                    $th.removeClass("zort-toggled");
                } else {
                    $th.addClass("zort-toggled");
                }

                var trs = table.find("tr");

                trs.each(function(i, tr) {
                    var tds = $(tr).find("td, th");

                    tds.each(function(j, td) {
                        if (j == col) {
                            $(td).toggle();
                        }
                    });
                });
            }

            function doSorting(th, ev) {
                var $th = $(th);
                var index = $(th).index();
                $th.siblings().removeClass('asc desc');

                if ($th.hasClass("asc")) { //already sorted asc, sort desc
                    $th.removeClass("asc");
                    $th.addClass("desc");
                    sort(index, "desc");
                } else if ($th.hasClass("desc")) { //sorted desc, reset table
                    $th.removeClass("desc");
                    reset();
                } else { //not sorted, sort asc
                    $th.addClass("asc");
                    sort(index, "asc");
                }
            }

            function sort(col, order) {
                for (j = 0; j < total; ++j) {
                    for (i = 1; i < total - j; ++i) {
                        var row1 = $(table.find("tr")[i]);
                        var row2 = $(table.find("tr")[i + 1]);
                        var val1 = row1.find("td")[col].innerHTML;
                        var val2 = row2.find("td")[col].innerHTML;

                        if (val1 != "" && !isNaN(val1)) val1 = val1 * 1;
                        if (val2 != "" && !isNaN(val2)) val2 = val2 * 1;

                        if (order == "asc") {
                            if (val1 > val2) {
                                row1.before(row2);
                            }
                        } else if (order == "desc"){
                            if (val1 < val2) {
                                row1.before(row2);
                            }
                        }
                    }
                }
            }

            function filter(str, col) {
                str = str.toLowerCase();
                str = str.replace("<", "<").replace(">", ">");

                for (i = 1; i <= total; ++i) {
                    var row = $(table.find("tr")[i]);
                    var cells = row.find("td");
                    var visible = false;

                    for (j = 0; j < cells.length; ++j) {
                        if (col == undefined || col == j) {
                            var text = cells[j].innerHTML.toLowerCase();

                            if (text.indexOf(str) > -1) {
                                visible = true;
                            }
                        }
                    }

                    if (!visible) {
                        row.hide();
                    } else {
                        row.show();
                    }
                }
            }

            function reset() {
                var backTable = $('
');
                backTable.html(backup);
                var trs = backTable.find("tr");

                table.find("tr").each(function(i, el) {
                    var tr = $(trs[i]);

                    $(el).find("td, th").each(function(j, cell) {
                        var td = $(tr.find("td, th")[j]);
                        console.log(td.html(), $(cell).html());
                        $(cell).html(td.html());
                    });
                });
            }

            // Init
            setHandlers();
            addFilterBox();
        });
    };
}(jQuery));
<|endoftext|>


<commit_before>var program = require('commander');

program
	.version( '0.0.1' )
	.option( '--twitter-max-id', 'Maximum tweet id acceptable when retrieving data ' )
	.option( '--search [max_id]', 'Search for past tweets', false )
	.option( '--stream', 'Stream the latest tweets' )
	.option( '--field [key]', 'Field to be used during summarization' )
	.option( '--depth', 'Max tree depth', 9 )
	.option( '--old [tweet_field]', 'Old tweet key' )
	.option( '--new [tweet_field]', 'New tweet key' )
	.option( '--predicate [pred]', 'Boolean function that says whether a tweet should be discarde (false) or not (true) ', "return true;" )
	.option( '-m, --min [tweet_field]', 'Find the minimum value of a set' )
	.option( '-M, --max [tweet_field]', 'Find the maximum value of a set ' )
	.option( '-c, --config [type]', 'Config file' )
	.parse( process.argv );

params = {};
if( program.config ) {
	params = require( "../" + program.config );
}

if( program.min ) {
	params[ "min" ] = program.min;
}

if( program.max ) {
	params[ "max" ] = program.max;
}

if( program.search ) {
	params[ "search" ] = program.search;
}

if( program.stream ) {
	params[ "stream" ] = program.stream;
}

if( program.field ) {
	params[ "field" ] = program.field;
}

if( program.depth ) {
	params[ "depth" ] = program.depth;
}

if( program.predicate ) {
	params[ "predicate" ] = program.predicate;
}

if( program.old ) {
	params[ "old" ] = program.old;
}

if( program.new ) {
	params[ "new" ] = program.new;
}
<commit_msg>More args<commit_after>program = require('commander');

function parserList( list ) {
	return list.split(",");
}

program
	.version( '0.0.1' )
	.option( '--twitter-max-id', 'Maximum tweet id acceptable when retrieving data ' )
	.option( '--search [max_id]', 'Search for past tweets', false )
	.option( '--stream', 'Stream the latest tweets' )
	.option( '--field [key]', 'Field to be used during summarization' )
	.option( '--depth', 'Max tree depth', 9 )
	.option( '--old [tweet_field]', 'Old tweet key' )
	.option( '--new [tweet_field]', 'New tweet key' )
	.option( '--predicate [pred]', 'Boolean function that says whether a tweet should be discarde (false) or not (true) ', "return true;" )
	.option( '--keys <list>', "Comma separated list of keys to be used during summarization", parserList )
	.option( '-m, --min [tweet_field]', 'Find the minimum value of a set' )
	.option( '-M, --max [tweet_field]', 'Find the maximum value of a set ' )
	.option( '-c, --config [type]', 'Config file' )
	.parse( process.argv );

params = {};
if( program.config ) {
	params = require( "../" + program.config );
}

if( program.min ) {
	params[ "min" ] = program.min;
}

if( program.max ) {
	params[ "max" ] = program.max;
}

if( program.search ) {
	params[ "search" ] = program.search;
}

if( program.stream ) {
	params[ "stream" ] = program.stream;
}

if( program.field ) {
	params[ "field" ] = program.field;
}

if( program.depth ) {
	params[ "depth" ] = program.depth;
}

if( program.predicate ) {
	params[ "predicate" ] = program.predicate;
}

if( program.old ) {
	params[ "old" ] = program.old;
}

if( program.new ) {
	params[ "new" ] = program.new;
}

if( program.keys ) {
	params["keys"] = program.keys;
}
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    csv = require('csv-parser'),
    levelup = require('levelup'),
    key = require('./lib/key.js'),
    queue = require('queue-async');

var verbose = true;
if (require.main === module) {
    verbose = true;
    if (process.stdin.isTTY) {
        if (process.argv[2] === undefined) {
            return console.log('file argument required \n`node import-csv.js [source csv]`');
        }
        loadTask(process.argv[2]);
    }
}

module.exports = {
    loadTask: loadTask,
    deleteTask: deleteTask
}

function loadTask(fileLoc, callback) {
    var task = path.basename(fileLoc).split('.')[0],
        topq = queue();

    // ensure that we create & close the tracking database before loading anything
    topq.defer(function(tqcallback){
        levelup('./ldb/' + task + '-tracking.ldb', {}, function(err, trackingdb){
            if (err) return tqcallback(err);
            trackingdb.close(function(err){
                tqcallback(err);
            });
        });
    });

    topq.awaitAll(function(err, results){
        // @TODO: make fixedlist task-specific instead of global

        // perform some tests to see if there's a 'fixed' file for this task
        var fixedq = queue();
        fixedq
            .defer(function(fqcallback){
                fs.readdir('./fixed', function(err, files){
                    if (err) return fqcallback(err);
                    fqcallback(err, (files.indexOf(task) > -1));
                });
            })
            .defer(function(fqcallback){
                fs.stat('./fixed/' + task, function(err, info){
                    if (err) return fqcallback(err);
                    fqcallback(err, (info.size > 0));
                });
            });

        // once the tests come back, check if they're all positive. if so, load fixes & tasks.
        fixedq.awaitAll(function(err, results){
            if((!err) && results.every(function(x) { return x; })) {
                fs.readFile('./fixed/' + task, function(err, data){
                    var fixed_list = data.toString().split("\n");
                    _doImport(fileLoc, task, fixed_list, callback);
                });
            }
            else {
                _doImport(fileLoc, task, [], callback);
            }
        });
    });
}

function _doImport(fileLoc, task, fixed_list, callback) {
    if (verbose) console.log('importing task from ' + fileLoc);

    var count = 0;

    levelup('./ldb/' + task + '.ldb', function(err, db){

        if (err) throw callback(err);

        var q = queue();

        q.defer(function(qcallback){
            fs.createReadStream(fileLoc)
                .pipe(csv())
                .on('data', function(data) {
                    var object_hash = key.hashObject(data);
                    if (fixed_list.indexOf(object_hash) === -1) {
                        // item is not fixed
                        var object_id = key.compose(1, object_hash);
                        count++;
                        q.defer(function(qsubcallback){
                            db.put(object_id, JSON.stringify(data), function(err){
                                qsubcallback(err);
                            });
                        });
                    }
                })
                .on('end', function(err){
                    if(count === 0) {
                        var keyval = key.compose(1, 'random');
                        q.defer(db.put, keyval, JSON.stringify({ignore: true}));
                    }
                    qcallback(err);
                });
            });

        q.awaitAll(function(err, results){
            if (verbose) { console.log('done with ' + task + '. ' + count + ' items imported'); }
            db.close();
            if (callback) callback(null);
        });
    });
}

// must not contain subdirectories
function _deleteNonEmptyDirectory(path, callback) {
    path = path.replace(/\/$/, '');
    var q = queue();
    q.defer(function(defercallback) {
        fs.readdir(path, function(err, files){
            // no matching directory?
            if (err) return defercallback(err);

            files.forEach(function(f, i){
                q.defer(fs.unlink, (path + '/' + f));
                if(i === (files.length-1)) defercallback(null);
            })
        });
    });
    q.awaitAll(function(err, results) {
        if(err) callback(err);

        fs.rmdir(path, function(err) {
            callback(err, null);
        });
    });
}

function deleteTask(task, callback){
    var q = queue();
    if (verbose) { console.log('deleting task ' + task); }
    q.defer(_deleteNonEmptyDirectory, './ldb/' + task + '.ldb')
     .defer(_deleteNonEmptyDirectory, './ldb/' + task + '-tracking.ldb')
     .awaitAll(function(err, results) {
        // don't throw errors for missing directories
        if(err && (err.errno === 34) && (err.code === 'ENOENT')) err = null;

        callback(err, results);
     });
}

<commit_msg>unclear why you can't q.defer() db.put without a wrapper, but you can't<commit_after>var fs = require('fs'),
    path = require('path'),
    csv = require('csv-parser'),
    levelup = require('levelup'),
    key = require('./lib/key.js'),
    queue = require('queue-async'),
    rimraf = require('rimraf');

var verbose = false;
if (require.main === module) {
    verbose = true;
    if (process.stdin.isTTY) {
        if (process.argv[2] === undefined) {
            return console.log('file argument required \n`node import-csv.js [source csv]`');
        }
        loadTask(process.argv[2]);
    }
}

module.exports = {
    loadTask: loadTask,
    deleteTask: deleteTask
}

function loadTask(fileLoc, callback) {
    var task = path.basename(fileLoc).split('.')[0],
        topq = queue();

    // ensure that we create & close the tracking database before loading anything
    topq.defer(function(tqcallback){
        levelup('./ldb/' + task + '-tracking.ldb', {}, function(err, trackingdb){
            if (err) return tqcallback(err);
            trackingdb.close(function(err){
                tqcallback(err);
            });
        });
    });

    topq.awaitAll(function(err, results){
        // @TODO: make fixedlist task-specific instead of global

        // perform some tests to see if there's a 'fixed' file for this task
        var fixedq = queue();
        fixedq
            .defer(function(fqcallback){
                fs.readdir('./fixed', function(err, files){
                    if (err) return fqcallback(err);
                    fqcallback(err, (files.indexOf(task) > -1));
                });
            })
            .defer(function(fqcallback){
                fs.stat('./fixed/' + task, function(err, info){
                    if (err) return fqcallback(err);
                    fqcallback(err, (info.size > 0));
                });
            });

        // once the tests come back, check if they're all positive. if so, load fixes & tasks.
        fixedq.awaitAll(function(err, results){
            if((!err) && results.every(function(x) { return x; })) {
                fs.readFile('./fixed/' + task, function(err, data){
                    var fixed_list = data.toString().split("\n");
                    _doImport(fileLoc, task, fixed_list, callback);
                });
            }
            else {
                _doImport(fileLoc, task, [], callback);
            }
        });
    });
}

function _doImport(fileLoc, task, fixed_list, callback) {
    if (verbose) console.log('importing task from ' + fileLoc);

    var count = 0;

    levelup('./ldb/' + task + '.ldb', function(err, db){

        if (err) throw callback(err);

        var q = queue();

        q.defer(function(qcallback){
            fs.createReadStream(fileLoc)
                .pipe(csv())
                .on('data', function(data) {
                    var object_hash = key.hashObject(data);
                    if (fixed_list.indexOf(object_hash) === -1) {
                        // item is not fixed
                        var object_id = key.compose(1, object_hash);
                        count++;
                        q.defer(function(qsubcallback) {
                            db.put(object_id, JSON.stringify(data), function(err){
                                qsubcallback(err);
                            });
                        });
                    }
                })
                .on('end', function(err){
                    if(count === 0) {
                        var keyval = key.compose(1, 'random');
                        q.defer(function(qsubcallback) {
                            db.put(keyval, JSON.stringify({ignore: true}), function(err) {
                                qsubcallback(err);
                            });
                        });
                    }
                    qcallback(err);
                });
            });

        q.awaitAll(function(err, results){
            if (verbose) { console.log('done with ' + task + '. ' + count + ' items imported'); }
            db.close(function(err){
                console.log('closing database ' + task);
                if (callback) callback(err);
            });
        });
    });
}

function deleteTask(task, callback){
    var q = queue(1);
    if (verbose) { console.log('deleting task ' + task); }
    q.defer(rimraf, './ldb/' + task + '.ldb')
     .defer(rimraf, './ldb/' + task + '-tracking.ldb')
     .awaitAll(function(err, results) {
        callback(err, results);
     });
}

<|endoftext|>


<commit_before>'use strict';
const assert = require('assert');

let extractDomain;

if (process.env.NODE_ENV === 'travis') {
    extractDomain = require('./dist/extract-domain');
} else {
    extractDomain = require('./index');
}

const urls = [
    'https://www.npmjs.com/package/extract-domain',
    'http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
    'https://npmjs.com/package/extract-domain',
    'http://example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
    'http://www.so.many.sub.domains.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
    'http://user:password@example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
    'ftp://example.org/resource.txt',
    'http://www.npmjs.com',
    'http://www.npmjs.com?query=test',
    'http://www.npmjs.com#fragment',
    'this.is.my@email.com',
    'test@something.com',
];

const expected = [
    'npmjs.com',
    'example.com',
    'npmjs.com',
    'example.org',
    'email.com',
    'something.com',
];

describe('extract domain', () => {
    it('should return the domain if it already has been extracted', () => {
        assert.strictEqual(extractDomain('example.com'), 'example.com');
    });

    it('should extract given domain from url', () => {
        assert.strictEqual(extractDomain(urls[0]), expected[0]);
        assert.strictEqual(extractDomain(urls[1]), expected[1]);
        assert.strictEqual(extractDomain(urls[7]), expected[0]);
        assert.strictEqual(extractDomain(urls[8]), expected[0]);
        assert.strictEqual(extractDomain(urls[10]), expected[4]);
    });

    it('should extract given domain from an array of urls', () => {
        const domains = extractDomain(urls);

        domains.map((domain) => assert(expected.indexOf(domain) > -1));
    });

    it('should return empty string if it is not a url', () => {
        assert.strictEqual(extractDomain('/i.am/just.astring//7test'), '');
    });

    it('should throw syntax error exception if the argument is not string nor array', () => {
        try {
            extractDomain({});
        } catch (e) {
            assert.strictEqual(e.name, 'TypeError');

            assert.strictEqual(
                e.message,
                'The given URL is not a string. Please verify your string|array.'
            );
        }
    });

    it('should throw syntax error exception if the array value is not a string', () => {
        try {
            extractDomain([['wow']]);
        } catch (e) {
            assert.strictEqual(e.name, 'TypeError');

            assert.strictEqual(
                e.message,
                'The given URL is not a string. Please verify your string|array.'
            );
        }
    });

    it('should support tld if options flag is used', () => {
        assert.strictEqual(
            extractDomain(
                'http://www.so.many.sub.domains.example.co.uk:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: true }
            ),
            'example.co.uk'
        );

        assert.strictEqual(
            extractDomain(
                'http://user:password@www.so.many.sub.domains.example.co.uk:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: true }
            ),
            'example.co.uk'
        );

        assert.strictEqual(
            extractDomain(
                'http://user:password@www.so.many.sub.domains.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: true }
            ),
            'example.com'
        );

        assert.strictEqual(extractDomain('https://example.com', { tld: true }), 'example.com');
    });
});
<commit_msg>Add some more negative tests (#17)<commit_after>'use strict';
const assert = require('assert');

let extractDomain;

if (process.env.NODE_ENV === 'travis') {
    extractDomain = require('./dist/extract-domain');
} else {
    extractDomain = require('./index');
}

const urls = [
    'https://www.npmjs.com/package/extract-domain',
    'http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
    'https://npmjs.com/package/extract-domain',
    'http://example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
    'http://www.so.many.sub.domains.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
    'http://user:password@example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
    'ftp://example.org/resource.txt',
    'http://www.npmjs.com',
    'http://www.npmjs.com?query=test',
    'http://www.npmjs.com#fragment',
    'this.is.my@email.com',
    'test@something.com',
    'http://www.example.co.uk/',
];

const expected = [
    'npmjs.com',
    'example.com',
    'npmjs.com',
    'example.org',
    'email.com',
    'something.com',
    'co.uk',
];

describe('extract domain', () => {
    it('should return the domain if it already has been extracted', () => {
        assert.strictEqual(extractDomain('example.com'), 'example.com');
    });

    it('should extract given domain from url', () => {
        assert.strictEqual(extractDomain(urls[0]), expected[0]);
        assert.strictEqual(extractDomain(urls[1]), expected[1]);
        assert.strictEqual(extractDomain(urls[7]), expected[0]);
        assert.strictEqual(extractDomain(urls[8]), expected[0]);
        assert.strictEqual(extractDomain(urls[10]), expected[4]);
        assert.strictEqual(extractDomain(urls[12]), expected[6]);
    });

    it('should extract given domain from an array of urls', () => {
        const domains = extractDomain(urls);

        domains.map((domain) => assert(expected.indexOf(domain) > -1));
    });

    it('should return empty string if it is not a url', () => {
        assert.strictEqual(extractDomain('/i.am/just.astring//7test'), '');
    });

    it('should throw syntax error exception if the argument is not string nor array', () => {
        try {
            extractDomain({});
        } catch (e) {
            assert.strictEqual(e.name, 'TypeError');

            assert.strictEqual(
                e.message,
                'The given URL is not a string. Please verify your string|array.'
            );
        }
    });

    it('should throw syntax error exception if the array value is not a string', () => {
        try {
            extractDomain([['wow']]);
        } catch (e) {
            assert.strictEqual(e.name, 'TypeError');

            assert.strictEqual(
                e.message,
                'The given URL is not a string. Please verify your string|array.'
            );
        }
    });

    it('should support tld if options flag is used', () => {
        assert.strictEqual(
            extractDomain(
                'http://www.so.many.sub.domains.example.co.uk:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: true }
            ),
            'example.co.uk'
        );

        assert.strictEqual(
            extractDomain(
                'http://user:password@www.so.many.sub.domains.example.co.uk:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: true }
            ),
            'example.co.uk'
        );

        assert.strictEqual(
            extractDomain(
                'http://user:password@www.so.many.sub.domains.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: true }
            ),
            'example.com'
        );

        assert.strictEqual(extractDomain('https://example.com', { tld: true }), 'example.com');
    });

    it('should not support tld if options flag is used with false value', () => {
        assert.strictEqual(
            extractDomain(
                'http://www.so.many.sub.domains.example.co.uk:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: false }
            ),
            'co.uk'
        );

        assert.strictEqual(
            extractDomain(
                'http://user:password@www.so.many.sub.domains.example.co.uk:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: false }
            ),
            'co.uk'
        );

        assert.strictEqual(
            extractDomain(
                'http://user:password@www.so.many.sub.domains.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument',
                { tld: false }
            ),
            'example.com'
        );

        assert.strictEqual(extractDomain('https://example.com', { tld: false }), 'example.com');
    });
});
<|endoftext|>


<commit_before>/**
 * 视图逻辑
 */
define(function(require, exports) {
	'require:nomunge,exports:nomunge,module:nomunge';

	// 业务逻辑
	var biz = require('./biz.js'),

	// 工具集
	util = require('../util/util.js'),

	// 弹出消息
	tip = require('../util/tip.js'),

	// 编辑层
	editor = require('../util/editor.js'),

	// 渲染器
	Render = require('../model/render.js'),

	// 组结点渲染器
	groupRender = new Render('groupTemp'),

	// 行结点渲染器
	lineRender = new Render('lineTemp'),

	// 头渲染器
	headRender = new Render('headTemp'),

	// 标记(setter/getter)
	mark = function(key, value) {
		if (typeof value == 'undefined') {
			return biz.getData(key);
		} else {
			return biz.putData(key, value);
		}
	},

	/**
	 * 保存文件
	 */
	saveData = function(file) {
		try {
			biz.saveData(file);
			file && setTimeout(function() {
				tip.showInfoTip(util.i18n('saveSuccess'));
			}, 0);
			return true;
		} catch (e) {
			tip.showErrorTip(util.i18n('cantWriteFile'));
			return false;
		}
	},

	/**
	 * 筛选当前标签页的绑定
	 */
	filterCurrentTab = function() {
		util.getCurrentTab(function(tab) {
			var hostname = util.findHostname(tab.url);
			if (hostname) {
				var data = biz.loadData(),
				i, sum, toHide;
				for (i in data) {
					toHide = $();
					sum = data[i].traverse(function() {
						if (this.hostname != hostname) {
							toHide = toHide.add(this.target);
						}
					});
					if (sum == toHide.length) {
						data[i].target.closest('.block').addClass('hidden');
					} else {
						toHide.addClass('hidden');
					}
				}
				mark('currentTab', '1');
				data = null;
			}
		});
	},

	/**
	 * 筛选当前工程的组
	 */
	filterCurrentProject = function(project) {
		var data = biz.loadData(), i;
		for (var i in data) {
			if (i.indexOf('==@') != -1
					&& i.split('==@')[1].indexOf(project) != -1) {
				data[i].target.closest('.block').removeClass('hidden');
			} else {
				data[i].target.closest('.block').addClass('hidden');
			}
		}
		mark('currentProject', project);
	},

	/**
	 * 组筛选初始化
	 */
	groupFilterInit = function(projs) {
		var selected = mark('currentProject'),
		select, i;
		if (!$.isEmptyObject(projs)) {
			select = $('select');
			if (select.length == 0) {
				select = $('<select>');
				select.change(function() {
					mark('currentTab', '');
					if (this.selectedIndex) {
						filterCurrentProject(this.options[this.selectedIndex].text);
					} else {
						mark('currentProject', '');
						$('#content .hidden').removeClass('hidden');
					}
				});
				$('#groupFilter').html(select);
			}
			select.html('<option>' + util.i18n('allProjects') + '</option>');
			for (i in projs) {
				if (selected == i) {
					select.append('<option selected="selected">' + i + '</option>');
				} else {
					select.append('<option>' + i + '</option>');
				}
			}
		} else {
			$('#groupFilter').html(util.i18n('groupFilter'));
		}
	},

	/**
	 * 批量启用/禁用
	 */
	batchCheck = function(enables, disables) {
		if (saveData()) {
			enables && enables.trigger('checkon');
			disables && disables.trigger('checkoff');
		}
	};

	/**
	 * 切换启用/禁用按钮
	 */
	exports.check = function(target) {
		var node = target.closest('.node');
		if (node.hasClass('group')) {
			try {
				biz.checkGroup(node, batchCheck);
			} catch (e) {
				tip.showErrorTip(util.i18n('duplicates'));
			}
		} else {
			biz.checkLine(node, batchCheck);
		}
	};

	/**
	 * 添加组
	 */
	exports.addGroup = function(target, line) {
		var fields = biz.editFields({
			addr: '127.0.0.1',
			hostname: '',
			comment: ''
		});
		if (!line) { // 新组
			fields = biz.editFields({
				line: line || util.i18n('newGroup')
			}).concat(fields);
		}
		editor.show(target.data('title'), fields, function(err, data) {
			if (err) { // 输入校验
				tip.showErrorTip(err);
			} else {
				if (line) { // 新项已有所属组
					data.line = line;
				}
				biz.addGroup(data);
				saveData();
				setTimeout(function() {
					exports.refresh(false);
				}, 0);
				editor.hide();
			}
		});
	};

	/**
	 * 添加行
	 */
	exports.addLine = function(target) {
		var node = target.closest('.node'),
		line = node.data('target').line;
		exports.addGroup(target, line);
	};

	/**
	 * 编辑按钮
	 */
	exports.edit = function(target) {
		var data = target.closest('.node').data('target'),
		fields = biz.editFields(data);
		editor.show(target.data('title'), fields, function(err, nData) {
			if (err) { // 输入校验
				tip.showErrorTip(err);
			} else {
				var render = data.addr ? lineRender : groupRender,
				node = null;
				for (var i in nData) { // 新值覆盖
					data[i] = nData[i];
				}
				node = $(render.render(data));
				data.target.replaceWith(node);
				data.setTarget(node);
				saveData() && editor.hide();
			}
		});
	};

	/**
	 * 删除按钮
	 */
	exports.remove = function(target) {
		tip.showInfoTip(target.data('title') + '?', function() {
			var data = target.closest('.node').data('target');
			if (!data.addr) {
				delete biz.loadData()[data.line];
			}
			data.delink();
			saveData();
			target.trigger('remove');
		});
	};

	/**
	 * 展开/收缩按钮
	 */
	exports.expand = function(target) {
		if (!target.hasClass('lock')) {
			target.addClass('lock');
			var group = target.closest('.group'),
			collapse = target.hasClass('collapse');
			group.data('target').hide = collapse;
			if (saveData()) {
				if (collapse) { // 收缩已经展开的
					group.next().slideUp(function() {
						target.removeClass('collapse lock').addClass('expand');
					});
				} else { // 展开已经收缩的
					group.next().slideDown(function() {
						target.removeClass('expand lock').addClass('collapse');
					});
				}
			}
		}
	};

	/**
	 * 链接按钮
	 */
	exports.link = function() {
		var val = $('#hostsPath').val();
		if (!val) {
			tip.showErrorTip(util.i18n('blankPath'));
		} else if (!util.fileExists(val)) {
			tip.showErrorTip(util.i18n('fileNotExist'));
		} else if (util.isDirectory(val)) {
			tip.showErrorTip(util.i18n('noDirectory'));
		} else {
			chrome.tabs.create({
				url : 'file:///' + val.replace(/\\/g, '/')
			});
		}
	};

	/**
	 * 渲染头
	 */
	exports.renderHead = function() {
		$('#content').before(headRender.render({
			version: biz.getVersion(),
			hostsPath: biz.getHostsPath()
		}));
	};

	/**
     * 搜索
     */
    exports.searchHandle = function () {
        var val = $('#search-input').val().trim();
        if (val) {
            var data = biz.loadData();
            alert(data);
        }
    };

	/**
	 * 刷新数据
	 */
	exports.refresh = function(refresh) {
		var val = $('#hostsPath').val();
		if (!val) {
			tip.showErrorTip(util.i18n('blankPath'));
		} else if (!util.fileExists(val)) {
			tip.showErrorTip(util.i18n('fileNotExist'));
		} else if (util.isDirectory(val)) {
			tip.showErrorTip(util.i18n('noDirectory'));
		} else {
			try {
				biz.setHostsPath(val);
				var data = biz.loadData(true),
				content = $('#content').html(''),
				blocks = $('

'),
				projs = {},
				block, lines, i;
				if (data.error) {
					tip.showErrorTip(util.i18n(data.error));
					delete data.error;
				}
				for (i in data) {
					block = $('

').appendTo(blocks);
					data[i].setTarget($(groupRender.render(data[i])).appendTo(block));
					lines = $('
').appendTo(block);
					data[i].hide && lines.hide();
					data[i].traverse(function() {
						this.setTarget($(lineRender.render(this)).appendTo(lines));
					});
					if (i.indexOf('==@') != -1) {
						projs[$.trim(i.split('==@')[1])] = true;
					}
				}
				content.append(blocks);
				groupFilterInit(projs);
				if (mark('currentTab')) {
					filterCurrentTab();
				} else if (mark('currentProject')) {
					filterCurrentProject(mark('currentProject'));
				}
				blocks.css('display', 'block');
				if (refresh !== false) {
					tip.showInfoTip(util.i18n('loadSuccess'));
				}
			} catch (e) {
				tip.showErrorTip(util.i18n('cantReadFile'));
			}
		}
	};

	/**
	 * 备份数据
	 */
	exports.backup = function(target) {
		if (biz.canWrite()) {
			editor.show(util.i18n('copyMe'), [{
				label: ' ',
				name: 'copyMe',
				type: 'textarea',
				value: biz.loadContent()
			}], function() {
				editor.hide();
			});
			return;
		}
		editor.show(util.i18n('backupPath'), [ {
			label: '{{:backupPath}}',
			name: 'path',
			value: $('#hostsPath').val()
		} ], function(err, data) {
			if (!data.path) {
				tip.showErrorTip(util.i18n('blankPath'));
			} else if (util.fileExists(data.path)) {
				tip.showInfoTip(util.i18n('overwrite'), function() {
					saveData(data.path);
					editor.hide();
				});
			} else if (util.isDirectory(data.path)) {
				tip.showErrorTip(util.i18n('noDirectory'));
			} else {
				saveData(data.path) && editor.hide();
			}
		});
	};

	/**
	 * 滚动阴影效果
	 */
	exports.scroll = function(target) {
		clearTimeout(target.data('timeout'));
		target.data('timeout', setTimeout(function() {
			target.removeClass('scroll-top scroll-bottom');
		}, 1000));
		if (target.scrollTop() < target.data('scroll')) { // top
			target.addClass('scroll-bottom').removeClass('scroll-top');
		} else if (target.scrollTop() > target.data('scroll')) { // bottom
			target.addClass('scroll-top').removeClass('scroll-bottom');
		}
		target.data('scroll', target.scrollTop());
	};

	/**
	 * 显示当前路径的绑定
	 */
	exports.current = function() {
		if (mark('currentTab')) {
			$('#content .hidden').removeClass('hidden');
			mark('currentTab', '');
		} else {
			mark('currentProject', '');
			$('select').each(function(i, n) {
				n.selectedIndex = 0;
			});
			filterCurrentTab();
		}
	};

	/**
	 * 编辑取消
	 */
	exports.olCancel = function() {
		editor.hide();
		exports.close();
	};

	/**
	 * 显示当前tab的IP
	 */
	exports.showCurrentIP = function() {
		util.getCurrentTab(function(tab) {
			var ip = biz.getIP(tab.id);
			ip && $('#currentIP').html(util.i18n('currentTabIP') + ip);
		});
	};

	/**
	 * 导入多行hosts数据
	 */
	exports.imports = function(target) {
		var node = target.closest('.node');
		editor.show(target.data('title'), [{
			label: target.data('title'),
			name: 'imports',
			type: 'textarea'
		}], function(err, data) {
			var fragment = $.trim(data.imports) || '';
			if (fragment) {
				if (node.hasClass('group')) {
					biz.parseData(fragment, null, node.data('target').line);
				} else {
					biz.parseData(fragment);
				}
				saveData();
				exports.refresh(false);
			}
			editor.hide();
		});
	};

	/**
	 * 编辑保存
	 */
	exports.olSave = editor.save;

	/**
	 * 按钮文字提示
	 */
	exports.showTip = tip.showTip;

	/**
	 * 确认提示
	 */
	exports.confirm = tip.confirm;

	/**
	 * 关闭提示
	 */
	exports.close = tip.close;

});
<commit_msg>complete search feature<commit_after>/**
 * 视图逻辑
 */
define(function(require, exports) {
	'require:nomunge,exports:nomunge,module:nomunge';

	// 业务逻辑
	var biz = require('./biz.js'),

	// 工具集
	util = require('../util/util.js'),

	// 弹出消息
	tip = require('../util/tip.js'),

	// 编辑层
	editor = require('../util/editor.js'),

	// 渲染器
	Render = require('../model/render.js'),

	// 组结点渲染器
	groupRender = new Render('groupTemp'),

	// 行结点渲染器
	lineRender = new Render('lineTemp'),

	// 头渲染器
	headRender = new Render('headTemp'),

	// 标记(setter/getter)
	mark = function(key, value) {
		if (typeof value == 'undefined') {
			return biz.getData(key);
		} else {
			return biz.putData(key, value);
		}
	},

	/**
	 * 保存文件
	 */
	saveData = function(file) {
		try {
			biz.saveData(file);
			file && setTimeout(function() {
				tip.showInfoTip(util.i18n('saveSuccess'));
			}, 0);
			return true;
		} catch (e) {
			tip.showErrorTip(util.i18n('cantWriteFile'));
			return false;
		}
	},

	/**
	 * 筛选当前标签页的绑定
	 */
	filterCurrentTab = function() {
		util.getCurrentTab(function(tab) {
			var hostname = util.findHostname(tab.url);
			if (hostname) {
				var data = biz.loadData(),
				i, sum, toHide;
				for (i in data) {
					toHide = $();
					sum = data[i].traverse(function() {
						if (this.hostname != hostname) {
							toHide = toHide.add(this.target);
						}
					});
					if (sum == toHide.length) {
						data[i].target.closest('.block').addClass('hidden');
					} else {
						toHide.addClass('hidden');
					}
				}
				mark('currentTab', '1');
				data = null;
			}
		});
	},

	/**
	 * 筛选当前工程的组
	 */
	filterCurrentProject = function(project) {
		var data = biz.loadData(), i;
		for (var i in data) {
			if (i.indexOf('==@') != -1
					&& i.split('==@')[1].indexOf(project) != -1) {
				data[i].target.closest('.block').removeClass('hidden');
			} else {
				data[i].target.closest('.block').addClass('hidden');
			}
		}
		mark('currentProject', project);
	},

	/**
	 * 组筛选初始化
	 */
	groupFilterInit = function(projs) {
		var selected = mark('currentProject'),
		select, i;
		if (!$.isEmptyObject(projs)) {
			select = $('select');
			if (select.length == 0) {
				select = $('<select>');
				select.change(function() {
					mark('currentTab', '');
					if (this.selectedIndex) {
						filterCurrentProject(this.options[this.selectedIndex].text);
					} else {
						mark('currentProject', '');
						$('#content .hidden').removeClass('hidden');
					}
				});
				$('#groupFilter').html(select);
			}
			select.html('<option>' + util.i18n('allProjects') + '</option>');
			for (i in projs) {
				if (selected == i) {
					select.append('<option selected="selected">' + i + '</option>');
				} else {
					select.append('<option>' + i + '</option>');
				}
			}
		} else {
			$('#groupFilter').html(util.i18n('groupFilter'));
		}
	},

	/**
	 * 批量启用/禁用
	 */
	batchCheck = function(enables, disables) {
		if (saveData()) {
			enables && enables.trigger('checkon');
			disables && disables.trigger('checkoff');
		}
	};

	/**
	 * 切换启用/禁用按钮
	 */
	exports.check = function(target) {
		var node = target.closest('.node');
		if (node.hasClass('group')) {
			try {
				biz.checkGroup(node, batchCheck);
			} catch (e) {
				tip.showErrorTip(util.i18n('duplicates'));
			}
		} else {
			biz.checkLine(node, batchCheck);
		}
	};

	/**
	 * 添加组
	 */
	exports.addGroup = function(target, line) {
		var fields = biz.editFields({
			addr: '127.0.0.1',
			hostname: '',
			comment: ''
		});
		if (!line) { // 新组
			fields = biz.editFields({
				line: line || util.i18n('newGroup')
			}).concat(fields);
		}
		editor.show(target.data('title'), fields, function(err, data) {
			if (err) { // 输入校验
				tip.showErrorTip(err);
			} else {
				if (line) { // 新项已有所属组
					data.line = line;
				}
				biz.addGroup(data);
				saveData();
				setTimeout(function() {
					exports.refresh(false);
				}, 0);
				editor.hide();
			}
		});
	};

	/**
	 * 添加行
	 */
	exports.addLine = function(target) {
		var node = target.closest('.node'),
		line = node.data('target').line;
		exports.addGroup(target, line);
	};

	/**
	 * 编辑按钮
	 */
	exports.edit = function(target) {
		var data = target.closest('.node').data('target'),
		fields = biz.editFields(data);
		editor.show(target.data('title'), fields, function(err, nData) {
			if (err) { // 输入校验
				tip.showErrorTip(err);
			} else {
				var render = data.addr ? lineRender : groupRender,
				node = null;
				for (var i in nData) { // 新值覆盖
					data[i] = nData[i];
				}
				node = $(render.render(data));
				data.target.replaceWith(node);
				data.setTarget(node);
				saveData() && editor.hide();
			}
		});
	};

	/**
	 * 删除按钮
	 */
	exports.remove = function(target) {
		tip.showInfoTip(target.data('title') + '?', function() {
			var data = target.closest('.node').data('target');
			if (!data.addr) {
				delete biz.loadData()[data.line];
			}
			data.delink();
			saveData();
			target.trigger('remove');
		});
	};

	/**
	 * 展开/收缩按钮
	 */
	exports.expand = function(target) {
		if (!target.hasClass('lock')) {
			target.addClass('lock');
			var group = target.closest('.group'),
			collapse = target.hasClass('collapse');
			group.data('target').hide = collapse;
			if (saveData()) {
				if (collapse) { // 收缩已经展开的
					group.next().slideUp(function() {
						target.removeClass('collapse lock').addClass('expand');
					});
				} else { // 展开已经收缩的
					group.next().slideDown(function() {
						target.removeClass('expand lock').addClass('collapse');
					});
				}
			}
		}
	};

	/**
	 * 链接按钮
	 */
	exports.link = function() {
		var val = $('#hostsPath').val();
		if (!val) {
			tip.showErrorTip(util.i18n('blankPath'));
		} else if (!util.fileExists(val)) {
			tip.showErrorTip(util.i18n('fileNotExist'));
		} else if (util.isDirectory(val)) {
			tip.showErrorTip(util.i18n('noDirectory'));
		} else {
			chrome.tabs.create({
				url : 'file:///' + val.replace(/\\/g, '/')
			});
		}
	};

	/**
	 * 渲染头
	 */
	exports.renderHead = function() {
		$('#content').before(headRender.render({
			version: biz.getVersion(),
			hostsPath: biz.getHostsPath()
		}));
	};

	/**
     * 搜索
     */
    exports.searchHandle = function () {
        var val = $('#search-input').val().trim();
        var data = biz.loadData(), i, line , flag;
        for (i in data) {
            flag = false;
            line = data[i].next;
            while (line && line.target != data[i].target) {
                if (line.addr.indexOf(val) != -1 || line.hostname.indexOf(val) != -1) {
                    line.target.closest('.node').removeClass('hidden');
                    flag = true;
                } else {
                    line.target.closest('.node').addClass('hidden');
                }
                line = line.next;
            }
            if (flag) {
                data[i].target.closest('.block').removeClass('hidden');
            } else {
                data[i].target.closest('.block').addClass('hidden');
            }
        }
    };

	/**
	 * 刷新数据
	 */
	exports.refresh = function(refresh) {
		var val = $('#hostsPath').val();
		if (!val) {
			tip.showErrorTip(util.i18n('blankPath'));
		} else if (!util.fileExists(val)) {
			tip.showErrorTip(util.i18n('fileNotExist'));
		} else if (util.isDirectory(val)) {
			tip.showErrorTip(util.i18n('noDirectory'));
		} else {
			try {
				biz.setHostsPath(val);
				var data = biz.loadData(true),
				content = $('#content').html(''),
				blocks = $('

'),
				projs = {},
				block, lines, i;
				if (data.error) {
					tip.showErrorTip(util.i18n(data.error));
					delete data.error;
				}
				for (i in data) {
					block = $('

').appendTo(blocks);
					data[i].setTarget($(groupRender.render(data[i])).appendTo(block));
					lines = $('
').appendTo(block);
					data[i].hide && lines.hide();
					data[i].traverse(function() {
						this.setTarget($(lineRender.render(this)).appendTo(lines));
					});
					if (i.indexOf('==@') != -1) {
						projs[$.trim(i.split('==@')[1])] = true;
					}
				}
				content.append(blocks);
				groupFilterInit(projs);
				if (mark('currentTab')) {
					filterCurrentTab();
				} else if (mark('currentProject')) {
					filterCurrentProject(mark('currentProject'));
				}
				blocks.css('display', 'block');
				if (refresh !== false) {
					tip.showInfoTip(util.i18n('loadSuccess'));
				}
			} catch (e) {
				tip.showErrorTip(util.i18n('cantReadFile'));
			}
		}
	};

	/**
	 * 备份数据
	 */
	exports.backup = function(target) {
		if (biz.canWrite()) {
			editor.show(util.i18n('copyMe'), [{
				label: ' ',
				name: 'copyMe',
				type: 'textarea',
				value: biz.loadContent()
			}], function() {
				editor.hide();
			});
			return;
		}
		editor.show(util.i18n('backupPath'), [ {
			label: '{{:backupPath}}',
			name: 'path',
			value: $('#hostsPath').val()
		} ], function(err, data) {
			if (!data.path) {
				tip.showErrorTip(util.i18n('blankPath'));
			} else if (util.fileExists(data.path)) {
				tip.showInfoTip(util.i18n('overwrite'), function() {
					saveData(data.path);
					editor.hide();
				});
			} else if (util.isDirectory(data.path)) {
				tip.showErrorTip(util.i18n('noDirectory'));
			} else {
				saveData(data.path) && editor.hide();
			}
		});
	};

	/**
	 * 滚动阴影效果
	 */
	exports.scroll = function(target) {
		clearTimeout(target.data('timeout'));
		target.data('timeout', setTimeout(function() {
			target.removeClass('scroll-top scroll-bottom');
		}, 1000));
		if (target.scrollTop() < target.data('scroll')) { // top
			target.addClass('scroll-bottom').removeClass('scroll-top');
		} else if (target.scrollTop() > target.data('scroll')) { // bottom
			target.addClass('scroll-top').removeClass('scroll-bottom');
		}
		target.data('scroll', target.scrollTop());
	};

	/**
	 * 显示当前路径的绑定
	 */
	exports.current = function() {
		if (mark('currentTab')) {
			$('#content .hidden').removeClass('hidden');
			mark('currentTab', '');
		} else {
			mark('currentProject', '');
			$('select').each(function(i, n) {
				n.selectedIndex = 0;
			});
			filterCurrentTab();
		}
	};

	/**
	 * 编辑取消
	 */
	exports.olCancel = function() {
		editor.hide();
		exports.close();
	};

	/**
	 * 显示当前tab的IP
	 */
	exports.showCurrentIP = function() {
		util.getCurrentTab(function(tab) {
			var ip = biz.getIP(tab.id);
			ip && $('#currentIP').html(util.i18n('currentTabIP') + ip);
		});
	};

	/**
	 * 导入多行hosts数据
	 */
	exports.imports = function(target) {
		var node = target.closest('.node');
		editor.show(target.data('title'), [{
			label: target.data('title'),
			name: 'imports',
			type: 'textarea'
		}], function(err, data) {
			var fragment = $.trim(data.imports) || '';
			if (fragment) {
				if (node.hasClass('group')) {
					biz.parseData(fragment, null, node.data('target').line);
				} else {
					biz.parseData(fragment);
				}
				saveData();
				exports.refresh(false);
			}
			editor.hide();
		});
	};

	/**
	 * 编辑保存
	 */
	exports.olSave = editor.save;

	/**
	 * 按钮文字提示
	 */
	exports.showTip = tip.showTip;

	/**
	 * 确认提示
	 */
	exports.confirm = tip.confirm;

	/**
	 * 关闭提示
	 */
	exports.close = tip.close;

});
<|endoftext|>