<commit_before>"use strict";

var _ = require("underscore");
var util = require("substance-util");
var Document = require("substance-document");

// Substance.Article
// -----------------

var Article = function(options) {
  options = options || {};

  // Extend Schema
  // --------

  options.schema = util.deepclone(Document.schema);

  // Merge in custom types
  _.each(Article.types, function(type, key) {
    options.schema.types[key] = type;
  });

  // Register annotation types
  _.each(Article.annotations, function(aType, key) {
    options.schema.types[key] = aType;
  });

  // Merge in node types
  _.each(Article.nodeTypes, function(node, key) {
    options.schema.types[key] = node.type;
  });

  // Merge in custom indexes
  _.each(Article.indexes, function(index, key) {
    options.schema.indexes[key] = index;
  });

  // Call parent constructor
  // --------

  Document.call(this, options);

  this.nodeTypes = Article.nodeTypes;

  // Seed the doc
  // --------

  // Create the document node

  this.create({
    id: "document",
    type: "document",
    guid: options.id, // external global document id
    creator: options.creator,
    created_at: options.created_at,
    views: ["content"], // is views really needed on the instance level
    title: "",
    abstract: ""
  });

  // Create views on the doc
  _.each(Article.views, function(view) {
    this.create({
      id: view,
      "type": "view",
      nodes: []
    });
  }, this);

};

Article.Prototype = function() {

  this.fromSnapshot = function(data, options) {
    return Article.fromSnapshot(data, options);
  };

};

// Factory method
// --------
//
// TODO: Ensure the snapshot doesn't get chronicled

Article.fromSnapshot = function(data, options) {
  options = options || {};
  // options.seed = [];
  var doc = new Article(options);

  _.each(data.nodes, function(n) {
    if (doc.get(n.id)) {
      doc.delete(n.id); // skip existing nodes
    }
    doc.create(n);
  });

  return doc;
};

// Define available views
// --------

Article.views = ["content"];

// Register node types
// --------

Article.nodeTypes = require("./nodes");

// Define annotation types
// --------

Article.annotations = {

  "strong": {
    "parent": "annotation",
    "properties": {
    }
  },

  "emphasis": {
    "properties": {
    },
    "parent": "annotation"
  },

  "code": {
    "parent": "annotation",
    "properties": {
    }
  },

  "link": {
    "parent": "annotation",
    "properties": {
      "url": "string"
    }
  },

  "idea": {
    "parent": "annotation",
    "properties": {
    }
  },

  "error": {
    "parent": "annotation",
    "properties": {
    }
  },

  "question": {
    "parent": "annotation",
    "properties": {
    }
  },

  // Greenish figure references in the text

  "figure_reference": {
    "parent": "annotation",
    "properties": {
      "target": "figure"
    }
  },

  // Blueish citation references in the text

  "citation_reference": {
    "parent": "annotation",
    "properties": {
      "target": "content"
    }
  },

  "cross_reference": {
    "parent": "annotation",
    "properties": {
      "target": "content"
    }
  }

};

// Custom type definitions
// --------
//
// Holds comments

Article.types = {

  // Abstarct Annotation Node
  // --------

  "annotation": {
    "properties": {
      "path": ["array", "string"], // -> e.g. ["text_1", "content"]
      "range": "object"
    }
  },

  // Abstract Figure Type
  // --------

  "figure": {
    "properties": {
    }
  },

  // Abstract Citation Type
  // --------

  "citation": {
    "properties": {
    }
  },

  // Document
  // --------

  "document": {
    "properties": {
      "views": ["array", "view"],
      "guid": "string",
      "creator": "string",
      "title": "string",
      "abstract": "string"
    }
  },

  // Comments
  // --------

  "comment": {
    "properties": {
      "content": "string",
      "created_at": "string", // should be date
      "creator": "string", // should be date
      "node": "node" // references either a content node or annotation
    }
  }
};

// Custom indexes
// --------
//

Article.indexes = {
  // All annotations are now indexed by node
  // "annotations": {
  //   "type": "annotation",
  //   "properties": ["node"]
  // },
  // all comments are now indexed by node association
  "comments": {
    "type": "comment",
    "properties": ["node"]
  }
};

Article.Prototype.prototype = Document.prototype;
Article.prototype = new Article.Prototype();

// Add convenience accessors for builtin document attributes
Object.defineProperties(Document.prototype, {
  id: {
    get: function () {
      return this.get("document").guid;
    },
    set: function() {
      throw "doc.id is immutable";
    }
  },
  creator: {
    get: function () {
      return this.get("document").creator;
    }
  },
  created_at: {
    get: function () {
      return this.get("document").created_at;
    }
  },
  title: {
    get: function () {
      return this.get("document").title;
    }
  },
  abstract: {
    get: function () {
      return this.get("document").abstract;
    }
  },
  views: {
    get: function () {
      // Note: returing a copy to avoid inadvertent changes
      return this.get("document").views.slice(0);
    }
  },
});

module.exports = Article;
<commit_msg>Provide schema id and version.<commit_after>"use strict";

var _ = require("underscore");
var util = require("substance-util");
var Document = require("substance-document");

// Substance.Article
// -----------------

var Article = function(options) {
  options = options || {};

  // Extend Schema
  // --------

  options.schema = util.deepclone(Document.schema);

  options.schema.id = "lens-article";
  options.schema.version = "0.1.0";

  // Merge in custom types
  _.each(Article.types, function(type, key) {
    options.schema.types[key] = type;
  });

  // Register annotation types
  _.each(Article.annotations, function(aType, key) {
    options.schema.types[key] = aType;
  });

  // Merge in node types
  _.each(Article.nodeTypes, function(node, key) {
    options.schema.types[key] = node.type;
  });

  // Merge in custom indexes
  _.each(Article.indexes, function(index, key) {
    options.schema.indexes[key] = index;
  });

  // Call parent constructor
  // --------

  Document.call(this, options);

  this.nodeTypes = Article.nodeTypes;

  // Seed the doc
  // --------

  // Create the document node

  this.create({
    id: "document",
    type: "document",
    guid: options.id, // external global document id
    creator: options.creator,
    created_at: options.created_at,
    views: ["content"], // is views really needed on the instance level
    title: "",
    abstract: ""
  });

  // Create views on the doc
  _.each(Article.views, function(view) {
    this.create({
      id: view,
      "type": "view",
      nodes: []
    });
  }, this);

};

Article.Prototype = function() {

  this.fromSnapshot = function(data, options) {
    return Article.fromSnapshot(data, options);
  };

};

// Factory method
// --------
//
// TODO: Ensure the snapshot doesn't get chronicled

Article.fromSnapshot = function(data, options) {
  options = options || {};
  // options.seed = [];
  var doc = new Article(options);

  _.each(data.nodes, function(n) {
    if (doc.get(n.id)) {
      doc.delete(n.id); // skip existing nodes
    }
    doc.create(n);
  });

  return doc;
};

// Define available views
// --------

Article.views = ["content"];

// Register node types
// --------

Article.nodeTypes = require("./nodes");

// Define annotation types
// --------

Article.annotations = {

  "strong": {
    "parent": "annotation",
    "properties": {
    }
  },

  "emphasis": {
    "properties": {
    },
    "parent": "annotation"
  },

  "code": {
    "parent": "annotation",
    "properties": {
    }
  },

  "link": {
    "parent": "annotation",
    "properties": {
      "url": "string"
    }
  },

  "idea": {
    "parent": "annotation",
    "properties": {
    }
  },

  "error": {
    "parent": "annotation",
    "properties": {
    }
  },

  "question": {
    "parent": "annotation",
    "properties": {
    }
  },

  // Greenish figure references in the text

  "figure_reference": {
    "parent": "annotation",
    "properties": {
      "target": "figure"
    }
  },

  // Blueish citation references in the text

  "citation_reference": {
    "parent": "annotation",
    "properties": {
      "target": "content"
    }
  },

  "cross_reference": {
    "parent": "annotation",
    "properties": {
      "target": "content"
    }
  }

};

// Custom type definitions
// --------
//
// Holds comments

Article.types = {

  // Abstarct Annotation Node
  // --------

  "annotation": {
    "properties": {
      "path": ["array", "string"], // -> e.g. ["text_1", "content"]
      "range": "object"
    }
  },

  // Abstract Figure Type
  // --------

  "figure": {
    "properties": {
    }
  },

  // Abstract Citation Type
  // --------

  "citation": {
    "properties": {
    }
  },

  // Document
  // --------

  "document": {
    "properties": {
      "views": ["array", "view"],
      "guid": "string",
      "creator": "string",
      "title": "string",
      "abstract": "string"
    }
  },

  // Comments
  // --------

  "comment": {
    "properties": {
      "content": "string",
      "created_at": "string", // should be date
      "creator": "string", // should be date
      "node": "node" // references either a content node or annotation
    }
  }
};

// Custom indexes
// --------
//

Article.indexes = {
  // All annotations are now indexed by node
  // "annotations": {
  //   "type": "annotation",
  //   "properties": ["node"]
  // },
  // all comments are now indexed by node association
  "comments": {
    "type": "comment",
    "properties": ["node"]
  }
};

Article.Prototype.prototype = Document.prototype;
Article.prototype = new Article.Prototype();

// Add convenience accessors for builtin document attributes
Object.defineProperties(Document.prototype, {
  id: {
    get: function () {
      return this.get("document").guid;
    },
    set: function() {
      throw "doc.id is immutable";
    }
  },
  creator: {
    get: function () {
      return this.get("document").creator;
    }
  },
  created_at: {
    get: function () {
      return this.get("document").created_at;
    }
  },
  title: {
    get: function () {
      return this.get("document").title;
    }
  },
  abstract: {
    get: function () {
      return this.get("document").abstract;
    }
  },
  views: {
    get: function () {
      // Note: returing a copy to avoid inadvertent changes
      return this.get("document").views.slice(0);
    }
  },
});

module.exports = Article;
<|endoftext|>


<commit_before>
angular.module('BookmarksSrvc', [])
.factory('BookmarksService', ['AvatarService', 'StorageService',
    function(AvatarService, StorageService) {

    var data = null;
    var key = 'bookmarks';
    var adata = null;
    var akey = 'active';

    function readData() {
        console.log('BookmarksService - readData');
        if (!adata) {
            readActiveJournal();
        }
        if (!data) {
            readBookmarks();
        }
    };

    function readBookmarks() {
        console.log('BookmarksService - readBookmarks');
        data = StorageService.getCache(key);
        console.log(data);
        if (!data || !data.bookmarks || !data.bookmarks.length) {
            setDefaultBookmarks();
        }
        preProcessBookmarks();
    };

    function setDefaultBookmarks(){
        data = {'bookmarks':null};
        data.bookmarks = [
            {'username':'torontoru'},
            {'username':'toronto-ru'},
            {'username':'tema'},
            {'username':'russos'},
            {'username':'tanyant'},
            {'username':'leoz-net'}
        ];
        StorageService.setCache(key,data);
    };

    function preProcessBookmarks(){
        for (var i = 0; i < data.bookmarks.length; i++) {
            preProcessBookmark(i);
        }
    };

    function preProcessBookmark(i){
        //            TextService.convert(friends[i], 'fullname');
        AvatarService.getAvatar(data.bookmarks[i],data.bookmarks[i].username);
    };

    function hasJournals(){
        return (data.bookmarks.length > 0);
    };

    function hasJournal(journalName){
        for (var i = 0; i < data.bookmarks.length; i++) {
            if(data.bookmarks[i].username == journalName) {
                return true;
            }
        }
        return false;
    };

    function addJournal(journalName){
        var o = {'username':journalName};
        data.bookmarks.push(o);
        preProcessBookmark(data.bookmarks.length - 1);
        StorageService.setCache(key,data);
    };

    function deleteJournal(journal){
        data.bookmarks.splice(data.bookmarks.indexOf(journal), 1);
        StorageService.setCache(key,data);
    };

    function deleteJournalbyName(journalName){
        for (var i = 0; i < data.bookmarks.length; i++) {
            if(data.bookmarks[i].username == journalName) {
                data.bookmarks.splice(i, 1);
                break;
            }
        }
    };

    function getBookmarks(){
        return data.bookmarks;
    };

    /**/

    function readActiveJournal() {
        console.log('BookmarksService - readActiveJournal');
        adata = StorageService.getCache(akey);
        console.log(adata);
        if (!adata || !adata.active) {
            setDefaultActiveJournal();
        }
    };

    function setDefaultActiveJournal(){
        adata = {'active':null};
        setActiveJournal('torontoru');
    };

    function getActiveJournal(){
        return adata.active;
    };

    function setActiveJournal(journalName){
        adata.active = journalName;
        StorageService.setCache(akey,adata);
    };

    /**/

    return{
        default_avatar: AvatarService.defaultAvatar,
        read_data: readData,
        read_active_journal: readActiveJournal,
        has_journals: hasJournals,
        has_journal: hasJournal,
        add_journal: addJournal,
        delete_journal: deleteJournal,
        delete_journal_by_name: deleteJournalbyName,
        get_bookmarks: getBookmarks,
        get_active_journal: getActiveJournal,
        set_active_journal: setActiveJournal
    }
}]);
<commit_msg>Add English blogs<commit_after>
angular.module('BookmarksSrvc', [])
.factory('BookmarksService', ['AvatarService', 'StorageService',
    function(AvatarService, StorageService) {

    var data = null;
    var key = 'bookmarks';
    var adata = null;
    var akey = 'active';

    function readData() {
        console.log('BookmarksService - readData');
        if (!adata) {
            readActiveJournal();
        }
        if (!data) {
            readBookmarks();
        }
    };

    function readBookmarks() {
        console.log('BookmarksService - readBookmarks');
        data = StorageService.getCache(key);
        console.log(data);
        if (!data || !data.bookmarks || !data.bookmarks.length) {
            setDefaultBookmarks();
        }
        preProcessBookmarks();
    };

    // English language bookmarks

    var default_bookmarks_eng = [
        {'username':'greatpoets'},
        {'username':'ontd-science'},
        {'username':'doctorwho'},
        {'username':'toronto'},
        {'username':'ohnotheydidnt'},
        {'username':'linguaphiles'},
        {'username':'lolcats'}
    ];

    // Russian language bookmarks

    var default_bookmarks_cyr = [
        {'username':'torontoru'},
        {'username':'toronto-ru'},
        {'username':'tema'},
        {'username':'russos'},
        {'username':'tanyant'},
        {'username':'leoz-net'}
    ];

    function setDefaultBookmarks(){
        data = {'bookmarks':null};
        data.bookmarks = default_bookmarks_eng;
        StorageService.setCache(key,data);
    };

    function preProcessBookmarks(){
        for (var i = 0; i < data.bookmarks.length; i++) {
            preProcessBookmark(i);
        }
    };

    function preProcessBookmark(i){
        //            TextService.convert(friends[i], 'fullname');
        AvatarService.getAvatar(data.bookmarks[i],data.bookmarks[i].username);
    };

    function hasJournals(){
        return (data.bookmarks.length > 0);
    };

    function hasJournal(journalName){
        for (var i = 0; i < data.bookmarks.length; i++) {
            if(data.bookmarks[i].username == journalName) {
                return true;
            }
        }
        return false;
    };

    function addJournal(journalName){
        var o = {'username':journalName};
        data.bookmarks.push(o);
        preProcessBookmark(data.bookmarks.length - 1);
        StorageService.setCache(key,data);
    };

    function deleteJournal(journal){
        data.bookmarks.splice(data.bookmarks.indexOf(journal), 1);
        StorageService.setCache(key,data);
    };

    function deleteJournalbyName(journalName){
        for (var i = 0; i < data.bookmarks.length; i++) {
            if(data.bookmarks[i].username == journalName) {
                data.bookmarks.splice(i, 1);
                break;
            }
        }
    };

    function getBookmarks(){
        return data.bookmarks;
    };

    /**/

    function readActiveJournal() {
        console.log('BookmarksService - readActiveJournal');
        adata = StorageService.getCache(akey);
        console.log(adata);
        if (!adata || !adata.active) {
            setDefaultActiveJournal();
        }
    };

    function setDefaultActiveJournal(){
        adata = {'active':null};
        setActiveJournal('torontoru');
    };

    function getActiveJournal(){
        return adata.active;
    };

    function setActiveJournal(journalName){
        adata.active = journalName;
        StorageService.setCache(akey,adata);
    };

    /**/

    return{
        default_avatar: AvatarService.defaultAvatar,
        read_data: readData,
        read_active_journal: readActiveJournal,
        has_journals: hasJournals,
        has_journal: hasJournal,
        add_journal: addJournal,
        delete_journal: deleteJournal,
        delete_journal_by_name: deleteJournalbyName,
        get_bookmarks: getBookmarks,
        get_active_journal: getActiveJournal,
        set_active_journal: setActiveJournal
    }
}]);
<|endoftext|>


<commit_before>/**
 * Chapter11
 *
 * 예외와 에러처리
 */

(() => {

    /**
     * Error 객체
     *
     * 자바스크립트에는 내장된 Error 객체가 있고 이 객체는 에러 처리에 간편하게 사용할 수 있습니다.
     * Error 인스턴스를 만들면서 에러 메세지를 지정할 수 있습니다.
     */
    {
        const err = new Error('invalid email');

        // Error 인스턴스를 만드는 것만으로는 아무 일도 일어나지 않습니다. 이 인스턴스는 에러와 통신하는 수단입니다.
        // 이메일 주소의 유효성을 검사하는 함수가 있다고 합시다. 이 함수는 검사한 이메일 주소가 올바르면 이메일 주소를 문자열로 반환합니다.
        // 바르지 않다면 Error 인스턴스를 반환합니다. 단순함을 위해 앳(@)만 있으면 유효한 이메일 주소로 간주하기로 합시다.
        const validateEmail = email => email.match(/@/) ? email : new Error(`invalid email: ${email}`);

        // 이 함수를 사용할 때는 instanceof 연산자를 써서 Error 인스턴스가 반환됐는지 확인합니다.
        // 에러 메시지는 message 프로퍼티에 있습니다.
        const email = 'jane@doe.com';

        const validatedEmail = validateEmail(email);
        if (validatedEmail instanceof Error) {
            console.log(`Error: ${validatedEmail.message}`);
        } else {
            console.log(`Valid emil: ${validatedEmail}`);
        }

        // 이 방법도 Error 인스턴스를 활용하는 유효하고 유용한 방법이긴 하지만, Error 인스턴스는 예외 처리에서 더 자주 사용됩니다.
    }

    /**
     * try/catch와 예외처리
     */
    {

    }

})();<commit_msg>Update chapter11<commit_after>/**
 * Chapter11
 *
 * 예외와 에러처리
 */

(() => {

    /**
     * Error 객체
     *
     * 자바스크립트에는 내장된 Error 객체가 있고 이 객체는 에러 처리에 간편하게 사용할 수 있습니다.
     * Error 인스턴스를 만들면서 에러 메세지를 지정할 수 있습니다.
     */
    {
        const err = new Error('invalid email');

        // Error 인스턴스를 만드는 것만으로는 아무 일도 일어나지 않습니다. 이 인스턴스는 에러와 통신하는 수단입니다.
        // 이메일 주소의 유효성을 검사하는 함수가 있다고 합시다. 이 함수는 검사한 이메일 주소가 올바르면 이메일 주소를 문자열로 반환합니다.
        // 바르지 않다면 Error 인스턴스를 반환합니다. 단순함을 위해 앳(@)만 있으면 유효한 이메일 주소로 간주하기로 합시다.
        const validateEmail = email => email.match(/@/) ? email : new Error(`invalid email: ${email}`);

        // 이 함수를 사용할 때는 instanceof 연산자를 써서 Error 인스턴스가 반환됐는지 확인합니다.
        // 에러 메시지는 message 프로퍼티에 있습니다.
        const email = 'jane@doe.com';

        const validatedEmail = validateEmail(email);
        if (validatedEmail instanceof Error) {
            console.error(`Error: ${validatedEmail.message}`);
        } else {
            console.log(`Valid emil: ${validatedEmail}`);
        }

        // 이 방법도 Error 인스턴스를 활용하는 유효하고 유용한 방법이긴 하지만, Error 인스턴스는 예외 처리에서 더 자주 사용됩니다.
    }

    /**
     * try/catch와 예외처리
     *
     * 이전 예제의 validateEmail은 사용자가 이메일 주소에서 @을 빼먹은, 예상할 수 있는 에러는 처리할 수 있습니다. 하지만 예상치 못한
     * 에러가 일어날 가능성은 여전한데, 예를 들어 부주의한 프로그래머가 email에 문제열이 아닌 어떤 것을 할당한다고 합시다. 지금대로라면,
     * 이전 예제의 함수는 email에 null, 숫자, 객체 등 문자열이 아닌 무언가가 들어온다면 에러를 일으킵니다. 프로그램은 앞뒤 설명 없이 멈춰버리겠죠.
     * 이렇게 예상치 못한 에러에 대처하려면 try...catch 문으로 코드 전체를 감쌀 수 있습니다.
     */
    {
        const validateEmail = email => email.match(/@/) ? email : new Error(`invalid email: ${email}`);

        const email = null;

        try {
            const validatedEmail = validateEmail(email);
            if (validatedEmail instanceof Error) {
                console.error(`Error: ${validatedEmail.message}`);
            } else {
                console.log(`Valid email: ${validatedEmail}`);
            }
        } catch (err) {
            console.error(`Error: ${err.message}`);
        }
    }

    /**
     * 에러 일으키기
     *
     * 자바스크립트가 에러를 일으키기만 기다릴 필요 없이 직접 에러를 일으켜서(throw, raise) 예외 처리 작업을 시작할 수도 있습니다.
     *
     * 예외 처리 기능이 있는 다른 언어와는 달리, 자바스크립트는 에러를 일으킬 때 꼭 객체만이 아니라 숫자나 문자열 등 어떤 값이든 catch 절에 넘길 수
     * 있습니다. 하지만 Error 인스턴스를 넘기는 것이 가장 편리합니다.
     *
     * 은행 애플리케이션에 사용할 현금 인출 기능을 만든다고 생각해 봅시다. 계좌의 잔고가(balance)가 요청받은 금액보다 적다면 예외를 일으켜야 할 겁니다.
     * (현금 인출 단계에 들어가기 전에 잔고를 먼저 체크하는게 당연하므로, 잔고가 부족한 건 예외적인 상황입니다.)
     */
    {
        const billPay = (amount, payee, account) => {
            if (amount > account.balance)
                throw new Error('insufficient funds');
            account.transfer(payee, amount);
        };

        // throw를 호출하면 현재 함수는 즉시 실행을 멈춥니다. 따라서 위 예제에서는 account.transfer가 호출되지 않으므로 잔고가 부족한데도 현금을 찾아가는
        // 사고는 발생하지 않습니다.
    }

    /**
     * 예외 처리와 호출 스택
     *
     * 프로그램이 함수를 호출하고, 그 함수는 다른 함수를 호출하고, 호출된 함수는 또 다른 함수를 호출하는 일이 반복됩니다. 자바스크립트 인터프리터는 이런 과정을
     * 모두 추적하고 있어야 합니다. 함수 a가 함수 b를 호출하고 함수 b는 함수 c를 호출한다면, 함수 c가 실행을 마칠 때 실행 흐름은 함수 b로 돌아갑니다. 그리고
     * b가 실행을 마칠 때 실행 흐름은 함수 a로 돌아갑니다. 바꿔 말해, c가 실행 중일 때는 a와 b는 완료 될 수 없습니다. 이렇게 완료되지 않은 함수가 쌓이는 것을
     * 호출 스택(call stack)이라 부릅니다.
     *
     * 에러는 캐치될 때까지 호출 스택을 따라 올라갑니다.
     *
     * 에러는 호출 스택 어디에서든 캐치할 수 있습니다. 어디에서 이 에러를 캐치하지 않으면 자바스크립트 인터프리터는 프로그램을 멈춥니다. 이런것을 처리하지 않은
     * (unhandled) 예외, 캐치하지 않은(uncaught) 예외라고 부르며 프로그램이 충돌하는 원인이 됩니다. 에러가 일어날 수 있는 곳은 정말 다양하므로 가능한
     * 에러를 모두 캐치하기는 정말 어렵습니다.
     *
     * 에러를 캐치하면 호출 스택에서 문제 해결에 유용한 정보를 얻을 수 있습니다. 예를 들어 함수 c에서 에러가 일어났다면, 호출 스택은 c에서 일어난 에러를 보고하는
     * 데 그치지 않고 b가 c를 호출했으며 b는 a에서 호출했다는 것도 함께 알려줍니다. 프로그램 여기저기에서 함수 c를 호출할 수 있으므로 이런 정보는 디버그에 유용하게
     * 쓸 수 있습니다.
     *
     * 대부분의 자바스크립트 환경에서 Error 인스턴스에는 스택을 문자열로 표현한 stack 프로퍼티가 있습니다. 이 기능은 자바스크립트 표준은 아니지만 대부분의 환경에서
     * 지원합니다.
     */
    {
        const a = () => {
            console.log('a: calling b');
            b();
        };
        const b = () => {
            console.log('b: calling c');
            c();
            console.log('b: done');
        };
        const c = () => {
            console.log('c: throwing error');
            throw new Error('c error');
            console.log('c: done');
        };
        const d = () => {
            console.log('d: calling c');
            c();
            console.log('d: done');
        };

        try {
            a();
        } catch (err) {
            console.log(err.stack);
        }

        try {
            d();
        } catch (err) {
            console.log(err.stack);
        }
    }

    /**
     * try...catch...finally
     *
     * try 블록의 코드가 HTTP 연결이나 파일 같은 일종의 '자원'을 처리할 때가 있습니다. 프로그램에서 이 자원을 계속 가지고 있을 수는 없으므로 에러가 있든 없든
     * 어느 시점에서는 이 자원을 해제해야 합니다. try 블록에는 문을 원하는 만큼 쓸 수 있고, 그중 어디서든 에러가 일어나서 자원을 해제할 기회가 아예 사라질 수도
     * 있으므로 try 블록에서 자원을 해제하는 건 안전하지 않습니다. 이런 상황에서 finally 블록이 필요합니다. finally 블록은 에러가 일어나든, 일어나지 않든
     * 반드시 호출 됩니다.
     */
    {
        try {
            console.log('this line is executed...');
            throw new Error('whoops');
            console.log('this line is not...');
        } catch (err) {
            console.log('there was an error...');
        } finally {
            console.log('...always executed');
            console.log('perform cleanup here');
        }
    }

    /**
     * 요약
     *
     * 에외 처리에는 비용이 필요합니다. 예외를 캐치하지 못하는 위험도 있고 예외 처리 자체도 대가를 지불해야 하는 연산입니다. 예외는 catch 블록을 만날 때까지
     * 스택을 거슬러 올라가야 하므로 자바스크립트 인터프리터가 예외를 계속 추적하고 있어야 합니다. 이런 비용은 큰 문제가 되지 않을 수도 있지만, 자주 실행되는
     * 코드에서 예외를 발생시키면 성능 문제가 발생할 가능성이 있습니다.
     *
     * 프로그램을 일부러 멈추려 하는 게 아니라면, 예외를 일으켰으면 반드시 캐치해야합니다. 원인 없는 결과는 없는 법입니다. 예외 처리는 예상할 수 없는 상황에
     * 대바한 마지노선으로 생각하고, 예상할 수 있는 에러는 조건문으로 처리하는 것이 최선입니다.
     */

})();<|endoftext|>


<commit_before>const searchInput = document.querySelector('input[type="search"]');
const tabList = document.querySelector('.tab-list');

// Initialize tabs
{
  getAllTabs().then(injectTabsInList);
}
// Set a timeout on focus so input is focused on popup
setTimeout(() => searchInput.focus(), 100);
// Any keydown should activate the search field,
// TODO: on arrow keys ignore focus and switch tab index
window.addEventListener('keydown', handleKeyDown);
tabList.addEventListener('click', switchTabs);
searchInput.addEventListener('change', updateSearch);
searchInput.addEventListener('keyup', updateSearch);

function handleKeyDown(event) {
  switch (event.key) {
    case "ArrowRight":
    case "ArrowDown":
      document.activeElement.nextElementSibling.focus();
      // navigateResults(1);
      break;
    case "ArrowLeft":
    case "ArrowUp":
      document.activeElement.previousElementSibling.focus();
      break;
    case "Enter":
      if (document.activeElement.nodeName === "INPUT") {
        document.querySelector('.tab-link').focus();
      }
      switchTabs(document.activeElement.dataset.id)
      break;
    default:
      searchInput.focus();
      break;
  }
}

function navigateResults(number) {
  const active = document.activeElement;
  switch (activeElement) {
    case "INPUT":
      document.querySelector('.selected').focus();
      const newTab = document.activeElement.dataset.id;
      switchActiveTab(newTab);
      break;
    default:
      break;
  }
}

function getAllTabs() {
  return browser.tabs.query({ currentWindow: true });
}

function updateSearch(event) {
  const query = event.target.value.toLowerCase();
  const tabFilter = tab => tab.title.toLowerCase().includes(query);
  return getAllTabs()
    .then(tabArray => tabArray.filter(tabFilter))
    .then(injectTabsInList);
}

function injectTabsInList(tabArray) {
  tabList.innerHTML = tabArray.map(tabToTag).join('');
}

function tabToTag(tab, index) {
  const className = 'tab-link';
    // index === 0
    // ? 'tab-link selected'
    // : 'tab-link';
  return `

      ${tab.title}

  `;
}

function switchTabs(event) {
  if (event.target.className !== 'tab-link') return;
  event.preventDefault();
  const tabId = parseInt(event.target.dataset.id);
  switchActiveTab(tabId);
}

function switchActiveTab(id) {
  browser.tabs.update(id, { active: true });
  window.close();
}

// To switch tabs
// onClick -> tabs.update(tab.id, { active: true })
<commit_msg>Add tab switching<commit_after>const searchInput = document.querySelector('input[type="search"]');
const tabList = document.querySelector('.tab-list');

// Initialize tabs
{
  getAllTabs().then(injectTabsInList);
}
// Set a timeout on focus so input is focused on popup
setTimeout(() => searchInput.focus(), 100);
// Any keydown should activate the search field,
// TODO: on arrow keys ignore focus and switch tab index
window.addEventListener('keydown', handleKeyDown);
tabList.addEventListener('click', switchTabs);
searchInput.addEventListener('change', updateSearch);
searchInput.addEventListener('keyup', updateSearch);

function handleKeyDown(event) {
  switch (event.key) {
    case "Tab":
      event.preventDefault();
    case "ArrowRight":
    case "ArrowDown":
    case "ArrowLeft":
    case "ArrowUp":
      navigateResults(event.key);
      // document.activeElement.previousElementSibling.focus();
      break;
    case "Enter":
      if (document.activeElement.nodeName === "INPUT") {
        document.querySelector('.tab-link').focus();
      }
      switchTabs(document.activeElement.dataset.id)
      break;
    default:
      searchInput.focus();
      break;
  }
}

function navigateResults(direction) {
  if (document.activeElement.nodeName === 'INPUT') {
    document.querySelector('.tab-link').focus();
    return;
  }

  switch (direction) {
    case "Tab":
    case "ArrowRight":
    case "ArrowDown":
      document.activeElement.nextElementSibling.focus();
      break;
    case "ArrowLeft":
    case "ArrowUp":
      document.activeElement.previousElementSibling.focus();
      break;
  }
}

function getAllTabs() {
  return browser.tabs.query({ currentWindow: true });
}

function updateSearch(event) {
  const query = event.target.value.toLowerCase();
  const tabFilter = tab => tab.title.toLowerCase().includes(query);
  return getAllTabs()
    .then(tabArray => tabArray.filter(tabFilter))
    .then(injectTabsInList);
}

function injectTabsInList(tabArray) {
  tabList.innerHTML = tabArray.map(tabToTag).join('');
}

function tabToTag(tab, index) {
  const className = 'tab-link';
    // index === 0
    // ? 'tab-link selected'
    // : 'tab-link';
  return `

      ${tab.title}

  `;
}

function switchTabs(event) {
  if (event.target.className !== 'tab-link') return;
  event.preventDefault();
  const tabId = parseInt(event.target.dataset.id);
  switchActiveTab(tabId);
}

function switchActiveTab(id) {
  browser.tabs.update(id, { active: true });
  window.close();
}

// To switch tabs
// onClick -> tabs.update(tab.id, { active: true })
<|endoftext|>


<commit_before>var Ipc = require('ipc');

function _needCompile ( metaType ) {
    return metaType === 'javascript' ||
        metaType === 'coffeescript' ||
        metaType === 'typescript'
    ;
}

Ipc.on('asset-db:asset-changed', function (type, uuid) {
    // console.log(arguments);
    if ( _needCompile(type) ) {
        Editor.Compiler.compileLater();
    }
});

Ipc.on('asset-db:assets-moved', function ( results ) {
    var needRecompile = results.some(function ( result ) {
        var info = Editor.assetdb.assetInfo(result.uuid);
        return _needCompile(info['meta-type']);
    });

    if ( needRecompile ) {
        Editor.Compiler.compileLater();
    }
});

Ipc.on('asset-db:assets-created', function ( results ) {
    var needRecompile = results.some(function ( result ) {
        var info = Editor.assetdb.assetInfo(result.uuid);
        return _needCompile(info['meta-type']);
    });

    if ( needRecompile ) {
        Editor.Compiler.compileLater();
    }
});

Ipc.on('asset-db:assets-deleted', function ( results ) {
    var needRecompile = results.some(function ( result ) {
        var info = Editor.assetdb.assetInfoByPath(result.path);
        return _needCompile(info['meta-type']);
    });

    if ( needRecompile ) {
        Editor.Compiler.compileLater();
    }
});
<commit_msg>change assets-changed ipc parameter<commit_after>var Ipc = require('ipc');

function _needCompile ( metaType ) {
    return metaType === 'javascript' ||
        metaType === 'coffeescript' ||
        metaType === 'typescript'
    ;
}

Ipc.on('asset-db:asset-changed', function ( result ) {
    // console.log(arguments);
    if ( _needCompile( result.type ) ) {
        Editor.Compiler.compileLater();
    }
});

Ipc.on('asset-db:assets-moved', function ( results ) {
    var needRecompile = results.some(function ( result ) {
        var info = Editor.assetdb.assetInfo(result.uuid);
        return _needCompile(info['meta-type']);
    });

    if ( needRecompile ) {
        Editor.Compiler.compileLater();
    }
});

Ipc.on('asset-db:assets-created', function ( results ) {
    var needRecompile = results.some(function ( result ) {
        var info = Editor.assetdb.assetInfo(result.uuid);
        return _needCompile(info['meta-type']);
    });

    if ( needRecompile ) {
        Editor.Compiler.compileLater();
    }
});

Ipc.on('asset-db:assets-deleted', function ( results ) {
    var needRecompile = results.some(function ( result ) {
        var info = Editor.assetdb.assetInfoByPath(result.path);
        return _needCompile(info['meta-type']);
    });

    if ( needRecompile ) {
        Editor.Compiler.compileLater();
    }
});
<|endoftext|>


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

(function () {
    "use strict";

    plot.dataKeys = ['x', 'sigma_x', 'y', 'sigma_y', 'rho', 'Selected'];

    plot.propertiesKeys = [
        'Ellipse Fill Color',
        'Title',
        'Uncertainty',
        'X Axis',
        'Y Axis',
        'LAMBDA_235',
        'LAMBDA_238'];

    plot.draw = function (data) {
        initializeWetherill({
            LAMBDA_235: plot.getProperty("LAMBDA_235"),
            LAMBDA_238: plot.getProperty("LAMBDA_238")
        });

        var x = plot.x = d3.scale.linear()
                .range([0, plot.width]);

        var y = plot.y = d3.scale.linear()
                .range([plot.height, 0]);

        plot.t = d3.scale.linear();

        var xMin = 0;
        var xMax = 1;
        var yMin = 0;
        var yMax = 1;

        if (data.length > 0){
            var dataXMin = d3.min(data, function (d) {
                return d.x - d.sigma_x * plot.getProperty("Uncertainty");
            });

            var dataYMin = d3.min(data, function (d) {
                return d.y - d.sigma_y * plot.getProperty("Uncertainty");
            });

            var dataXMax = d3.max(data, function (d) {
                return d.x + d.sigma_x * plot.getProperty("Uncertainty");
            });

            var dataYMax = d3.max(data, function (d) {
                return d.y + d.sigma_y * plot.getProperty("Uncertainty");
            });

            var xRange = dataXMax - dataXMin;
            var yRange = dataYMax - dataYMin;

            xMin = dataXMin - 0.05 * xRange;
            yMin =  dataYMin - 0.05 * yRange;
            xMax = dataXMax + 0.05 * xRange;
            yMax = dataYMax + 0.05 * yRange;
        }

        x.domain([xMin, xMax]);
        y.domain([yMin, yMax]);

        // initialize the concordia envelope
        plot.area.clipped.append("path")
                .attr("class", "uncertaintyEnvelope")
                .attr("fill", "lightgray")
                .attr("stroke", "none")
                .attr("shape-rendering", "geometricPrecision");

        // initialize the concordia
        plot.area.clipped.append("path")
                .attr("class", "concordia")
                .attr("fill", "none")
                .attr("stroke", "blue")
                .attr("stroke-width", 2)
                .attr("shape-rendering", "geometricPrecision");

        plot.area.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + plot.height + ")")
                .append("text")
                .attr("class", "label")
                .attr("x", plot.width / 2)
                .attr("y", 35);

        plot.area.append("g")
                .attr("class", "y axis")
                .append("text")
                .attr("class", "label")
                .attr("transform", "rotate(-90)")
                .attr("x", -plot.height / 2)
                .attr("y", -50)
                .attr("dy", ".71em");

        plot.update(data);
    };

    plot.update = function (data) {
        var x = plot.x;
        var y = plot.y;

        d3.select(".x.axis .label").text(plot.getProperty("X Axis"));
        d3.select(".y.axis .label").text(plot.getProperty("Y Axis"));

        var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom");

        var yAxis = d3.svg.axis()
                .scale(y)
                .orient("left");

        var ellipses;
        (ellipses = plot.area.clipped.selectAll(".ellipse")
                .data(data))
                .enter().append("path")
                .attr("class", "ellipse")
                .attr("fill-opacity", 0.3)
                .attr("stroke", "black");

        ellipses.attr("fill", function(d) {
            var fill;

            if (!d['Selected']) {
                fill = 'gray';
            } else {
                fill = plot.getProperty("Ellipse Fill Color");
            }

            return fill;
        });

        var dots;
        (dots = plot.area.clipped.selectAll(".dot")
                .data(data))
                .enter().append("circle")
                .attr("class", "dot")
                .attr("r", 1.5);

        // utilities for generating path data elements
        var moveTo = function (path, p) {
            path.push("M", p[0], ",", p[1]);
        };

        var lineTo = function (path, p) {
            path.push("L", p[0], ",", p[1]);
        };

        var close = function (path) {
            path.push("Z");
        };

        var cubicBezier = function (path, p1, p2, p3) {
            path.push(
                    "C", p1[0], ",", p1[1],
                    ",", p2[0], ",", p2[1],
                    ",", p3[0], ",", p3[1]);
        };

        var minT = Math.max(
                newtonMethod(wetherill.x, x.domain()[0]),
                newtonMethod(wetherill.y, y.domain()[0]));

        var maxT = Math.min(
                newtonMethod(wetherill.x, x.domain()[1]),
                newtonMethod(wetherill.y, y.domain()[1]));

        // build the concordia line
        plot.area.clipped.select(".concordia")
                .attr("d", function () {
                    var approximateSegment = function (path, minT, maxT) {
                        var p1 = wetherill(minT).plus(
                                wetherill.prime(minT).times((maxT - minT) / 3))
                                .scaleBy(x, y);
                        var p2 = wetherill(maxT).minus(
                                wetherill.prime(maxT).times((maxT - minT) / 3))
                                .scaleBy(x, y);
                        var p3 = wetherill(maxT).scaleBy(x, y);

                        // append a cubic bezier to the path
                        cubicBezier(path, p1, p2, p3);
                    };

                    // initialize path
                    var path = [];
                    moveTo(path, wetherill(minT).scaleBy(x, y));

                    // determine the step size using the number of pieces
                    var pieces = 30;
                    var stepSize = (maxT - minT) / pieces;

                    // build the pieces
                    for (var i = 0; i < pieces; i++) {
                        approximateSegment(path, minT + stepSize * i, minT + stepSize * (i + 1));
                    }

                    return path.join("");
                });

        plot.area.clipped.select(".uncertaintyEnvelope")
                .attr("d", function () {
                    var approximateUpperSegment = function (path, minT, maxT) {
                        var p1 = wetherill.upperEnvelope(minT).plus(
                            wetherill.prime(minT).times((maxT - minT) / 3))
                            .scaleBy(x, y);
                        var p2 = wetherill.upperEnvelope(maxT).minus(
                            wetherill.prime(maxT).times((maxT - minT) / 3))
                            .scaleBy(x, y);
                        var p3 = wetherill.upperEnvelope(maxT).scaleBy(x, y);

                        // append a cubic bezier to the path
                        cubicBezier(path, p1, p2, p3);
                    };

                    var approximateLowerSegment = function (path, minT, maxT) {
                        var p1 = wetherill.lowerEnvelope(minT).plus(
                            wetherill.prime(minT).times((maxT - minT) / 3))
                            .scaleBy(x, y);
                        var p2 = wetherill.lowerEnvelope(maxT).minus(
                            wetherill.prime(maxT).times((maxT - minT) / 3))
                            .scaleBy(x, y);
                        var p3 = wetherill.lowerEnvelope(maxT).scaleBy(x, y);

                        // append a cubic bezier to the path
                        cubicBezier(path, p1, p2, p3);
                    };

                    var minT = Math.max(
                            newtonMethod(wetherill.upperEnvelope.x, x.domain()[0]),
                            newtonMethod(wetherill.upperEnvelope.y, y.domain()[0]));

                    var maxT = Math.min(
                            newtonMethod(wetherill.upperEnvelope.x, x.domain()[1]),
                            newtonMethod(wetherill.upperEnvelope.y, y.domain()[1]));

                    // initialize path
                    var path = [];
                    moveTo(path, wetherill.upperEnvelope(minT).scaleBy(x, y));

                    // determine the step size using the number of pieces
                    var pieces = 30;
                    var stepSize = (maxT - minT) / pieces;

                    // build the pieces
                    for (var i = 0; i < pieces; i++) {
                        approximateUpperSegment(path, minT + stepSize * i, minT + stepSize * (i + 1));
                    }

                    lineTo(path, [x.range()[1], y.range()[1]]);

                    var minT = Math.max(
                            newtonMethod(wetherill.lowerEnvelope.x, x.domain()[0]),
                            newtonMethod(wetherill.lowerEnvelope.y, y.domain()[0]));

                    var maxT = Math.min(
                            newtonMethod(wetherill.lowerEnvelope.x, x.domain()[1]),
                            newtonMethod(wetherill.lowerEnvelope.y, y.domain()[1]));

                    lineTo(path, wetherill.lowerEnvelope(maxT).scaleBy(x, y));

                    var stepSize = (maxT - minT) / pieces;

                    // build the pieces
                    for (var i = 0; i < pieces; i++) {
                        approximateLowerSegment(path, maxT - stepSize * i, maxT - stepSize * (i + 1));
                    }

                    lineTo(path, [x.range()[0], y.range()[0]]);
                    close(path);

                    return path.join("");
                });

        plot.t.domain([minT, maxT]);

        var ticks;
        (ticks = plot.area.clipped.selectAll(".tick")
                .data(plot.t.ticks()))
                .enter()
                .append("circle")
                .attr("class", "tick")
                .attr("r", 5);

        ticks
                .attr("cx", function (t) { return x(wetherill.x(t)); })
                .attr("cy", function (t) { return y(wetherill.y(t)); });

        var tickLabels;
        (tickLabels = plot.area.clipped.selectAll(".tickLabel")
                .data(plot.t.ticks()))
                .attr("x", function (t) { return x(wetherill.x(t)) + 12; })
                .attr("y", function (t) { return y(wetherill.y(t)) + 5; })
                .text(function (t) { return t / 1000000; })
                .enter()
                .append("text")
                .attr("font-family", "sans-serif")
                .attr("class", "tickLabel");

        // update the ellipses
        ellipses.attr("d", function (d) {
            var k = 4 / 3 * (Math.sqrt(2) - 1);
            var r = [
                [d.sigma_x, d.rho * d.sigma_y],
                [0, d.sigma_y * Math.sqrt(1 - d.rho * d.rho)]
            ];
            var controlPointsBase = [
                [1, 0],
                [1, k],
                [k, 1],
                [0, 1],
                [-k, 1],
                [-1, k],
                [-1, 0],
                [-1, -k],
                [-k, -1],
                [0, -1],
                [k, -1],
                [1, -k],
                [1, 0]
            ];

            var ellipsePath = d3.svg.line()
                    .x(function (datum) {
                        return x(datum[0] + d.x);
                    })
                    .y(function (datum) {
                        return y(datum[1] + d.y);
                    })
                    .interpolate(function (points) {
                        var i = 1, path = [points[0][0], ",", points[0][1]];
                        while (i + 3 <= points.length) {
                            cubicBezier(path, points[i++], points[i++], points[i++]);
                        }
                        return path.join("");
                    });

            return ellipsePath(numeric.mul(
                plot.getProperty("Uncertainty"),
                numeric.dot(controlPointsBase, r)));
        });

        // update the center points
        dots
                .attr("cx", function (d) {
                    return x(d.x);
                })
                .attr("cy", function (d) {
                    return y(d.y);
                });

        // retick the axes
        plot.area.selectAll(".x.axis")
                .call(xAxis);

        plot.area.selectAll(".y.axis")
                .call(yAxis);

        // axis styling
        plot.area.selectAll(".axis text")
                .attr("font-family", "sans-serif")
                .attr("font-size", "10px");

        plot.area.selectAll(".axis path, .axis line")
                .attr("fill", "none")
                .attr("stroke", "black")
                .attr("shape-rendering", "geometricPrecision"); // see SVG docs

        // exit
        ellipses.exit().remove();
        dots.exit().remove();
        ticks.exit().remove();
        tickLabels.exit().remove();

        var zoom = d3.behavior.zoom()
                .x(x)
                .y(y)
                .scaleExtent([.5, 2.5])
                .on("zoom", function () {
                    var t = zoom.translate();
                    var tx = t[0];
                    var ty = t[1];

                    // keep the viewbox northeast of (0, 0)
                    if (x.domain()[0] < 0)
                        tx += x.range()[0] - x(0);
                    if (y.domain()[0] < 0)
                        ty += y.range()[0] - y(0);
                    zoom.translate([tx, ty]);

                    plot.x.domain([zoom.x().domain()[0], zoom.x().domain()[1]]);
                    plot.y.domain([zoom.y().domain()[0], zoom.y().domain()[1]]);

                    plot.update(data);
                });
        plot.area.clipped.call(zoom);
    };
})();
<commit_msg>Refactors UncertaintyEllipsePlot so the ellipse data points are calculated only once<commit_after>/*
 * Copyright 2014 CIRDLES.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function () {
    "use strict";

    plot.dataKeys = ['x', 'sigma_x', 'y', 'sigma_y', 'rho', 'Selected'];

    plot.propertiesKeys = [
        'Ellipse Fill Color',
        'Title',
        'Uncertainty',
        'X Axis',
        'Y Axis',
        'LAMBDA_235',
        'LAMBDA_238'];

    plot.draw = function (data) {
        initializeWetherill({
            LAMBDA_235: plot.getProperty("LAMBDA_235"),
            LAMBDA_238: plot.getProperty("LAMBDA_238")
        });

        var x = plot.x = d3.scale.linear()
                .range([0, plot.width]);

        var y = plot.y = d3.scale.linear()
                .range([plot.height, 0]);

        plot.t = d3.scale.linear();

        var xMin = 0;
        var xMax = 1;
        var yMin = 0;
        var yMax = 1;

        if (data.length > 0){
            var dataXMin = d3.min(data, function (d) {
                return d.x - d.sigma_x * plot.getProperty("Uncertainty");
            });

            var dataYMin = d3.min(data, function (d) {
                return d.y - d.sigma_y * plot.getProperty("Uncertainty");
            });

            var dataXMax = d3.max(data, function (d) {
                return d.x + d.sigma_x * plot.getProperty("Uncertainty");
            });

            var dataYMax = d3.max(data, function (d) {
                return d.y + d.sigma_y * plot.getProperty("Uncertainty");
            });

            var xRange = dataXMax - dataXMin;
            var yRange = dataYMax - dataYMin;

            xMin = dataXMin - 0.05 * xRange;
            yMin =  dataYMin - 0.05 * yRange;
            xMax = dataXMax + 0.05 * xRange;
            yMax = dataYMax + 0.05 * yRange;
        }

        x.domain([xMin, xMax]);
        y.domain([yMin, yMax]);

        // initialize the concordia envelope
        plot.area.clipped.append("path")
                .attr("class", "uncertaintyEnvelope")
                .attr("fill", "lightgray")
                .attr("stroke", "none")
                .attr("shape-rendering", "geometricPrecision");

        // initialize the concordia
        plot.area.clipped.append("path")
                .attr("class", "concordia")
                .attr("fill", "none")
                .attr("stroke", "blue")
                .attr("stroke-width", 2)
                .attr("shape-rendering", "geometricPrecision");

        plot.area.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + plot.height + ")")
                .append("text")
                .attr("class", "label")
                .attr("x", plot.width / 2)
                .attr("y", 35);

        plot.area.append("g")
                .attr("class", "y axis")
                .append("text")
                .attr("class", "label")
                .attr("transform", "rotate(-90)")
                .attr("x", -plot.height / 2)
                .attr("y", -50)
                .attr("dy", ".71em");

        var k = 4 / 3 * (Math.sqrt(2) - 1);
        var controlPointsBase = [
            [1, 0],
            [1, k],
            [k, 1],
            [0, 1],
            [-k, 1],
            [-1, k],
            [-1, 0],
            [-1, -k],
            [-k, -1],
            [0, -1],
            [k, -1],
            [1, -k],
            [1, 0]
        ];

        var cacheData = plot.cacheData = data.map(function (d) {
            var r = [
                [d.sigma_x, d.rho * d.sigma_y],
                [0, d.sigma_y * Math.sqrt(1 - d.rho * d.rho)]
            ];

            var shift = function(dx, dy) {
                return function(p) {
                    return [p[0] + dx, p[1] + dy];
                };
            };

            var points = numeric.mul(
                    plot.getProperty("Uncertainty"),
                    numeric.dot(controlPointsBase, r));

            return points.map(shift(d.x, d.y));
        });

        plot.update(data);
    };

    plot.update = function (data) {
        var x = plot.x;
        var y = plot.y;

        d3.select(".x.axis .label").text(plot.getProperty("X Axis"));
        d3.select(".y.axis .label").text(plot.getProperty("Y Axis"));

        var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom");

        var yAxis = d3.svg.axis()
                .scale(y)
                .orient("left");

        var ellipses;
        (ellipses = plot.area.clipped.selectAll(".ellipse")
                .data(plot.cacheData))
                .enter().append("path")
                .attr("class", "ellipse")
                .attr("fill-opacity", 0.3)
                .attr("stroke", "black");

        ellipses.attr("fill", function(d) {
            var fill;

            if (!d['Selected']) {
                fill = 'gray';
            } else {
                fill = plot.getProperty("Ellipse Fill Color");
            }

            return fill;
        });

        var dots;
        (dots = plot.area.clipped.selectAll(".dot")
                .data(data))
                .enter().append("circle")
                .attr("class", "dot")
                .attr("r", 1.5);

        // utilities for generating path data elements
        var moveTo = function (path, p) {
            path.push("M", p[0], ",", p[1]);
        };

        var lineTo = function (path, p) {
            path.push("L", p[0], ",", p[1]);
        };

        var close = function (path) {
            path.push("Z");
        };

        var cubicBezier = function (path, p1, p2, p3) {
            path.push(
                    "C", p1[0], ",", p1[1],
                    ",", p2[0], ",", p2[1],
                    ",", p3[0], ",", p3[1]);
        };

        var minT = Math.max(
                newtonMethod(wetherill.x, x.domain()[0]),
                newtonMethod(wetherill.y, y.domain()[0]));

        var maxT = Math.min(
                newtonMethod(wetherill.x, x.domain()[1]),
                newtonMethod(wetherill.y, y.domain()[1]));

        // build the concordia line
        plot.area.clipped.select(".concordia")
                .attr("d", function () {
                    var approximateSegment = function (path, minT, maxT) {
                        var p1 = wetherill(minT).plus(
                                wetherill.prime(minT).times((maxT - minT) / 3))
                                .scaleBy(x, y);
                        var p2 = wetherill(maxT).minus(
                                wetherill.prime(maxT).times((maxT - minT) / 3))
                                .scaleBy(x, y);
                        var p3 = wetherill(maxT).scaleBy(x, y);

                        // append a cubic bezier to the path
                        cubicBezier(path, p1, p2, p3);
                    };

                    // initialize path
                    var path = [];
                    moveTo(path, wetherill(minT).scaleBy(x, y));

                    // determine the step size using the number of pieces
                    var pieces = 30;
                    var stepSize = (maxT - minT) / pieces;

                    // build the pieces
                    for (var i = 0; i < pieces; i++) {
                        approximateSegment(path, minT + stepSize * i, minT + stepSize * (i + 1));
                    }

                    return path.join("");
                });

        plot.area.clipped.select(".uncertaintyEnvelope")
                .attr("d", function () {
                    var approximateUpperSegment = function (path, minT, maxT) {
                        var p1 = wetherill.upperEnvelope(minT).plus(
                            wetherill.prime(minT).times((maxT - minT) / 3))
                            .scaleBy(x, y);
                        var p2 = wetherill.upperEnvelope(maxT).minus(
                            wetherill.prime(maxT).times((maxT - minT) / 3))
                            .scaleBy(x, y);
                        var p3 = wetherill.upperEnvelope(maxT).scaleBy(x, y);

                        // append a cubic bezier to the path
                        cubicBezier(path, p1, p2, p3);
                    };

                    var approximateLowerSegment = function (path, minT, maxT) {
                        var p1 = wetherill.lowerEnvelope(minT).plus(
                            wetherill.prime(minT).times((maxT - minT) / 3))
                            .scaleBy(x, y);
                        var p2 = wetherill.lowerEnvelope(maxT).minus(
                            wetherill.prime(maxT).times((maxT - minT) / 3))
                            .scaleBy(x, y);
                        var p3 = wetherill.lowerEnvelope(maxT).scaleBy(x, y);

                        // append a cubic bezier to the path
                        cubicBezier(path, p1, p2, p3);
                    };

                    var minT = Math.max(
                            newtonMethod(wetherill.upperEnvelope.x, x.domain()[0]),
                            newtonMethod(wetherill.upperEnvelope.y, y.domain()[0]));

                    var maxT = Math.min(
                            newtonMethod(wetherill.upperEnvelope.x, x.domain()[1]),
                            newtonMethod(wetherill.upperEnvelope.y, y.domain()[1]));

                    // initialize path
                    var path = [];
                    moveTo(path, wetherill.upperEnvelope(minT).scaleBy(x, y));

                    // determine the step size using the number of pieces
                    var pieces = 30;
                    var stepSize = (maxT - minT) / pieces;

                    // build the pieces
                    for (var i = 0; i < pieces; i++) {
                        approximateUpperSegment(path, minT + stepSize * i, minT + stepSize * (i + 1));
                    }

                    lineTo(path, [x.range()[1], y.range()[1]]);

                    var minT = Math.max(
                            newtonMethod(wetherill.lowerEnvelope.x, x.domain()[0]),
                            newtonMethod(wetherill.lowerEnvelope.y, y.domain()[0]));

                    var maxT = Math.min(
                            newtonMethod(wetherill.lowerEnvelope.x, x.domain()[1]),
                            newtonMethod(wetherill.lowerEnvelope.y, y.domain()[1]));

                    lineTo(path, wetherill.lowerEnvelope(maxT).scaleBy(x, y));

                    var stepSize = (maxT - minT) / pieces;

                    // build the pieces
                    for (var i = 0; i < pieces; i++) {
                        approximateLowerSegment(path, maxT - stepSize * i, maxT - stepSize * (i + 1));
                    }

                    lineTo(path, [x.range()[0], y.range()[0]]);
                    close(path);

                    return path.join("");
                });

        plot.t.domain([minT, maxT]);

        var ticks;
        (ticks = plot.area.clipped.selectAll(".tick")
                .data(plot.t.ticks()))
                .enter()
                .append("circle")
                .attr("class", "tick")
                .attr("r", 5);

        ticks
                .attr("cx", function (t) { return x(wetherill.x(t)); })
                .attr("cy", function (t) { return y(wetherill.y(t)); });

        var tickLabels;
        (tickLabels = plot.area.clipped.selectAll(".tickLabel")
                .data(plot.t.ticks()))
                .attr("x", function (t) { return x(wetherill.x(t)) + 12; })
                .attr("y", function (t) { return y(wetherill.y(t)) + 5; })
                .text(function (t) { return t / 1000000; })
                .enter()
                .append("text")
                .attr("font-family", "sans-serif")
                .attr("class", "tickLabel");

        // update the ellipses
        ellipses.attr("d", function (d) {
            var ellipsePath = d3.svg.line()
                    .x(function (datum) {
                        return x(datum[0]);
                    })
                    .y(function (datum) {
                        return y(datum[1]);
                    })
                    .interpolate(function (points) {
                        var i = 1, path = [points[0][0], ",", points[0][1]];
                        while (i + 3 <= points.length) {
                            cubicBezier(path, points[i++], points[i++], points[i++]);
                        }
                        return path.join("");
                    });

            return ellipsePath(d);
        });

        // update the center points
        dots
                .attr("cx", function (d) {
                    return x(d.x);
                })
                .attr("cy", function (d) {
                    return y(d.y);
                });

        // retick the axes
        plot.area.selectAll(".x.axis")
                .call(xAxis);

        plot.area.selectAll(".y.axis")
                .call(yAxis);

        // axis styling
        plot.area.selectAll(".axis text")
                .attr("font-family", "sans-serif")
                .attr("font-size", "10px");

        plot.area.selectAll(".axis path, .axis line")
                .attr("fill", "none")
                .attr("stroke", "black")
                .attr("shape-rendering", "geometricPrecision"); // see SVG docs

        // exit
        ellipses.exit().remove();
        dots.exit().remove();
        ticks.exit().remove();
        tickLabels.exit().remove();

        var zoom = d3.behavior.zoom()
                .x(x)
                .y(y)
                .scaleExtent([.5, 2.5])
                .on("zoom", function () {
                    var t = zoom.translate();
                    var tx = t[0];
                    var ty = t[1];

                    // keep the viewbox northeast of (0, 0)
                    if (x.domain()[0] < 0)
                        tx += x.range()[0] - x(0);
                    if (y.domain()[0] < 0)
                        ty += y.range()[0] - y(0);
                    zoom.translate([tx, ty]);

                    plot.x.domain([zoom.x().domain()[0], zoom.x().domain()[1]]);
                    plot.y.domain([zoom.y().domain()[0], zoom.y().domain()[1]]);

                    plot.update(data);
                });
        plot.area.clipped.call(zoom);
    };
})();
<|endoftext|>


<commit_before>// import Mantissa

Mantissa.ScrollTable = {};

Mantissa.ScrollTable.ScrollingWidget = Nevow.Athena.Widget.subclass('Mantissa.ScrollTable.ScrollingWidget');

Mantissa.ScrollTable.ScrollingWidget.methods(
    function __init__(self, node) {
        Mantissa.ScrollTable.ScrollingWidget.upcall(self, '__init__', node);
        self._rows = [];
        self._rowTimeout = null;
        self._requestWaiting = false;
        self._moreAfterRequest = false;

        self._scrollContent = self.nodeByAttribute("class", "scroll-content");
        self._scrollViewport = self.nodeByAttribute('class', 'scroll-viewport');
        self._headerRow = self.nodeByAttribute('class', 'scroll-header-row');
        self.setRowHeight();

        self.callRemote("getTableMetadata").addCallback(
            function(metadata) {
                /*
                  argument passing convention!  woo, someday soon Javascript
                  will have all the expressiveness of PL/1.  Maybe then we can
                  decide what registers get used to store variables, too!!!!
                */
                var columnNames = metadata[0];
                self.columnTypes = metadata[1];
                var rowCount = metadata[2];
                var currentSort = metadata[3];
                var isAscendingNow = metadata[4];

                /*
                  (OK, seriously, there should be some kind of
                  multiple-value-unpacking that's easier than this, since we
                  want to decrease the number of round-trips as much as
                  possible...)
                */

                self._createRowHeaders(columnNames);
                self.setSortInfo(currentSort, isAscendingNow);
                self.setViewportHeight(rowCount);
                // Go suuuper fast for the first request
                self.scrolled(10);
            });
    },

    function setRowHeight(self) {
        var rowHeight = self._headerRow.clientHeight;
        if (rowHeight == 0) {
            rowHeight = 20; /* IE can't see clientHeight on some nodes...? */
        }
        /* actually this is wrong, we should calculate from a dummy
            row, but blah templates or something. */

        self._rowHeight = rowHeight;
    },

    function setViewportHeight(self, rowCount) {
        var scrollContentHeight = self._rowHeight * rowCount;
        self._scrollContent.style.height = scrollContentHeight + 'px';
    },

    function adjustViewportHeight(self, rowCount) {
        var height = parseInt(self._scrollContent.style.height);
        self._scrollContent.style.height = height + (self._rowHeight * rowCount) + "px";
    },

    function _getSomeRows(self) {
        var scrollViewportHeight = self._scrollViewport.clientHeight;
        var desiredRowCount = Math.ceil((scrollViewportHeight) / self._rowHeight);

        var firstRow = Math.floor(self._scrollViewport.scrollTop / self._rowHeight);

        var requestNeeded = false;

        for (var i = 0; i < desiredRowCount; i++) {
            if (typeof self._rows[firstRow] === 'undefined') {
                requestNeeded = true;
                break;
            }
            firstRow++;
        }

        if (!requestNeeded) {
            return Divmod.Defer.succeed(1);
        }

        /* do we have the rows we need ? */

        return self.callRemote("requestRowRange", firstRow, firstRow + desiredRowCount).addCallback(
            function(rowData) {
                return self.createRows(firstRow, rowData);
            });
    },

    function createRows(self, idx, rowData) {
        MochiKit.Base.map(
            function(row) {
                if (typeof self._rows[idx] === 'undefined') {
                    self._createRow(idx, row);
                }
                idx++;
            },
            rowData);
    },

    function formatDate(self, date) {
        return date.toUTCString();
    },

    function massageColumnValue(self, columnName, columnType, columnValue) {
        if(columnType == 'timestamp') {
            return self.formatDate(new Date(columnValue * 1000));
        }
	if(columnValue ==  null) {
            return '';
	}
        return columnValue;
    },

    function makeCellElement(self, colName, rowData) {
        return MochiKit.DOM.DIV({"class": "scroll-cell"},
                                self.massageColumnValue(
                                     colName, self.columnTypes[colName], rowData[colName]));
    },

    function _createRow(self, rowOffset, rowData) {
        var cells = [];

        for(var colName in rowData) {
            if(!(colName in self._columnOffsets) || self.skipColumn(colName)) {
                continue;
            }
            cells.push([colName, self.makeCellElement(colName, rowData)]);
        }

        cells = cells.sort(
            function(data1, data2) {
                var a = self._columnOffsets[data1[0]];
                var b = self._columnOffsets[data2[0]];

                if (a<b) {
                    return -1;
                }
                if (a>b) {
                    return 1;
                }
                return 0;
            });

        cells = MochiKit.Base.map.apply(null, [null].concat(cells))[1];
        var rowNode = self.makeRowElement(rowOffset, rowData, cells);

        self._rows[rowOffset] = [rowData, rowNode];
        self._scrollContent.appendChild(rowNode);
    },

    function makeRowElement(self, rowOffset, rowData, cells) {
        return MochiKit.DOM.A(
            {"class": "scroll-row",
             "href": rowData['__id__']},
            cells);
    },

    function skipColumn(self, name) {
        return false;
    },

    function _createRowHeaders(self, columnNames) {
        var capitalize = function(s) {
            var words = s.split(/ /);
            var capped = "";
            for(var i = 0; i < words.length; i++) {
                capped += words[i].substr(0, 1).toUpperCase();
                capped += words[i].substr(1, words[i].length) + " ";
            }
            return capped;
        }

        var headerRow = self._headerRow;
        var columnOffsets = {};
        var headerNodes = [];
        for( var i = 0; i < columnNames.length; i++ ) {
            if(self.skipColumn(columnNames[i])) {
                continue;
            }
            columnOffsets[columnNames[i]] = i;
            (function () {
                var bindName = columnNames[i];
                var displayName;

                if(self.columnAliases && bindName in self.columnAliases) {
                    displayName = self.columnAliases[bindName];
                } else {
                    displayName = capitalize(bindName);
                }

                /*
                 * ^ Thank you, brilliant JavaScript designers, for inventing
                 * a whole new language.  This is _way_ better than (let ()).
                 */
                var headerNode = self.makeHeaderRow(bindName, displayName);
                headerRow.appendChild(headerNode);
                headerNodes.push(headerNode);
            })();
        }
        self._headerNodes = headerNodes;
        self._columnOffsets = columnOffsets;
    },

    function makeHeaderRow(self, bindName, displayName) {
        return MochiKit.DOM.DIV({"class": "scroll-column-header",
                    onclick: function () {
                    /* XXX real-time feedback, ugh */
                    self.callRemote("resort", bindName).addCallback(
                        function(isAscendingNow) {
                            self.setSortInfo(bindName, isAscendingNow);
                            self.emptyAndRefill();
                        })
                        }}, displayName);
    },

    function emptyAndRefill(self) {
        for (var whichRow = 0; whichRow < self._rows.length; whichRow++) {
            if (typeof self._rows[whichRow] !== 'undefined') {
                var rowNode = self._rows[whichRow][1];
                rowNode.parentNode.removeChild(rowNode);
            }
        }
        self._rows = [];
        self.scrolled();
    },

    function setSortInfo(self, currentSortColumn, isAscendingNow) {
        for(var j = 0; j < self._headerNodes.length; j++) {
            while(1 < self._headerNodes[j].childNodes.length) {
                self._headerNodes[j].removeChild(self._headerNodes[j].lastChild);
            }
        }
        var c;
        if(isAscendingNow) {
            c = '\u2191'; // up arrow
        } else {
            c = '\u2193'; // down arrow
        }
        self._headerNodes[self._columnOffsets[currentSortColumn]].appendChild(
            MochiKit.DOM.SPAN({"class": "sort-arrow"}, c));
    },

    function scrolled(self, proposedTimeout) {
        if (typeof proposedTimeout === 'undefined') {
            proposedTimeout = 250;
        }
        if (self._requestWaiting) {
            self._moreAfterRequest = true;
            return;
        }
        if (self._rowTimeout !== null) {
            clearTimeout(self._rowTimeout);
        }
        self._rowTimeout = setTimeout(
            function () {
                self._rowTimeout = null;
                self._requestWaiting = true;
                var rowCount = self._rows.length;
                self._getSomeRows().addBoth(
                    function (rslt) {
                        self._requestWaiting = false;
                        if (self._moreAfterRequest) {
                            self._moreAfterRequest = false;
                            self.scrolled();
                        }
                        if(rowCount < self._rows.length) {
                            self.cbRowsFetched();
                        }
                        return rslt;

                    });
            },
            proposedTimeout);
    },

    function cbRowsFetched(self) {});
<commit_msg>apply patch from #840.  author: moe, reviewer: glyph.  fixes issue with scrolltable row positioning.  closes #840<commit_after>// import Mantissa

Mantissa.ScrollTable = {};

Mantissa.ScrollTable.ScrollingWidget = Nevow.Athena.Widget.subclass('Mantissa.ScrollTable.ScrollingWidget');

Mantissa.ScrollTable.ScrollingWidget.methods(
    function __init__(self, node) {
        Mantissa.ScrollTable.ScrollingWidget.upcall(self, '__init__', node);
        self._rows = [];
        self._rowTimeout = null;
        self._requestWaiting = false;
        self._moreAfterRequest = false;

        self._scrollContent = self.nodeByAttribute("class", "scroll-content");
        self._scrollViewport = self.nodeByAttribute('class', 'scroll-viewport');
        self._headerRow = self.nodeByAttribute('class', 'scroll-header-row');
        self.setRowHeight();

        self.callRemote("getTableMetadata").addCallback(
            function(metadata) {
                /*
                  argument passing convention!  woo, someday soon Javascript
                  will have all the expressiveness of PL/1.  Maybe then we can
                  decide what registers get used to store variables, too!!!!
                */
                var columnNames = metadata[0];
                self.columnTypes = metadata[1];
                var rowCount = metadata[2];
                var currentSort = metadata[3];
                var isAscendingNow = metadata[4];

                /*
                  (OK, seriously, there should be some kind of
                  multiple-value-unpacking that's easier than this, since we
                  want to decrease the number of round-trips as much as
                  possible...)
                */

                self._createRowHeaders(columnNames);
                self.setSortInfo(currentSort, isAscendingNow);
                self.setViewportHeight(rowCount);
                // Go suuuper fast for the first request
                self.scrolled(10);
            });
    },

    function setRowHeight(self) {
        var rowHeight = self._headerRow.clientHeight;
        if (rowHeight == 0) {
            rowHeight = 20; /* IE can't see clientHeight on some nodes...? */
        }
        /* actually this is wrong, we should calculate from a dummy
            row, but blah templates or something. */

        self._rowHeight = rowHeight;
    },

    function setViewportHeight(self, rowCount) {
        var scrollContentHeight = self._rowHeight * rowCount;
        self._scrollContent.style.height = scrollContentHeight + 'px';
    },

    function adjustViewportHeight(self, rowCount) {
        var height = parseInt(self._scrollContent.style.height);
        self._scrollContent.style.height = height + (self._rowHeight * rowCount) + "px";
    },

    function _getSomeRows(self) {
        var scrollViewportHeight = self._scrollViewport.clientHeight;
        var desiredRowCount = Math.ceil((scrollViewportHeight) / self._rowHeight);

        var firstRow = Math.floor(self._scrollViewport.scrollTop / self._rowHeight);

        var requestNeeded = false;

        for (var i = 0; i < desiredRowCount; i++) {
            if (typeof self._rows[firstRow] === 'undefined') {
                requestNeeded = true;
                break;
            }
            firstRow++;
        }

        if (!requestNeeded) {
            return Divmod.Defer.succeed(1);
        }

        /* do we have the rows we need ? */

        return self.callRemote("requestRowRange", firstRow, firstRow + desiredRowCount).addCallback(
            function(rowData) {
                return self.createRows(firstRow, rowData);
            });
    },

    function createRows(self, idx, rowData) {
        MochiKit.Base.map(
            function(row) {
                if (typeof self._rows[idx] === 'undefined') {
                    self._createRow(idx, row);
                }
                idx++;
            },
            rowData);
    },

    function formatDate(self, date) {
        return date.toUTCString();
    },

    function massageColumnValue(self, columnName, columnType, columnValue) {
        if(columnType == 'timestamp') {
            return self.formatDate(new Date(columnValue * 1000));
        }
	if(columnValue ==  null) {
            return '';
	}
        return columnValue;
    },

    function makeCellElement(self, colName, rowData) {
        return MochiKit.DOM.DIV({"class": "scroll-cell"},
                                self.massageColumnValue(
                                     colName, self.columnTypes[colName], rowData[colName]));
    },

    function _createRow(self, rowOffset, rowData) {
        var cells = [];

        for(var colName in rowData) {
            if(!(colName in self._columnOffsets) || self.skipColumn(colName)) {
                continue;
            }
            cells.push([colName, self.makeCellElement(colName, rowData)]);
        }

        cells = cells.sort(
            function(data1, data2) {
                var a = self._columnOffsets[data1[0]];
                var b = self._columnOffsets[data2[0]];

                if (a<b) {
                    return -1;
                }
                if (a>b) {
                    return 1;
                }
                return 0;
            });

        cells = MochiKit.Base.map.apply(null, [null].concat(cells))[1];
        var rowNode = self.makeRowElement(rowOffset, rowData, cells);

        rowNode.style.position = "absolute";
        rowNode.style.top = (rowOffset * self._rowHeight) + "px";

        self._rows[rowOffset] = [rowData, rowNode];
        self._scrollContent.appendChild(rowNode);
    },

    function makeRowElement(self, rowOffset, rowData, cells) {
        return MochiKit.DOM.A(
            {"class": "scroll-row",
             "href": rowData['__id__']},
            cells);
    },

    function skipColumn(self, name) {
        return false;
    },

    function _createRowHeaders(self, columnNames) {
        var capitalize = function(s) {
            var words = s.split(/ /);
            var capped = "";
            for(var i = 0; i < words.length; i++) {
                capped += words[i].substr(0, 1).toUpperCase();
                capped += words[i].substr(1, words[i].length) + " ";
            }
            return capped;
        }

        var headerRow = self._headerRow;
        var columnOffsets = {};
        var headerNodes = [];
        for( var i = 0; i < columnNames.length; i++ ) {
            if(self.skipColumn(columnNames[i])) {
                continue;
            }
            columnOffsets[columnNames[i]] = i;
            (function () {
                var bindName = columnNames[i];
                var displayName;

                if(self.columnAliases && bindName in self.columnAliases) {
                    displayName = self.columnAliases[bindName];
                } else {
                    displayName = capitalize(bindName);
                }

                /*
                 * ^ Thank you, brilliant JavaScript designers, for inventing
                 * a whole new language.  This is _way_ better than (let ()).
                 */
                var headerNode = self.makeHeaderRow(bindName, displayName);
                headerRow.appendChild(headerNode);
                headerNodes.push(headerNode);
            })();
        }
        self._headerNodes = headerNodes;
        self._columnOffsets = columnOffsets;
    },

    function makeHeaderRow(self, bindName, displayName) {
        return MochiKit.DOM.DIV({"class": "scroll-column-header",
                    onclick: function () {
                    /* XXX real-time feedback, ugh */
                    self.callRemote("resort", bindName).addCallback(
                        function(isAscendingNow) {
                            self.setSortInfo(bindName, isAscendingNow);
                            self.emptyAndRefill();
                        })
                        }}, displayName);
    },

    function emptyAndRefill(self) {
        for (var whichRow = 0; whichRow < self._rows.length; whichRow++) {
            if (typeof self._rows[whichRow] !== 'undefined') {
                var rowNode = self._rows[whichRow][1];
                rowNode.parentNode.removeChild(rowNode);
            }
        }
        self._rows = [];
        self.scrolled();
    },

    function setSortInfo(self, currentSortColumn, isAscendingNow) {
        for(var j = 0; j < self._headerNodes.length; j++) {
            while(1 < self._headerNodes[j].childNodes.length) {
                self._headerNodes[j].removeChild(self._headerNodes[j].lastChild);
            }
        }
        var c;
        if(isAscendingNow) {
            c = '\u2191'; // up arrow
        } else {
            c = '\u2193'; // down arrow
        }
        self._headerNodes[self._columnOffsets[currentSortColumn]].appendChild(
            MochiKit.DOM.SPAN({"class": "sort-arrow"}, c));
    },

    function scrolled(self, proposedTimeout) {
        if (typeof proposedTimeout === 'undefined') {
            proposedTimeout = 250;
        }
        if (self._requestWaiting) {
            self._moreAfterRequest = true;
            return;
        }
        if (self._rowTimeout !== null) {
            clearTimeout(self._rowTimeout);
        }
        self._rowTimeout = setTimeout(
            function () {
                self._rowTimeout = null;
                self._requestWaiting = true;
                var rowCount = self._rows.length;
                self._getSomeRows().addBoth(
                    function (rslt) {
                        self._requestWaiting = false;
                        if (self._moreAfterRequest) {
                            self._moreAfterRequest = false;
                            self.scrolled();
                        }
                        if(rowCount < self._rows.length) {
                            self.cbRowsFetched();
                        }
                        return rslt;

                    });
            },
            proposedTimeout);
    },

    function cbRowsFetched(self) {});
<|endoftext|>


<commit_before>/*
Geodesic extension to Leaflet library, by Fragger
https://github.com/Fragger/Leaflet.Geodesic
Version from master branch, dated Apr 26, 2013
Modified by qnstie 2013-07-17 to maintain compatibility with Leaflet.draw
*/
(function () {
  function geodesicPoly(Klass, fill) {
    return Klass.extend({
      initialize: function (latlngs, options) {
        Klass.prototype.initialize.call(this, L.geodesicConvertLines(latlngs, fill), options);
        this._latlngsinit = this._convertLatLngs(latlngs);
      },
      getLatLngs: function () {
        return this._latlngsinit;
      },
      setLatLngs: function (latlngs) {
        this._latlngsinit = this._convertLatLngs(latlngs);
        return this.redraw();
      },
      addLatLng: function (latlng) {
        this._latlngsinit.push(L.latLng(latlng));
        return this.redraw();
      },
      spliceLatLngs: function () { // (Number index, Number howMany)
        var removed = [].splice.apply(this._latlngsinit, arguments);
        this._convertLatLngs(this._latlngsinit);
        this.redraw();
        return removed;
      },
      redraw: function() {
        this._latlngs = this._convertLatLngs(L.geodesicConvertLines(this._latlngsinit, fill));
        return Klass.prototype.redraw.call(this);
      }
    });
  }

  function geodesicConvertLine(startLatlng, endLatlng, convertedPoints) {
    var i,
      R = 6378137, // earth radius in meters (doesn't have to be exact)
      maxlength = 5000, // meters before splitting
      d2r = L.LatLng.DEG_TO_RAD,
      r2d = L.LatLng.RAD_TO_DEG,
      lat1, lat2, lng1, lng2, dLng, d, segments,
      f, A, B, x, y, z, fLat, fLng;

    dLng = (endLatlng.lng - startLatlng.lng) * d2r;
    lat1 = startLatlng.lat * d2r;
    lat2 = endLatlng.lat * d2r;
    lng1 = startLatlng.lng * d2r;
    lng2 = endLatlng.lng * d2r;

    // http://en.wikipedia.org/wiki/Great-circle_distance
    d = Math.atan2(Math.sqrt( Math.pow(Math.cos(lat2) * Math.sin(dLng), 2) + Math.pow(Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng), 2) ), Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLng));

    segments = Math.ceil(d * R / maxlength);
    // loop starts at 1 - we don't add the very first point
    // loop ends before 'segments' is reached - we don't add the very last point here but outside the loop
    // (this was to fix a bug - https://github.com/jonatkins/ingress-intel-total-conversion/issues/471
    //  rounding errors? maths bug? not sure - but it solves the issue! and is a slight optimisation)
    for (i = 1; i < segments; i++) {
      // http://williams.best.vwh.net/avform.htm#Intermediate
      // modified to handle longitude above +-180 degrees
      f = i / segments;
      A = Math.sin((1-f)*d) / Math.sin(d);
      B = Math.sin(f*d) / Math.sin(d);
      x = A * Math.cos(lat1) * Math.cos(0) + B * Math.cos(lat2) * Math.cos(dLng);
      y = A * Math.cos(lat1) * Math.sin(0) + B * Math.cos(lat2) * Math.sin(dLng);
      z = A * Math.sin(lat1)               + B * Math.sin(lat2);
      fLat = r2d * Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
      fLng = r2d * (Math.atan2(y, x)+lng1);

      convertedPoints.push(L.latLng([fLat, fLng]));
    }
    // push the final point unmodified
    convertedPoints.push(L.latLng(endLatlng));
  }

  L.geodesicConvertLines = function (latlngs, fill) {
    if (latlngs.length == 0) {
      return [];
    }

    for (var i = 0, len = latlngs.length; i < len; i++) {
      if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
        return;
      }
      latlngs[i] = L.latLng(latlngs[i]);
    }

    // geodesic calculations have issues when crossing the anti-meridian. so offset the points
    // so this isn't an issue, then add back the offset afterwards
    // a center longitude would be ideal - but the start point longitude will be 'good enough'
    var lngOffset = latlngs[0].lng;

    // points are wrapped after being offset relative to the first point coordinate, so they're
    // within +-180 degrees
    latlngs = latlngs.map(function(a){ return L.latLng(a.lat, a.lng-lngOffset).wrap(); });

    var geodesiclatlngs = [];

    if(!fill) {
      geodesiclatlngs.push(latlngs[0]);
    }
    for (i = 0, len = latlngs.length - 1; i < len; i++) {
      geodesicConvertLine(latlngs[i], latlngs[i+1], geodesiclatlngs);
    }
    if(fill) {
      geodesicConvertLine(latlngs[len], latlngs[0], geodesiclatlngs);
    }

    // now add back the offset subtracted above. no wrapping here - the drawing code handles
    // things better when there's no sudden jumps in coordinates. yes, lines will extend
    // beyond +-180 degrees - but they won't be 'broken'
    geodesiclatlngs = geodesiclatlngs.map(function(a){ return L.latLng(a.lat, a.lng+lngOffset); });

    return geodesiclatlngs;
  }

  L.GeodesicPolyline = geodesicPoly(L.Polyline, 0);
  L.GeodesicPolygon = geodesicPoly(L.Polygon, 1);

  //L.GeodesicMultiPolyline = createMulti(L.GeodesicPolyline);
  //L.GeodesicMultiPolygon = createMulti(L.GeodesicPolygon);

  /*L.GeodesicMultiPolyline = L.MultiPolyline.extend({
    initialize: function (latlngs, options) {
      L.MultiPolyline.prototype.initialize.call(this, L.geodesicConvertLines(latlngs), options);
    }
  });*/

  /*L.GeodesicMultiPolygon = L.MultiPolygon.extend({
    initialize: function (latlngs, options) {
      L.MultiPolygon.prototype.initialize.call(this, L.geodesicConvertLines(latlngs), options);
    }
  });*/

  L.GeodesicCircle = L.Polygon.extend({
    initialize: function (latlng, radius, options) {
      this._latlng = L.latLng(latlng);
      this._mRadius = radius;

      points = this._calcPoints();

      L.Polygon.prototype.initialize.call(this, points, options);
    },

    options: {
      fill: true
    },

    setLatLng: function (latlng) {
      this._latlng = L.latLng(latlng);
      points = this._calcPoints();
      this.setLatLngs(points);
    },

    setRadius: function (radius) {
      this._mRadius = radius;
      points = this._calcPoints();
      this.setLatLngs(points);

    },

    getLatLng: function () {
      return this._latlng;
    },

    getRadius: function() {
      return this._mRadius;
    },

    _calcPoints: function() {
      var R = 6378137; //earth radius in meters (approx - taken from leaflet source code)
      var d2r = L.LatLng.DEG_TO_RAD;
      var r2d = L.LatLng.RAD_TO_DEG;
//console.log("geodesicCircle: radius = "+this._mRadius+"m, centre "+this._latlng.lat+","+this._latlng.lng);

      // circle radius as an angle from the centre of the earth
      var radRadius = this._mRadius / R;

//console.log(" (radius in radians "+radRadius);

      // pre-calculate various values used for every point on the circle
      var centreLat = this._latlng.lat * d2r;
      var centreLng = this._latlng.lng * d2r;

      var cosCentreLat = Math.cos(centreLat);
      var sinCentreLat = Math.sin(centreLat);

      var cosRadRadius = Math.cos(radRadius);
      var sinRadRadius = Math.sin(radRadius);

      var calcLatLngAtAngle = function(angle) {
        var lat = Math.asin(sinCentreLat*cosRadRadius + cosCentreLat*sinRadRadius*Math.cos(angle));
        var lng = centreLng + Math.atan2(Math.sin(angle)*sinRadRadius*cosCentreLat, cosRadRadius-sinCentreLat*Math.sin(lat));

        return L.latLng(lat * r2d,lng * r2d);
      }

      var segments = Math.max(32,Math.floor(this._mRadius/1000));
//console.log(" (drawing circle as "+segments+" lines)");
      var points = [];
      for (var i=0; i<segments; i++) {
        var angle = Math.PI*2/segments*i;

        var point = calcLatLngAtAngle(angle)
        points.push ( point );
      }

      return points;
    },

  });

  L.geodesicPolyline = function (latlngs, options) {
    return new L.GeodesicPolyline(latlngs, options);
  };

  L.geodesicPolygon = function (latlngs, options) {
    return new L.GeodesicPolygon(latlngs, options);
  };

  /*
  L.geodesicMultiPolyline = function (latlngs, options) {
    return new L.GeodesicMultiPolyline(latlngs, options);
  };

  L.geodesicMultiPolygon = function (latlngs, options) {
    return new L.GeodesicMultiPolygon(latlngs, options);
  };

  */

  L.geodesicCircle = function (latlng, radius, options) {
    return new L.GeodesicCircle(latlng, radius, options);
  }

}());
<commit_msg>work-around for mobile-specific geodesic line drawing issue - appears to be an odd rounding problem not ideal, but better than the current situation<commit_after>/*
Geodesic extension to Leaflet library, by Fragger
https://github.com/Fragger/Leaflet.Geodesic
Version from master branch, dated Apr 26, 2013
Modified by qnstie 2013-07-17 to maintain compatibility with Leaflet.draw
*/
(function () {
  function geodesicPoly(Klass, fill) {
    return Klass.extend({
      initialize: function (latlngs, options) {
        Klass.prototype.initialize.call(this, L.geodesicConvertLines(latlngs, fill), options);
        this._latlngsinit = this._convertLatLngs(latlngs);
      },
      getLatLngs: function () {
        return this._latlngsinit;
      },
      setLatLngs: function (latlngs) {
        this._latlngsinit = this._convertLatLngs(latlngs);
        return this.redraw();
      },
      addLatLng: function (latlng) {
        this._latlngsinit.push(L.latLng(latlng));
        return this.redraw();
      },
      spliceLatLngs: function () { // (Number index, Number howMany)
        var removed = [].splice.apply(this._latlngsinit, arguments);
        this._convertLatLngs(this._latlngsinit);
        this.redraw();
        return removed;
      },
      redraw: function() {
        this._latlngs = this._convertLatLngs(L.geodesicConvertLines(this._latlngsinit, fill));
        return Klass.prototype.redraw.call(this);
      }
    });
  }

  function geodesicConvertLine(startLatlng, endLatlng, convertedPoints) {
    var i,
      R = 6378137, // earth radius in meters (doesn't have to be exact)
      maxlength = 5000, // meters before splitting
      d2r = L.LatLng.DEG_TO_RAD,
      r2d = L.LatLng.RAD_TO_DEG,
      lat1, lat2, lng1, lng2, dLng, d, segments,
      f, A, B, x, y, z, fLat, fLng;

    dLng = (endLatlng.lng - startLatlng.lng) * d2r;
    lat1 = startLatlng.lat * d2r;
    lat2 = endLatlng.lat * d2r;
    lng1 = startLatlng.lng * d2r;
    lng2 = endLatlng.lng * d2r;

    // http://en.wikipedia.org/wiki/Great-circle_distance
    d = Math.atan2(Math.sqrt( Math.pow(Math.cos(lat2) * Math.sin(dLng), 2) + Math.pow(Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng), 2) ), Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLng));

    segments = Math.ceil(d * R / maxlength);
    // loop starts at 1 - we don't add the very first point
    // loop ends before 'segments' is reached - we don't add the very last point here but outside the loop
    // (this was to fix a bug - https://github.com/jonatkins/ingress-intel-total-conversion/issues/471
    //  rounding errors? maths bug? not sure - but it solves the issue! and is a slight optimisation)
    // UPDATE: there still seem to be rounding errors on relatively short links - but only on mobile.
    // let's only add intermediate points if there's two or more
    if (segments >= 3) {
      for (i = 1; i < segments; i++) {
        // http://williams.best.vwh.net/avform.htm#Intermediate
        // modified to handle longitude above +-180 degrees
        f = i / segments;
        A = Math.sin((1-f)*d) / Math.sin(d);
        B = Math.sin(f*d) / Math.sin(d);
        x = A * Math.cos(lat1) * Math.cos(0) + B * Math.cos(lat2) * Math.cos(dLng);
        y = A * Math.cos(lat1) * Math.sin(0) + B * Math.cos(lat2) * Math.sin(dLng);
        z = A * Math.sin(lat1)               + B * Math.sin(lat2);
        fLat = r2d * Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
        fLng = r2d * (Math.atan2(y, x)+lng1);

        convertedPoints.push(L.latLng([fLat, fLng]));
      }
    }
    // push the final point unmodified
    convertedPoints.push(L.latLng(endLatlng));
  }

  L.geodesicConvertLines = function (latlngs, fill) {
    if (latlngs.length == 0) {
      return [];
    }

    for (var i = 0, len = latlngs.length; i < len; i++) {
      if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
        return;
      }
      latlngs[i] = L.latLng(latlngs[i]);
    }

    // geodesic calculations have issues when crossing the anti-meridian. so offset the points
    // so this isn't an issue, then add back the offset afterwards
    // a center longitude would be ideal - but the start point longitude will be 'good enough'
    var lngOffset = latlngs[0].lng;

    // points are wrapped after being offset relative to the first point coordinate, so they're
    // within +-180 degrees
    latlngs = latlngs.map(function(a){ return L.latLng(a.lat, a.lng-lngOffset).wrap(); });

    var geodesiclatlngs = [];

    if(!fill) {
      geodesiclatlngs.push(latlngs[0]);
    }
    for (i = 0, len = latlngs.length - 1; i < len; i++) {
      geodesicConvertLine(latlngs[i], latlngs[i+1], geodesiclatlngs);
    }
    if(fill) {
      geodesicConvertLine(latlngs[len], latlngs[0], geodesiclatlngs);
    }

    // now add back the offset subtracted above. no wrapping here - the drawing code handles
    // things better when there's no sudden jumps in coordinates. yes, lines will extend
    // beyond +-180 degrees - but they won't be 'broken'
    geodesiclatlngs = geodesiclatlngs.map(function(a){ return L.latLng(a.lat, a.lng+lngOffset); });

    return geodesiclatlngs;
  }

  L.GeodesicPolyline = geodesicPoly(L.Polyline, 0);
  L.GeodesicPolygon = geodesicPoly(L.Polygon, 1);

  //L.GeodesicMultiPolyline = createMulti(L.GeodesicPolyline);
  //L.GeodesicMultiPolygon = createMulti(L.GeodesicPolygon);

  /*L.GeodesicMultiPolyline = L.MultiPolyline.extend({
    initialize: function (latlngs, options) {
      L.MultiPolyline.prototype.initialize.call(this, L.geodesicConvertLines(latlngs), options);
    }
  });*/

  /*L.GeodesicMultiPolygon = L.MultiPolygon.extend({
    initialize: function (latlngs, options) {
      L.MultiPolygon.prototype.initialize.call(this, L.geodesicConvertLines(latlngs), options);
    }
  });*/

  L.GeodesicCircle = L.Polygon.extend({
    initialize: function (latlng, radius, options) {
      this._latlng = L.latLng(latlng);
      this._mRadius = radius;

      points = this._calcPoints();

      L.Polygon.prototype.initialize.call(this, points, options);
    },

    options: {
      fill: true
    },

    setLatLng: function (latlng) {
      this._latlng = L.latLng(latlng);
      points = this._calcPoints();
      this.setLatLngs(points);
    },

    setRadius: function (radius) {
      this._mRadius = radius;
      points = this._calcPoints();
      this.setLatLngs(points);

    },

    getLatLng: function () {
      return this._latlng;
    },

    getRadius: function() {
      return this._mRadius;
    },

    _calcPoints: function() {
      var R = 6378137; //earth radius in meters (approx - taken from leaflet source code)
      var d2r = L.LatLng.DEG_TO_RAD;
      var r2d = L.LatLng.RAD_TO_DEG;
//console.log("geodesicCircle: radius = "+this._mRadius+"m, centre "+this._latlng.lat+","+this._latlng.lng);

      // circle radius as an angle from the centre of the earth
      var radRadius = this._mRadius / R;

//console.log(" (radius in radians "+radRadius);

      // pre-calculate various values used for every point on the circle
      var centreLat = this._latlng.lat * d2r;
      var centreLng = this._latlng.lng * d2r;

      var cosCentreLat = Math.cos(centreLat);
      var sinCentreLat = Math.sin(centreLat);

      var cosRadRadius = Math.cos(radRadius);
      var sinRadRadius = Math.sin(radRadius);

      var calcLatLngAtAngle = function(angle) {
        var lat = Math.asin(sinCentreLat*cosRadRadius + cosCentreLat*sinRadRadius*Math.cos(angle));
        var lng = centreLng + Math.atan2(Math.sin(angle)*sinRadRadius*cosCentreLat, cosRadRadius-sinCentreLat*Math.sin(lat));

        return L.latLng(lat * r2d,lng * r2d);
      }

      var segments = Math.max(32,Math.floor(this._mRadius/1000));
//console.log(" (drawing circle as "+segments+" lines)");
      var points = [];
      for (var i=0; i<segments; i++) {
        var angle = Math.PI*2/segments*i;

        var point = calcLatLngAtAngle(angle)
        points.push ( point );
      }

      return points;
    },

  });

  L.geodesicPolyline = function (latlngs, options) {
    return new L.GeodesicPolyline(latlngs, options);
  };

  L.geodesicPolygon = function (latlngs, options) {
    return new L.GeodesicPolygon(latlngs, options);
  };

  /*
  L.geodesicMultiPolyline = function (latlngs, options) {
    return new L.GeodesicMultiPolyline(latlngs, options);
  };

  L.geodesicMultiPolygon = function (latlngs, options) {
    return new L.GeodesicMultiPolygon(latlngs, options);
  };

  */

  L.geodesicCircle = function (latlng, radius, options) {
    return new L.GeodesicCircle(latlng, radius, options);
  }

}());
<|endoftext|>