<commit_before>// Тесты с nodeunit
//https://github.com/caolan/nodeunit

exports.ConnectionLifecycle = {

    "'connecting' event": function(test) {
        // сколько будет тестов
        test.expect(1);

        var BabblerDevice = require('../src/babbler');
        var babbler = new BabblerDevice();

        babbler.on('connecting', function() {
            test.ok(true, "Should try to connect here");

            // закончили здесь
            test.done();
        });

        // подключаемся к устройству - ожидаем колбэки
        babbler.connect("/dev/xxx");
    },
    "Device does not exist": function(test) {
        // сколько будет тестов
        test.expect(3);

        var BabblerDevice = require('../src/babbler');
        var babbler = new BabblerDevice();

        babbler.on('connected', function() {
            test.ok(false, "Should not connect here");
        });

        babbler.on('connecting', function() {
            test.ok(true, "Should try to connect here");
        });

        babbler.on('disconnected', function(error) {
            test.ok(true, "Disconnected here");
            test.ok(error != undefined, "Error defined: " + error.message);

            // закончили здесь
            test.done();
        });

        // подключаемся к устройству - ожидаем колбэки
        babbler.connect("/dev/xxx");
    },
    "'connected'-'disconnected' events": function(test) {
        // сколько будет тестов
        test.expect(3);

        var BabblerDevice = require('../src/babbler');
        var babbler = new BabblerDevice();

        babbler.on('connected', function() {
            test.ok(true, "Connected ok");

            // подключились - отключаемся
            babbler.disconnect();
        });

        babbler.on('disconnected', function(error) {
            test.ok(true, "Disconnected ok");
            test.ok(error == undefined, "No errors");

            // закончили здесь
            test.done();
        });

        // подключаемся к устройству - ожидаем колбэки
        babbler.connect("/dev/ttyUSB0");
    },
    "Test commands": function(test) {
        // сколько будет тестов
        test.expect(9);

        var BabblerDevice = require('../src/babbler');
        var babbler = new BabblerDevice();

        babbler.on('connected', function() {
            test.ok(true, "Connected ok");

            // отправим существующую корректную команду
            babbler.sendCmd("ping", [],
                // onReply
                function(cmd, params, reply) {
                    test.ok(true, "Got reply");
                    test.equal(reply, "ok", "And reply is 'ok'");

                    test.equal(cmd, "ping", "cmd is 'ping'");
                    test.deepEqual(params, [], "and params are empty array");
                },
                // onError
                function(cmd, params, err) {
                    test.ok(false, "No errors");
                }
            );

            // отправим несуществующую некорректную команду
            babbler.sendCmd("pingzzz", ["hello"],
                // onReply
                function(cmd, params, reply) {
                    test.ok(true, "Got reply");
                    test.equal(reply, "dontunderstand", "And reply is 'dontunderstand'");

                    test.equal(cmd, "pingzzz", "cmd is 'pingzzz'");
                    test.deepEqual(params, ["hello"], "and params are ['hello'] array");

                    // отключаемся
                    babbler.disconnect();

                    // закончили здесь
                    test.done();
                },
                // onError
                function(cmd, params, err) {
                    test.ok(false, "No errors");
                }
            );
        });

        // подключаемся к устройству - ожидаем колбэки
        babbler.connect("/dev/ttyUSB0");
    }
};

//////////////////
// запускаем тесты
var reporter = require('nodeunit').reporters.verbose;
reporter.run(['test']);

<commit_msg>Тест для отправки команды после отключения<commit_after>// Тесты с nodeunit
//https://github.com/caolan/nodeunit

exports.ConnectionLifecycle = {

    "'connecting' event": function(test) {
        // сколько будет тестов
        test.expect(1);

        var BabblerDevice = require('../src/babbler');
        var babbler = new BabblerDevice();

        babbler.on('connecting', function() {
            test.ok(true, "Should try to connect here");

            // закончили здесь
            test.done();
        });

        // подключаемся к устройству - ожидаем колбэки
        babbler.connect("/dev/xxx");
    },
    "Device does not exist": function(test) {
        // сколько будет тестов
        test.expect(3);

        var BabblerDevice = require('../src/babbler');
        var babbler = new BabblerDevice();

        babbler.on('connected', function() {
            test.ok(false, "Should not connect here");
        });

        babbler.on('connecting', function() {
            test.ok(true, "Should try to connect here");
        });

        babbler.on('disconnected', function(err) {
            test.ok(true, "Disconnected here");
            test.ok(err != undefined, "Error defined: " + err.message);

            // закончили здесь
            test.done();
        });

        // подключаемся к устройству - ожидаем колбэки
        babbler.connect("/dev/xxx");
    },
    "'connected'-'disconnected' events": function(test) {
        // сколько будет тестов
        test.expect(3);

        var BabblerDevice = require('../src/babbler');
        var babbler = new BabblerDevice();

        babbler.on('connected', function() {
            test.ok(true, "Connected ok");

            // подключились - отключаемся
            babbler.disconnect();
        });

        babbler.on('disconnected', function(err) {
            test.ok(true, "Disconnected ok");
            test.ok(err == undefined, "No errors");

            // закончили здесь
            test.done();
        });

        // подключаемся к устройству - ожидаем колбэки
        babbler.connect("/dev/ttyUSB0");
    },
    "Test commands": function(test) {
        // сколько будет тестов
        test.expect(13);

        var BabblerDevice = require('../src/babbler');
        var babbler = new BabblerDevice();

        babbler.on('connected', function() {
            test.ok(true, "Connected ok");

            // отправим существующую корректную команду
            babbler.sendCmd("ping", [],
                // onReply
                function(cmd, params, reply) {
                    test.ok(true, "Got reply");
                    test.equal(reply, "ok", "And reply is 'ok'");

                    test.equal(cmd, "ping", "cmd is 'ping'");
                    test.deepEqual(params, [], "and params are empty array");
                },
                // onError
                function(cmd, params, err) {
                    test.ok(false, "No errors");
                }
            );

            // отправим несуществующую некорректную команду
            babbler.sendCmd("pingzzz", ["hello"],
                // onReply
                function(cmd, params, reply) {
                    test.ok(true, "Got reply");
                    test.equal(reply, "dontunderstand", "And reply is 'dontunderstand'");

                    test.equal(cmd, "pingzzz", "cmd is 'pingzzz'");
                    test.deepEqual(params, ["hello"], "and params are ['hello'] array");

                    // отключаемся
                    babbler.disconnect();
                },
                // onError
                function(cmd, params, err) {
                    test.ok(false, "No errors");
                }
            );

            // отправим команду после отключения
            babbler.sendCmd("ping", [],
                // onReply
                function(cmd, params, reply) {
                    test.ok(false, "No reply after disconnect");
                },
                // onError
                function(cmd, params, err) {
                    test.ok(true, "Got error");
                    test.ok(err != undefined, "Error defined: " + err.message);

                    test.equal(cmd, "ping", "cmd is 'ping'");
                    test.deepEqual(params, [], "and params are empty array");

                    // закончили здесь
                    test.done();
                }
            );
        });

        // подключаемся к устройству - ожидаем колбэки
        babbler.connect("/dev/ttyUSB0");
    }
};

//////////////////
// запускаем тесты
var reporter = require('nodeunit').reporters.verbose;
reporter.run(['test']);

<|endoftext|>


<commit_before>'use strict';

const isWorker = typeof importScripts !== 'undefined';

// var because must leak into globals
var module, exports, req;

// CommonJS shim for Web Worker
if (isWorker) {
  exports = self;

  req = (name, urlPrefix = 'https://unpkg.com/') => {
    let oldModule = module, oldExports = exports;
    exports = {};
    module = { exports };
    importScripts(urlPrefix + name);
    let exported = module.exports;
    module = oldModule;
    exports = oldExports;
    return exported;
  };
} else {
  req = require;
}

exports.parsers = {
  'Acorn (dev)'() {
    const { parse } = req('../../dist/acorn.js', '');
    return {
      version: '',
      parse: s => parse(s, { locations: true })
    };
  },

  'Acorn'() {
    const { version, parse } = req('acorn');
    return {
      version,
      parse: s => parse(s, { locations: true })
    };
  },

  'Esprima'() {
    const { version, parse } = req('esprima');
    return {
      version,
      parse: s => parse(s, { loc: true })
    }
  },

  'TypeScript'() {
    const { version, createSourceFile, ScriptTarget: { ES6 } } = req('typescript');
    return {
      version,
      parse: s => createSourceFile('source.js', s, ES6)
    };
  },

  'Traceur'() {
    req('traceur/bin/traceur.js'); // it creates a global :(
    const { SourceFile, Parser } = traceur.syntax;
    return {
      version: traceur.loader.TraceurLoader.prototype.version,
      parse: s => new Parser(new SourceFile('source.js', s)).parseScript()
    }
  },

  'Flow'() {
    const { parse } = req('flow-parser');
    return {
      version: isWorker ? '' : require('flow-parser/package.json').version,
      parse
    };
  },

  'Babylon'() {
    const { parse } = req('babylon');
    return {
      version: isWorker ? '' : require('babylon/package.json').version,
      parse
    };
  },
};

exports.parserNames = Object.keys(exports.parsers);

exports.inputNames = [
  'angular.js',
  'backbone.js',
  'ember.js',
  'jquery.js',
  'react-dom.js',
  'react.js'
];

let read;

if (isWorker) {
  read = name => fetch(name).then(response => response.text());
} else {
  const { readFile } = require('fs');

  read = name => new Promise((resolve, reject) => {
    readFile(`${__dirname}/${name}`, 'utf-8', (err, data) => {
      err ? reject(err) : resolve(data);
    });
  });
}

exports.inputs = Promise.all(
  exports.inputNames
  .map(name => read(`fixtures/${name}`))
);
<commit_msg>Fix test/bench require path<commit_after>'use strict';

const isWorker = typeof importScripts !== 'undefined';

// var because must leak into globals
var module, exports, req;

// CommonJS shim for Web Worker
if (isWorker) {
  exports = self;

  req = (name, urlPrefix = 'https://unpkg.com/') => {
    let oldModule = module, oldExports = exports;
    exports = {};
    module = { exports };
    importScripts(urlPrefix + name);
    let exported = module.exports;
    module = oldModule;
    exports = oldExports;
    return exported;
  };
} else {
  req = require;
}

exports.parsers = {
  'Acorn (dev)'() {
    const { parse } = req('../../acorn/dist/acorn.js', '');
    return {
      version: '',
      parse: s => parse(s, { locations: true })
    };
  },

  'Acorn'() {
    const { version, parse } = req('acorn');
    return {
      version,
      parse: s => parse(s, { locations: true })
    };
  },

  'Esprima'() {
    const { version, parse } = req('esprima');
    return {
      version,
      parse: s => parse(s, { loc: true })
    }
  },

  'TypeScript'() {
    const { version, createSourceFile, ScriptTarget: { ES6 } } = req('typescript');
    return {
      version,
      parse: s => createSourceFile('source.js', s, ES6)
    };
  },

  'Traceur'() {
    req('traceur/bin/traceur.js'); // it creates a global :(
    const { SourceFile, Parser } = traceur.syntax;
    return {
      version: traceur.loader.TraceurLoader.prototype.version,
      parse: s => new Parser(new SourceFile('source.js', s)).parseScript()
    }
  },

  'Flow'() {
    const { parse } = req('flow-parser');
    return {
      version: isWorker ? '' : require('flow-parser/package.json').version,
      parse
    };
  },

  'Babylon'() {
    const { parse } = req('babylon');
    return {
      version: isWorker ? '' : require('babylon/package.json').version,
      parse
    };
  },
};

exports.parserNames = Object.keys(exports.parsers);

exports.inputNames = [
  'angular.js',
  'backbone.js',
  'ember.js',
  'jquery.js',
  'react-dom.js',
  'react.js'
];

let read;

if (isWorker) {
  read = name => fetch(name).then(response => response.text());
} else {
  const { readFile } = require('fs');

  read = name => new Promise((resolve, reject) => {
    readFile(`${__dirname}/${name}`, 'utf-8', (err, data) => {
      err ? reject(err) : resolve(data);
    });
  });
}

exports.inputs = Promise.all(
  exports.inputNames
  .map(name => read(`fixtures/${name}`))
);
<|endoftext|>


<commit_before>/**
 * ngScope
 *
 * @author Anders D. Johnson
 * @url https://github.com/AndersDJohnson/ng-scope
 * @license MIT
 */
(function(angular, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['angular'], function(angular) {
      return factory(angular);
    });
  } else {
    return factory(angular);
  }
}(angular || null, function(angular) {

  /**
   * @ngdoc module
   * @name ngScope
   * @description ngScope
   * @example
     <doc:example>
       <doc:source>

       </doc:source>
     </doc:example>
     <doc:example>
       <doc:source>

       </doc:source>
     </doc:example>
   */
  var app = angular.module('ngScope', []);

  /**
   * @ngdoc directive
   * @name ngScope.directive:ngScope
   * @restrict A
   *
   * @description
   * Directive that sets up an inheriting child scope and extends with provided value.
   */
  app.directive('ngScope', [
    function () {
      return {
        restrict: 'A', // attribute 'ng-scope'
        scope: true, // extend parent scope
        link: function (scope, element, attrs) {
          // extend the scope with the evaluated attribute
          angular.extend(scope, scope.$eval(attrs.ngScope));
        }
      };
    }
  ]);

  return app;

}));
<commit_msg>update ng-scope<commit_after>/**
 * ngScope
 *
 * @author Anders D. Johnson
 * @url https://github.com/AndersDJohnson/ng-scope
 * @license MIT
 */
(function(angular, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['angular'], function(angular) {
      return factory(angular);
    });
  } else {
    return factory(angular);
  }
}(angular || null, function(angular) {

  /**
   * @ngdoc module
   * @name ngScope
   * @description ngScope
   * @example
     <doc:example>
       <doc:source>

       </doc:source>
     </doc:example>
     <doc:example>
       <doc:source>

       </doc:source>
     </doc:example>
   */
  var app = angular.module('ngScope', []);

  /**
   * @ngdoc directive
   * @name ngScope.directive:ngScope
   * @restrict A
   *
   * @description
   * Directive that sets up an inheriting child scope and extends with evaluated value of attribute.
   */
  app.directive('ngScope', [
    function () {
      return {
        restrict: 'A', // attribute 'ng-scope'
        scope: true, // extend parent scope
        link: function (scope, element, attrs) {
          // extend the scope with the evaluated attribute
          angular.extend(scope, scope.$eval(attrs.ngScope));
        }
      };
    }
  ]);

  return app;

}));
<|endoftext|>


<commit_before>[
{"id":"align_relative_to","title":"相對對齊 ..."},
{"id":"angle","title":"旋轉角度"},
{"id":"angleLabel","textContent":"角度:"},
{"id":"bkgnd_color","title":"更改背景顏色/不透明"},
{"id":"circle_cx","title":"改變圓的CX坐標"},
{"id":"circle_cy","title":"改變圓的CY坐標"},
{"id":"circle_r","title":"改變圓的半徑"},
{"id":"cornerRadiusLabel","textContent":"角半徑："},
{"id":"curve_segments","textContent":"曲線"},
{"id":"ellipse_cx","title":"改變橢圓的圓心x軸座標"},
{"id":"ellipse_cy","title":"改變橢圓的圓心y軸座標"},
{"id":"ellipse_rx","title":"改變橢圓的x軸長"},
{"id":"ellipse_ry","title":"改變橢圓的y軸長"},
{"id":"fill_color","title":"更改填充顏色"},
{"id":"fill_tool_bottom","textContent":"填充:"},
{"id":"fitToContent","textContent":"適合內容"},
{"id":"fit_to_all","textContent":"適合所有的內容"},
{"id":"fit_to_canvas","textContent":"適合畫布"},
{"id":"fit_to_layer_content","textContent":"適合圖層內容"},
{"id":"fit_to_sel","textContent":"適合選取的物件"},
{"id":"font_family","title":"更改字體"},
{"id":"font_size","title":"更改字體大小"},
{"id":"group_opacity","title":"更改所選項目不透明度"},
{"id":"icon_large","textContent":"大"},
{"id":"icon_medium","textContent":"中"},
{"id":"icon_small","textContent":"小"},
{"id":"icon_xlarge","textContent":"特大"},
{"id":"iheightLabel","textContent":"高度:"},
{"id":"image_height","title":"更改圖像高度"},
{"id":"image_opt_embed","textContent":"內嵌資料 (本地端檔案)"},
{"id":"image_opt_ref","textContent":"使用檔案參照"},
{"id":"image_url","title":"更改網址"},
{"id":"image_width","title":"更改圖像的寬度"},
{"id":"includedImages","textContent":"包含圖像"},
{"id":"iwidthLabel","textContent":"寬度:"},
{"id":"largest_object","textContent":"最大的物件"},
{"id":"layer_delete","title":"刪除圖層"},
{"id":"layer_down","title":"向下移動圖層"},
{"id":"layer_new","title":"新增圖層"},
{"id":"layer_rename","title":"重新命名圖層"},
{"id":"layer_up","title":"向上移動圖層"},
{"id":"layersLabel","textContent":"圖層:"},
{"id":"line_x1","title":"更改行的起點的x坐標"},
{"id":"line_x2","title":"更改行的終點x坐標"},
{"id":"line_y1","title":"更改行的起點的y坐標"},
{"id":"line_y2","title":"更改行的終點y坐標"},
{"id":"page","textContent":"網頁"},
{"id":"palette","title":"點擊更改填充顏色，按住Shift鍵單擊更改線條顏色"},
{"id":"path_node_x","title":"改變節點的x軸座標"},
{"id":"path_node_y","title":"改變節點的y軸座標"},
{"id":"rect_height","title":"更改矩形的高度"},
{"id":"rect_rx","title":"矩形角半徑的變化"},
{"id":"rect_width","title":"更改矩形的寬度"},
{"id":"relativeToLabel","textContent":"相對於:"},
{"id":"rheightLabel","textContent":"高度："},
{"id":"rwidthLabel","textContent":"寬度："},
{"id":"seg_type","title":"Change Segment type"},
{"id":"selLayerLabel","textContent":"移動物件到:"},
{"id":"selLayerNames","title":"移動被點選的物件其他圖層"},
{"id":"selectedPredefined","textContent":"使用預設值:"},
{"id":"selected_objects","textContent":"選取物件"},
{"id":"selected_x","title":"調整 X 軸"},
{"id":"selected_y","title":"調整 Y 軸"},
{"id":"smallest_object","textContent":"最小的物件"},
{"id":"straight_segments","textContent":"直線"},
{"id":"stroke_color","title":"線條顏色"},
{"id":"stroke_style","title":"更改線條（虛線）風格"},
{"id":"stroke_tool_bottom","textContent":"線條:"},
{"id":"stroke_width","title":"線條寬度"},
{"id":"svginfo_bg_note","textContent":"注意: 編輯器背景不會和圖像一起儲存"},
{"id":"svginfo_change_background","textContent":"編輯器背景"},
{"id":"svginfo_dim","textContent":"畫布大小"},
{"id":"svginfo_editor_prefs","textContent":"編輯器屬性"},
{"id":"svginfo_height","textContent":"高度:"},
{"id":"svginfo_icons","textContent":"圖示大小"},
{"id":"svginfo_image_props","textContent":"圖片屬性"},
{"id":"svginfo_lang","textContent":"語言"},
{"id":"svginfo_title","textContent":"標題"},
{"id":"svginfo_width","textContent":"寬度:"},
{"id":"text","title":"更改文字內容"},
{"id":"tool_alignbottom","title":"底部對齊"},
{"id":"tool_aligncenter","title":"居中對齊"},
{"id":"tool_alignleft","title":"向左對齊"},
{"id":"tool_alignmiddle","title":"中間對齊"},
{"id":"tool_alignright","title":"向右對齊"},
{"id":"tool_aligntop","title":"頂端對齊"},
{"id":"tool_bold","title":"粗體"},
{"id":"tool_circle","title":"圓"},
{"id":"tool_clear","title":"清空圖像"},
{"id":"tool_clone","title":"複製"},
{"id":"tool_clone_multi","title":"複製所選元素"},
{"id":"tool_delete","title":"刪除"},
{"id":"tool_delete_multi","title":"刪除所選元素"},
{"id":"tool_docprops","title":"文件屬性"},
{"id":"tool_docprops_cancel","textContent":"取消"},
{"id":"tool_docprops_save","textContent":"保存"},
{"id":"tool_ellipse","title":"橢圓"},
{"id":"tool_fhellipse","title":"徒手畫橢圓"},
{"id":"tool_fhpath","title":"鉛筆工具"},
{"id":"tool_fhrect","title":"徒手畫矩形"},
{"id":"tool_group","title":"群組"},
{"id":"tool_image","title":"圖像工具"},
{"id":"tool_italic","title":"斜體"},
{"id":"tool_line","title":"線工具"},
{"id":"tool_move_bottom","title":"移至底部"},
{"id":"tool_move_top","title":"移動到頂部"},
{"id":"tool_node_clone","title":"增加節點"},
{"id":"tool_node_delete","title":"刪除節點"},
{"id":"tool_node_link","title":"將控制點連起來"},
{"id":"tool_open","title":"打開圖像"},
{"id":"tool_path","title":"路徑工具"},
{"id":"tool_rect","title":"矩形"},
{"id":"tool_redo","title":"復原"},
{"id":"tool_reorient","title":"調整路徑"},
{"id":"tool_save","title":"保存圖像"},
{"id":"tool_select","title":"選擇工具"},
{"id":"tool_source","title":"編輯SVG原始碼"},
{"id":"tool_source_cancel","textContent":"取消"},
{"id":"tool_source_save","textContent":"保存"},
{"id":"tool_square","title":"方形"},
{"id":"tool_text","title":"文字工具"},
{"id":"tool_topath","title":"轉換成路徑"},
{"id":"tool_undo","title":"取消復原"},
{"id":"tool_ungroup","title":"取消群組"},
{"id":"tool_wireframe","title":"框線模式（只瀏覽線條）"},
{"id":"tool_zoom","title":"縮放工具"},
{"id":"tools_ellipse_show","title":"橢圓/圓工具"},
{"id":"tools_rect_show","title":"矩形/方形工具"},
{"id":"zoom","title":"更改縮放級別"},
{"id":"zoomLabel","textContent":"調整頁面大小:"},
{"id":"sidepanel_handle","textContent":"圖層","title":"拖拉以改變側邊面板的大小"},
{
 "js_strings": {
  "QerrorsRevertToSource":"SVG原始碼解析錯誤\n要回復到原本的SVG原始碼嗎？",
	"QignoreSourceChanges":"要忽略對SVG原始碼的更動嗎？",
  "QmoveElemsToLayer":"要搬移所選取的物件到'%s'層嗎?",
	"QwantToClear":"要清空圖像嗎？\n這會順便清空你的回復紀錄！",
	"cancel":"取消",
	"dupeLayerName":"喔不！已經有另一個同樣名稱的圖層了！",
	"enterNewImgURL":"輸入新的圖片網址",
	"enterUniqueLayerName":"請輸入一個名稱不重複的",
	"enterNewLayerName":"請輸入新圖層的名稱",
	"featNotSupported":"未提供此功能",
	"invalidAttrValGiven":"數值給定錯誤",
	"key_backspace":"空白",
	"key_del":"刪除",
	"key_down":"下",
	"key_up":"上",
	"layer":"圖層",
	"layerHasThatName":"圖層本來就是這個名稱（抱怨）",
	"noContentToFitTo":"找不到符合的內容",
	"ok":"確定",
	"pathCtrlPtTooltip":"拖拉控制點以改變曲線性質",
  "pathNodeTooltip":"拖拉節點以移動， 連擊節點以改變線段型態（直線/曲線）"
 }
}
]<commit_msg>Main menu items in zh-TW now display in Chinese. Also remove _show entries no longer needed. Thank you Mook!<commit_after>[
{"id":"align_relative_to","title":"相對對齊 ..."},
{"id":"angle","title":"旋轉角度"},
{"id":"angleLabel","textContent":"角度:"},
{"id":"bkgnd_color","title":"更改背景顏色/不透明"},
{"id":"circle_cx","title":"改變圓的CX坐標"},
{"id":"circle_cy","title":"改變圓的CY坐標"},
{"id":"circle_r","title":"改變圓的半徑"},
{"id":"cornerRadiusLabel","textContent":"角半徑："},
{"id":"curve_segments","textContent":"曲線"},
{"id":"ellipse_cx","title":"改變橢圓的圓心x軸座標"},
{"id":"ellipse_cy","title":"改變橢圓的圓心y軸座標"},
{"id":"ellipse_rx","title":"改變橢圓的x軸長"},
{"id":"ellipse_ry","title":"改變橢圓的y軸長"},
{"id":"fill_color","title":"更改填充顏色"},
{"id":"fill_tool_bottom","textContent":"填充:"},
{"id":"fitToContent","textContent":"適合內容"},
{"id":"fit_to_all","textContent":"適合所有的內容"},
{"id":"fit_to_canvas","textContent":"適合畫布"},
{"id":"fit_to_layer_content","textContent":"適合圖層內容"},
{"id":"fit_to_sel","textContent":"適合選取的物件"},
{"id":"font_family","title":"更改字體"},
{"id":"font_size","title":"更改字體大小"},
{"id":"group_opacity","title":"更改所選項目不透明度"},
{"id":"icon_large","textContent":"大"},
{"id":"icon_medium","textContent":"中"},
{"id":"icon_small","textContent":"小"},
{"id":"icon_xlarge","textContent":"特大"},
{"id":"iheightLabel","textContent":"高度:"},
{"id":"image_height","title":"更改圖像高度"},
{"id":"image_opt_embed","textContent":"內嵌資料 (本地端檔案)"},
{"id":"image_opt_ref","textContent":"使用檔案參照"},
{"id":"image_url","title":"更改網址"},
{"id":"image_width","title":"更改圖像的寬度"},
{"id":"includedImages","textContent":"包含圖像"},
{"id":"iwidthLabel","textContent":"寬度:"},
{"id":"largest_object","textContent":"最大的物件"},
{"id":"layer_delete","title":"刪除圖層"},
{"id":"layer_down","title":"向下移動圖層"},
{"id":"layer_new","title":"新增圖層"},
{"id":"layer_rename","title":"重新命名圖層"},
{"id":"layer_up","title":"向上移動圖層"},
{"id":"layersLabel","textContent":"圖層:"},
{"id":"line_x1","title":"更改行的起點的x坐標"},
{"id":"line_x2","title":"更改行的終點x坐標"},
{"id":"line_y1","title":"更改行的起點的y坐標"},
{"id":"line_y2","title":"更改行的終點y坐標"},
{"id":"page","textContent":"網頁"},
{"id":"palette","title":"點擊更改填充顏色，按住Shift鍵單擊更改線條顏色"},
{"id":"path_node_x","title":"改變節點的x軸座標"},
{"id":"path_node_y","title":"改變節點的y軸座標"},
{"id":"rect_height","title":"更改矩形的高度"},
{"id":"rect_rx","title":"矩形角半徑的變化"},
{"id":"rect_width","title":"更改矩形的寬度"},
{"id":"relativeToLabel","textContent":"相對於:"},
{"id":"rheightLabel","textContent":"高度："},
{"id":"rwidthLabel","textContent":"寬度："},
{"id":"seg_type","title":"Change Segment type"},
{"id":"selLayerLabel","textContent":"移動物件到:"},
{"id":"selLayerNames","title":"移動被點選的物件其他圖層"},
{"id":"selectedPredefined","textContent":"使用預設值:"},
{"id":"selected_objects","textContent":"選取物件"},
{"id":"selected_x","title":"調整 X 軸"},
{"id":"selected_y","title":"調整 Y 軸"},
{"id":"smallest_object","textContent":"最小的物件"},
{"id":"straight_segments","textContent":"直線"},
{"id":"stroke_color","title":"線條顏色"},
{"id":"stroke_style","title":"更改線條（虛線）風格"},
{"id":"stroke_tool_bottom","textContent":"線條:"},
{"id":"stroke_width","title":"線條寬度"},
{"id":"svginfo_bg_note","textContent":"注意: 編輯器背景不會和圖像一起儲存"},
{"id":"svginfo_change_background","textContent":"編輯器背景"},
{"id":"svginfo_dim","textContent":"畫布大小"},
{"id":"svginfo_editor_prefs","textContent":"編輯器屬性"},
{"id":"svginfo_height","textContent":"高度:"},
{"id":"svginfo_icons","textContent":"圖示大小"},
{"id":"svginfo_image_props","textContent":"圖片屬性"},
{"id":"svginfo_lang","textContent":"語言"},
{"id":"svginfo_title","textContent":"標題"},
{"id":"svginfo_width","textContent":"寬度:"},
{"id":"text","title":"更改文字內容"},
{"id":"tool_alignbottom","title":"底部對齊"},
{"id":"tool_aligncenter","title":"居中對齊"},
{"id":"tool_alignleft","title":"向左對齊"},
{"id":"tool_alignmiddle","title":"中間對齊"},
{"id":"tool_alignright","title":"向右對齊"},
{"id":"tool_aligntop","title":"頂端對齊"},
{"id":"tool_bold","title":"粗體"},
{"id":"tool_circle","title":"圓"},
{"id":"tool_clear","textContent":"清空圖像"},
{"id":"tool_clone","title":"複製"},
{"id":"tool_clone_multi","title":"複製所選元素"},
{"id":"tool_delete","title":"刪除"},
{"id":"tool_delete_multi","title":"刪除所選元素"},
{"id":"tool_docprops","textContent":"文件屬性"},
{"id":"tool_docprops_cancel","textContent":"取消"},
{"id":"tool_docprops_save","textContent":"保存"},
{"id":"tool_ellipse","title":"橢圓"},
{"id":"tool_fhellipse","title":"徒手畫橢圓"},
{"id":"tool_fhpath","title":"鉛筆工具"},
{"id":"tool_fhrect","title":"徒手畫矩形"},
{"id":"tool_group","title":"群組"},
{"id":"tool_image","title":"圖像工具"},
{"id":"tool_italic","title":"斜體"},
{"id":"tool_line","title":"線工具"},
{"id":"tool_move_bottom","title":"移至底部"},
{"id":"tool_move_top","title":"移動到頂部"},
{"id":"tool_node_clone","title":"增加節點"},
{"id":"tool_node_delete","title":"刪除節點"},
{"id":"tool_node_link","title":"將控制點連起來"},
{"id":"tool_open","textContent":"打開圖像"},
{"id":"tool_path","title":"路徑工具"},
{"id":"tool_rect","title":"矩形"},
{"id":"tool_redo","title":"復原"},
{"id":"tool_reorient","title":"調整路徑"},
{"id":"tool_save","textContent":"保存圖像"},
{"id":"tool_select","title":"選擇工具"},
{"id":"tool_source","title":"編輯SVG原始碼"},
{"id":"tool_source_cancel","textContent":"取消"},
{"id":"tool_source_save","textContent":"保存"},
{"id":"tool_square","title":"方形"},
{"id":"tool_text","title":"文字工具"},
{"id":"tool_topath","title":"轉換成路徑"},
{"id":"tool_undo","title":"取消復原"},
{"id":"tool_ungroup","title":"取消群組"},
{"id":"tool_wireframe","title":"框線模式（只瀏覽線條）"},
{"id":"tool_zoom","title":"縮放工具"},
{"id":"zoom","title":"更改縮放級別"},
{"id":"zoomLabel","textContent":"調整頁面大小:"},
{"id":"sidepanel_handle","textContent":"圖層","title":"拖拉以改變側邊面板的大小"},
{
 "js_strings": {
  "QerrorsRevertToSource":"SVG原始碼解析錯誤\n要回復到原本的SVG原始碼嗎？",
	"QignoreSourceChanges":"要忽略對SVG原始碼的更動嗎？",
  "QmoveElemsToLayer":"要搬移所選取的物件到'%s'層嗎?",
	"QwantToClear":"要清空圖像嗎？\n這會順便清空你的回復紀錄！",
	"cancel":"取消",
	"dupeLayerName":"喔不！已經有另一個同樣名稱的圖層了！",
	"enterNewImgURL":"輸入新的圖片網址",
	"enterUniqueLayerName":"請輸入一個名稱不重複的",
	"enterNewLayerName":"請輸入新圖層的名稱",
	"featNotSupported":"未提供此功能",
	"invalidAttrValGiven":"數值給定錯誤",
	"key_backspace":"空白",
	"key_del":"刪除",
	"key_down":"下",
	"key_up":"上",
	"layer":"圖層",
	"layerHasThatName":"圖層本來就是這個名稱（抱怨）",
	"noContentToFitTo":"找不到符合的內容",
	"ok":"確定",
	"pathCtrlPtTooltip":"拖拉控制點以改變曲線性質",
  "pathNodeTooltip":"拖拉節點以移動， 連擊節點以改變線段型態（直線/曲線）"
 }
}
]<|endoftext|>


<commit_before>/*

Nitro SDK (can also be used with other recent BBC APIs)

*/

var http = require('http');
var https = require('https');
var url = require('url');
var util = require('util');

var debuglog = util.debuglog('bbc');

var api = require('./nitroApi/api.js');

String.prototype.toCamelCase = function camelize() {
	return this.toLowerCase().replace(/[-_ \/\.](.)/g, function(match, group1) {
		return group1.toUpperCase();
    });
}

var dest = {};
var rateLimitEvents = 0;
var inFlight = 0;
var stacked = 0;

function query() {
	this.querystring = '';
	this.add = function(param,value,previous) {
		this.querystring += (this.querystring || previous===true ? '&' : '?')+param;
		if (value) this.querystring += '=' + encodeURIComponent(value);
		return this;
	};
	this.reset = function() {
		this.querystring = '';
		return this;
	};
	this.clone = function() {
		var newQ = new query();
		newQ.querystring = this.querystring;
		return newQ;
	}
	this.toString = function() {
		return this.querystring;
	}
}

function nitroRawEpisode(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/episodes/'+pid;
}

function nitroRawEpisodeGenreGroups(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/episodes/'+pid+'/genre_groups';
}

function nitroRawEpisodeFormats(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/episodes/'+pid+'/formats';
}

function nitroRawEpisodeAncestors(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/episodes/'+pid+'/ancestors';
}

function nitroRawMasterBrand(mbid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/master_brands/'+mbid;
}

function nitroRawBrand(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/brands/'+pid;
}

function nitroRawBrandFranchises(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/brands/'+pid+'/franchises';
}

function nitroRawPromotion(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/promotions/'+pid;
}

function hasHeader(header, headers) {
	// snaffled from request module
	var headers = Object.keys(headers || this.headers),
		lheaders = headers.map(function (h) {return h.toLowerCase();});
	header = header.toLowerCase();
	for (var i=0;i<lheaders.length;i++) {
		if (lheaders[i] === header) return headers[i];
	}
	return false;
}

function makeRequest(host,path,key,query,settings,callback,err){
	inFlight++;
	debuglog(host+path+(key ? '?K' : '')+query.querystring);

	var defaults = {
		Accept: 'application/json',
		User_Agent: 'BBCiPlayerRadio/1.6.1.1522345 (SM-N900; Android 4.4.2)',
		api_key_name: 'api_key',
		proto: 'http',
		port: null,
		backoff : 31000,
		payload: {}
	}

	settings = Object.assign({},defaults,settings); // merge/extend

	var qs = query.querystring;
	if (key) {
		qs = '?' + settings.api_key_name + '=' + encodeURIComponent(key) + qs;
	}

	var options = {
	  hostname: host,
	  port: settings.port ? settings.port : (settings.proto == 'http' ? 80 : 443),
	  path: path+qs,
	  method: 'GET',
	  headers: { 'Accept': settings.Accept,
		'User-Agent': settings.User_Agent
	  }
	};

	var list = '';
	var obj;
	var json = (settings.Accept == 'application/json');
	var proto = (settings.proto == 'http' ? http : https);

	var req = proto.request(options, function(res) {
	  res.setEncoding('utf8');
	  res.on('data', function (data) {
		   list += data;
	  });
	  res.on('end', function() {
		inFlight--;
		if (res.statusCode >= 300 && res.statusCode < 400 && hasHeader('location', res.headers)) {
			// handle redirects, as per request module
			var location = res.headers[hasHeader('location', res.headers)];
			var locUrl = url.parse(location);
			host = locUrl.host;
			path = locUrl.pathname;
			makeRequest(host,path,key,query,settings,callback,err);
		}
		else if (res.statusCode >= 400 && res.statusCode < 600) {
			if (list) {
				try {
					if (json) {
						obj = JSON.parse(list);
					}
					else {
						obj = list;
					}

					if (typeof err !== 'undefined') err(res.statusCode,obj);

					if (json && obj.fault) {
						// process Apigee rate-limiting
						if (obj.fault.detail && obj.fault.detail.errorcode) {
							if (obj.fault.detail.errorcode == 'policies.ratelimit.QuotaViolation') {
								rateLimitEvents++;
								// rate limiting, back off by configured amount
								stacked++;
								setTimeout(function(){
									stacked--;
									makeRequest(host,path,key,query,settings,callback)
								},settings.backoff);
							}
							else {
								log_fault(obj,res,query); // rate limits leak keys in error messages
							}
						}
					}
					else if (json && obj.errors) {
						log_error(obj,res,query);
					}
					else {
						log_error(null,res,query);
					}
				}
				catch (e) {
					if (typeof err !== 'undefined') err(res.statusCode,list);
					console.log(e);
					console.log('Invalid JSON received:');
					console.log(list);
				}
			}
			else {
				if (typeof err !== 'undefined') err(res.statusCode,null);
				log_error(null,res,query);
			}
		}
		else try {
			if (json) {
				obj = JSON.parse(list);
			}
			else {
				obj = list;
			}
			var result = callback(obj,settings.payload);
			if (dest.callback) {
				// call the callback's next required destination
				// e.g. second and subsequent pages
				if (dest.path) {
					makeRequest(host,dest.path,key,dest.query,settings,dest.callback,dest.err);
				}
				else {
					dest.callback({},settings.payload);
				}
			}
		}
		catch (e) {
			console.log('Something went wrong parsing the response JSON');
			console.log(e);
			console.log(res.statusCode+' '+res.statusMessage);
			console.log(res.headers);
			debuglog('** '+list);
		}
	   });
	});
	req.on('error', function(e) {
	  console.log('Problem with request: ' + e.message);
	});
	req.end();
}

function log_fault(fault,res,query) {
	/*
	{ "fault": {
		"faultstring": "Rate limit quota violation. Quota limit : 0 exceeded by 1. Total violation count : 1. Identifier : YOUR-API-KEY-HERE",
		"detail":
			{"errorcode": “policies.ratelimit.QuotaViolation"}
		}
	}
	*/
	if (res) console.log('Fault: '+res.statusCode+' '+res.statusMessage);
	if (query && query.toString()) console.log(query.querystring);
	if (fault) console.log(fault.fault.detail.errorcode+': '+fault.fault.faultstring);
}

function log_error(error,res,query) {
	/*
	{"errors":{"error":{"code":"XDMP-EXTIME","message":"Time limit exceeded"}}}
	*/
	if (res) console.log('Error: '+res.statusCode+' '+res.statusMessage);
	if (query && query.toString()) console.log(query.querystring);
	if (error) console.log(error.errors.error.code+': '+error.errors.error.message);
}

module.exports = {

	setReturn : function(destination) {
		dest = destination;
	},

	getReturn : function(){
		return dest;
	},

	logFault : function(fault,res,query) {
		log_fault(fault,res,query);
	},

	logError : function(error,res,query) {
		log_error(error,res,query);
	},

	hasHeader : hasHeader,

	make_request : function(host,path,key,query,settings,callback,err) {
		makeRequest(host,path,key,query,settings,callback,err);
	},

	ping : function(host,key,settings,callback) {
		var q = new query();
		makeRequest(host,nitroRawMasterBrand('bbc_radio_four'),key,q,settings,callback);
	},

	getRateLimitEvents : function() {
		return rateLimitEvents;
	},

	resetRateLimitEvents : function() {
		rateLimitEvents = 0;
	},

	getRequests : function() {
		return inFlight+stacked;
	},

	newQuery : function(param,value,previous) {
		q = new query();
		if (param) q.add(param,value,previous);
		return q;
	},

	queryFrom : function(url,previous) {
		q = new query();
		if (url.indexOf('?')>=0) {
			url = url.split('?')[1];
		}
		else if (url.indexOf('&')>=0) {
			url = url.split('&')[1];
		}
		q.querystring = ((previous ? '&' : '?')+url);
		return q;
	},

	iso8601durationToSeconds : function(input) {
		var reptms = /^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/;
		var hours = 0, minutes = 0, seconds = 0, totalseconds;

		if (reptms.test(input)) {
			var matches = reptms.exec(input);
			if (matches[1]) hours = Number(matches[1]);
			if (matches[2]) minutes = Number(matches[2]);
			if (matches[3]) seconds = Number(matches[3]);
			totalseconds = hours * 3600 + minutes * 60 + seconds;
		}
		return totalseconds;
	},

	nitroRawEpisode : nitroRawEpisode,

	nitroRawEpisodeGenreGroups : nitroRawEpisodeGenreGroups,

	nitroRawEpisodeFormats : nitroRawEpisodeFormats,

	nitroRawEpisodeAncestors : nitroRawEpisodeAncestors,

	nitroRawMasterBrand : nitroRawMasterBrand,

	nitroRawBrand : nitroRawBrand,

	nitroRawBrandFranchises : nitroRawBrandFranchises,

	nitroRawPromotion : nitroRawPromotion

}
<commit_msg>nitroSdk; set up request agents using keepAlive true<commit_after>/*

Nitro SDK (can also be used with other recent BBC APIs)

*/

var http = require('http');
var https = require('https');
var url = require('url');
var util = require('util');

var debuglog = util.debuglog('bbc');

var api = require('./nitroApi/api.js');

String.prototype.toCamelCase = function camelize() {
	return this.toLowerCase().replace(/[-_ \/\.](.)/g, function(match, group1) {
		return group1.toUpperCase();
    });
}

var httpAgent;
var httpsAgent;
var dest = {};
var rateLimitEvents = 0;
var inFlight = 0;
var stacked = 0;

function query() {
	this.querystring = '';
	this.add = function(param,value,previous) {
		this.querystring += (this.querystring || previous===true ? '&' : '?')+param;
		if (value) this.querystring += '=' + encodeURIComponent(value);
		return this;
	};
	this.reset = function() {
		this.querystring = '';
		return this;
	};
	this.clone = function() {
		var newQ = new query();
		newQ.querystring = this.querystring;
		return newQ;
	}
	this.toString = function() {
		return this.querystring;
	}
}

function nitroRawEpisode(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/episodes/'+pid;
}

function nitroRawEpisodeGenreGroups(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/episodes/'+pid+'/genre_groups';
}

function nitroRawEpisodeFormats(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/episodes/'+pid+'/formats';
}

function nitroRawEpisodeAncestors(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/episodes/'+pid+'/ancestors';
}

function nitroRawMasterBrand(mbid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/master_brands/'+mbid;
}

function nitroRawBrand(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/brands/'+pid;
}

function nitroRawBrandFranchises(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/brands/'+pid+'/franchises';
}

function nitroRawPromotion(pid, version) {
	if (!version) version = 1;
	return '/nitro/api/v'+version+'/promotions/'+pid;
}

function hasHeader(header, headers) {
	// snaffled from request module
	var headers = Object.keys(headers || this.headers),
		lheaders = headers.map(function (h) {return h.toLowerCase();});
	header = header.toLowerCase();
	for (var i=0;i<lheaders.length;i++) {
		if (lheaders[i] === header) return headers[i];
	}
	return false;
}

function makeRequest(host,path,key,query,settings,callback,err){
	inFlight++;
	debuglog(host+path+(key ? '?K' : '')+query.querystring);

	var defaults = {
		Accept: 'application/json',
		User_Agent: 'BBCiPlayerRadio/1.6.1.1522345 (SM-N900; Android 4.4.2)',
		api_key_name: 'api_key',
		proto: 'http',
		port: null,
		backoff : 31000,
		timeout : 120000,
		payload: {}
	}

	settings = Object.assign({},defaults,settings); // merge/extend

	var qs = query.querystring;
	if (key) {
		qs = '?' + settings.api_key_name + '=' + encodeURIComponent(key) + qs;
	}

	var options = {
	  hostname: host,
	  port: settings.port ? settings.port : (settings.proto == 'http' ? 80 : 443),
	  path: path+qs,
	  method: 'GET',
	  headers: { 'Accept': settings.Accept,
		'User-Agent': settings.User_Agent
	  }
	};
	var proto = (settings.proto == 'http' ? http : https);
	if (settings.proto == 'http') {
		if (!httpAgent) {
			httpAgent = new http.Agent({ keepAlive: true });
		}
		options.agent = httpAgent;
	}
	else {
		if (!httpsAgent) {
			httpsAgent = new https.Agent({ keepAlive: true });
		}
		options.agent = httpsAgent;
	}

	var list = '';
	var obj;
	var json = (settings.Accept == 'application/json');

	var req = proto.request(options, function(res) {
	  res.setEncoding('utf8');
	  res.on('data', function (data) {
		   list += data;
	  });
	  res.on('end', function() {
		inFlight--;
		if (res.statusCode >= 300 && res.statusCode < 400 && hasHeader('location', res.headers)) {
			// handle redirects, as per request module
			var location = res.headers[hasHeader('location', res.headers)];
			var locUrl = url.parse(location);
			host = locUrl.host;
			path = locUrl.pathname;
			makeRequest(host,path,key,query,settings,callback,err);
		}
		else if (res.statusCode >= 400 && res.statusCode < 600) {
			if (list) {
				try {
					if (json) {
						obj = JSON.parse(list);
					}
					else {
						obj = list;
					}

					if (typeof err !== 'undefined') err(res.statusCode,obj);

					if (json && obj.fault) {
						// process Apigee rate-limiting
						if (obj.fault.detail && obj.fault.detail.errorcode) {
							if (obj.fault.detail.errorcode == 'policies.ratelimit.QuotaViolation') {
								rateLimitEvents++;
								// rate limiting, back off by configured amount
								stacked++;
								setTimeout(function(){
									stacked--;
									makeRequest(host,path,key,query,settings,callback)
								},settings.backoff);
							}
							else {
								log_fault(obj,res,query); // rate limits leak keys in error messages
							}
						}
					}
					else if (json && obj.errors) {
						log_error(obj,res,query);
					}
					else {
						log_error(null,res,query);
					}
				}
				catch (e) {
					if (typeof err !== 'undefined') err(res.statusCode,list);
					console.log(e);
					console.log('Invalid JSON received:');
					console.log(list);
				}
			}
			else {
				if (typeof err !== 'undefined') err(res.statusCode,null);
				log_error(null,res,query);
			}
		}
		else try {
			if (json) {
				obj = JSON.parse(list);
			}
			else {
				obj = list;
			}
			var result = callback(obj,settings.payload);
			if (dest.callback) {
				// call the callback's next required destination
				// e.g. second and subsequent pages
				if (dest.path) {
					makeRequest(host,dest.path,key,dest.query,settings,dest.callback,dest.err);
				}
				else {
					dest.callback({},settings.payload);
				}
			}
		}
		catch (e) {
			console.log('Something went wrong parsing the response JSON');
			console.log(e);
			console.log(res.statusCode+' '+res.statusMessage);
			console.log(res.headers);
			debuglog('** '+list);
		}
	   });
	}).setTimeout(settings.timeout);
	req.on('error', function(e) {
	  console.log('Problem with request: ' + e.message);
	  if (list) console.log(list);
	});
	req.end();
}

function log_fault(fault,res,query) {
	/*
	{ "fault": {
		"faultstring": "Rate limit quota violation. Quota limit : 0 exceeded by 1. Total violation count : 1. Identifier : YOUR-API-KEY-HERE",
		"detail":
			{"errorcode": “policies.ratelimit.QuotaViolation"}
		}
	}
	*/
	if (res) console.log('Fault: '+res.statusCode+' '+res.statusMessage);
	if (query && query.toString()) console.log(query.querystring);
	if (fault) console.log(fault.fault.detail.errorcode+': '+fault.fault.faultstring);
}

function log_error(error,res,query) {
	/*
	{"errors":{"error":{"code":"XDMP-EXTIME","message":"Time limit exceeded"}}}
	*/
	if (res) console.log('Error: '+res.statusCode+' '+res.statusMessage);
	if (query && query.toString()) console.log(query.querystring);
	if (error) console.log(error.errors.error.code+': '+error.errors.error.message);
}

module.exports = {

	setReturn : function(destination) {
		dest = destination;
	},

	getReturn : function(){
		return dest;
	},

	logFault : function(fault,res,query) {
		log_fault(fault,res,query);
	},

	logError : function(error,res,query) {
		log_error(error,res,query);
	},

	hasHeader : hasHeader,

	make_request : function(host,path,key,query,settings,callback,err) {
		makeRequest(host,path,key,query,settings,callback,err);
	},

	ping : function(host,key,settings,callback) {
		var q = new query();
		makeRequest(host,nitroRawMasterBrand('bbc_radio_four'),key,q,settings,callback);
	},

	getRateLimitEvents : function() {
		return rateLimitEvents;
	},

	resetRateLimitEvents : function() {
		rateLimitEvents = 0;
	},

	getRequests : function() {
		return inFlight+stacked;
	},

	newQuery : function(param,value,previous) {
		q = new query();
		if (param) q.add(param,value,previous);
		return q;
	},

	queryFrom : function(url,previous) {
		q = new query();
		if (url.indexOf('?')>=0) {
			url = url.split('?')[1];
		}
		else if (url.indexOf('&')>=0) {
			url = url.split('&')[1];
		}
		q.querystring = ((previous ? '&' : '?')+url);
		return q;
	},

	iso8601durationToSeconds : function(input) {
		var reptms = /^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/;
		var hours = 0, minutes = 0, seconds = 0, totalseconds;

		if (reptms.test(input)) {
			var matches = reptms.exec(input);
			if (matches[1]) hours = Number(matches[1]);
			if (matches[2]) minutes = Number(matches[2]);
			if (matches[3]) seconds = Number(matches[3]);
			totalseconds = hours * 3600 + minutes * 60 + seconds;
		}
		return totalseconds;
	},

	nitroRawEpisode : nitroRawEpisode,

	nitroRawEpisodeGenreGroups : nitroRawEpisodeGenreGroups,

	nitroRawEpisodeFormats : nitroRawEpisodeFormats,

	nitroRawEpisodeAncestors : nitroRawEpisodeAncestors,

	nitroRawMasterBrand : nitroRawMasterBrand,

	nitroRawBrand : nitroRawBrand,

	nitroRawBrandFranchises : nitroRawBrandFranchises,

	nitroRawPromotion : nitroRawPromotion

}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule TouchableOpacity
 */
'use strict';

// Note (avik): add @flow when Flow supports spread properties in propTypes

var NativeMethodsMixin = require('NativeMethodsMixin');
var POPAnimationMixin = require('POPAnimationMixin');
var React = require('React');
var Touchable = require('Touchable');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');

var cloneWithProps = require('cloneWithProps');
var ensureComponentIsNative = require('ensureComponentIsNative');
var keyOf = require('keyOf');
var onlyChild = require('onlyChild');

/**
 * A wrapper for making views respond properly to touches.
 * On press down, the opacity of the wrapped view is decreased, dimming it.
 * This is done without actually changing the view hierarchy, and in general is
 * easy to add to an app without weird side-effects.
 *
 * Example:
 *
 * ```
 * renderButton: function() {
 *   return (
 *     <TouchableOpacity onPress={this._onPressButton}>
 *       <Image
 *         style={styles.button}
 *         source={require('image!myButton')}
 *       />
 *     </TouchableOpacity>
 *   );
 * },
 * ```
 */

var TouchableOpacity = React.createClass({
  mixins: [Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],

  propTypes: {
    ...TouchableWithoutFeedback.propTypes,
    /**
     * Determines what the opacity of the wrapped view should be when touch is
     * active.
     */
    activeOpacity: React.PropTypes.number,
  },

  getDefaultProps: function() {
    return {
      activeOpacity: 0.5,
    };
  },

  getInitialState: function() {
    return this.touchableGetInitialState();
  },

  componentDidMount: function() {
    ensureComponentIsNative(this.refs[CHILD_REF]);
  },

  componentDidUpdate: function() {
    ensureComponentIsNative(this.refs[CHILD_REF]);
  },

  setOpacityTo: function(value) {
    if (POPAnimationMixin) {
      // Reset with animation if POP is available
      this.stopAllAnimations();
      var anim = {
        type: this.AnimationTypes.linear,
        property: this.AnimationProperties.opacity,
        toValue: value,
      };
      this.startAnimation(CHILD_REF, anim);
    } else {
      // Reset immediately if POP is unavailable
      this.refs[CHILD_REF].setNativeProps({
        opacity: value
      });
    }
  },

  /**
   * `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
   * defined on your component.
   */
  touchableHandleActivePressIn: function() {
    this.refs[CHILD_REF].setNativeProps({
      opacity: this.props.activeOpacity
    });
    this.props.onPressIn && this.props.onPressIn();
  },

  touchableHandleActivePressOut: function() {
    this.setOpacityTo(1.0);
    this.props.onPressOut && this.props.onPressOut();
  },

  touchableHandlePress: function() {
    this.setOpacityTo(1.0);
    this.props.onPress && this.props.onPress();
  },

  touchableHandleLongPress: function() {
    this.props.onLongPress && this.props.onLongPress();
  },

  touchableGetPressRectOffset: function() {
    return PRESS_RECT_OFFSET;   // Always make sure to predeclare a constant!
  },

  touchableGetHighlightDelayMS: function() {
    return 0;
  },

  render: function() {
    return cloneWithProps(onlyChild(this.props.children), {
      ref: CHILD_REF,
      accessible: true,
      testID: this.props.testID,
      onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
      onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
      onResponderGrant: this.touchableHandleResponderGrant,
      onResponderMove: this.touchableHandleResponderMove,
      onResponderRelease: this.touchableHandleResponderRelease,
      onResponderTerminate: this.touchableHandleResponderTerminate,
    });
  },
});

/**
 * When the scroll view is disabled, this defines how far your touch may move
 * off of the button, before deactivating the button. Once deactivated, try
 * moving it back and you'll see that the button is once again reactivated!
 * Move it back and forth several times while the scroll view is disabled.
 */
var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};

var CHILD_REF = keyOf({childRef: null});

module.exports = TouchableOpacity;
<commit_msg>[Touchable] Change default `activeOpacity` to 0.2 to match iOS<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule TouchableOpacity
 */
'use strict';

// Note (avik): add @flow when Flow supports spread properties in propTypes

var NativeMethodsMixin = require('NativeMethodsMixin');
var POPAnimationMixin = require('POPAnimationMixin');
var React = require('React');
var Touchable = require('Touchable');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');

var cloneWithProps = require('cloneWithProps');
var ensureComponentIsNative = require('ensureComponentIsNative');
var keyOf = require('keyOf');
var onlyChild = require('onlyChild');

/**
 * A wrapper for making views respond properly to touches.
 * On press down, the opacity of the wrapped view is decreased, dimming it.
 * This is done without actually changing the view hierarchy, and in general is
 * easy to add to an app without weird side-effects.
 *
 * Example:
 *
 * ```
 * renderButton: function() {
 *   return (
 *     <TouchableOpacity onPress={this._onPressButton}>
 *       <Image
 *         style={styles.button}
 *         source={require('image!myButton')}
 *       />
 *     </TouchableOpacity>
 *   );
 * },
 * ```
 */

var TouchableOpacity = React.createClass({
  mixins: [Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],

  propTypes: {
    ...TouchableWithoutFeedback.propTypes,
    /**
     * Determines what the opacity of the wrapped view should be when touch is
     * active.
     */
    activeOpacity: React.PropTypes.number,
  },

  getDefaultProps: function() {
    return {
      activeOpacity: 0.2,
    };
  },

  getInitialState: function() {
    return this.touchableGetInitialState();
  },

  componentDidMount: function() {
    ensureComponentIsNative(this.refs[CHILD_REF]);
  },

  componentDidUpdate: function() {
    ensureComponentIsNative(this.refs[CHILD_REF]);
  },

  setOpacityTo: function(value) {
    if (POPAnimationMixin) {
      // Reset with animation if POP is available
      this.stopAllAnimations();
      var anim = {
        type: this.AnimationTypes.linear,
        property: this.AnimationProperties.opacity,
        toValue: value,
      };
      this.startAnimation(CHILD_REF, anim);
    } else {
      // Reset immediately if POP is unavailable
      this.refs[CHILD_REF].setNativeProps({
        opacity: value
      });
    }
  },

  /**
   * `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
   * defined on your component.
   */
  touchableHandleActivePressIn: function() {
    this.refs[CHILD_REF].setNativeProps({
      opacity: this.props.activeOpacity
    });
    this.props.onPressIn && this.props.onPressIn();
  },

  touchableHandleActivePressOut: function() {
    this.setOpacityTo(1.0);
    this.props.onPressOut && this.props.onPressOut();
  },

  touchableHandlePress: function() {
    this.setOpacityTo(1.0);
    this.props.onPress && this.props.onPress();
  },

  touchableHandleLongPress: function() {
    this.props.onLongPress && this.props.onLongPress();
  },

  touchableGetPressRectOffset: function() {
    return PRESS_RECT_OFFSET;   // Always make sure to predeclare a constant!
  },

  touchableGetHighlightDelayMS: function() {
    return 0;
  },

  render: function() {
    return cloneWithProps(onlyChild(this.props.children), {
      ref: CHILD_REF,
      accessible: true,
      testID: this.props.testID,
      onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
      onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
      onResponderGrant: this.touchableHandleResponderGrant,
      onResponderMove: this.touchableHandleResponderMove,
      onResponderRelease: this.touchableHandleResponderRelease,
      onResponderTerminate: this.touchableHandleResponderTerminate,
    });
  },
});

/**
 * When the scroll view is disabled, this defines how far your touch may move
 * off of the button, before deactivating the button. Once deactivated, try
 * moving it back and you'll see that the button is once again reactivated!
 * Move it back and forth several times while the scroll view is disabled.
 */
var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};

var CHILD_REF = keyOf({childRef: null});

module.exports = TouchableOpacity;
<|endoftext|>


<commit_before>frappe.pages['user-permissions'].onload = function(wrapper) {
	frappe.ui.make_app_page({
		parent: wrapper,
		title: "User Permissions Manager",
		icon: "icon-user",
		single_column: true
	});
	$(wrapper).find(".layout-main").html("

\

\

\

 "+__("Quick Help for User Permissions")+":
\

\

"
			+ __("Apart from Role based Permission Rules, you can apply User Permissions based on DocTypes.")
			+ "
"

			+ "
"
			+ __("These permissions will apply for all transactions where the permitted record is linked. For example, if Company C is added to User Permissions of user X, user X will only be able to see transactions that has company C as a linked value.")
			+ "
"

			+ "
"
			+ __("These will also be set as default values for those links, if only one such permission record is defined.")
			+ "
"

			+ "
"
			+ __("A user can be permitted to multiple records of the same DocType.")
			+ "
\

\

	\

");
	wrapper.user_permissions = new frappe.UserPermissions(wrapper);
}

frappe.pages['user-permissions'].refresh = function(wrapper) {
	wrapper.user_permissions.set_from_route();
}

frappe.UserPermissions = Class.extend({
	init: function(wrapper) {
		this.wrapper = wrapper;
		this.body = $(this.wrapper).find(".user-settings");
		this.filters = {};
		this.make();
		this.refresh();
	},
	make: function() {
		var me = this;
		return frappe.call({
			module:"frappe.core",
			page:"user_permissions",
			method: "get_users_and_links",
			callback: function(r) {
				me.options = r.message;

				me.filters.user = me.wrapper.appframe.add_field({
					fieldname: "user",
					label: __("User"),
					fieldtype: "Select",
					options: (["Select User..."].concat(r.message.users)).join("\n")
				});

				me.filters.doctype = me.wrapper.appframe.add_field({
					fieldname: "doctype",
					label: __("DocType"),
					fieldtype: "Select",
					options: (["Select DocType..."].concat(me.get_link_names())).join("\n")
				});

				me.filters.user_permission = me.wrapper.appframe.add_field({
					fieldname: "user_permission",
					label: __("Name"),
					fieldtype: "Link",
					options: "[Select]"
				});

				me.download = me.wrapper.appframe.add_field({
					fieldname: "download",
					label: __("Download"),
					fieldtype: "Button",
					icon: "icon-download"
				});

				me.upload = me.wrapper.appframe.add_field({
					fieldname: "upload",
					label: __("Upload"),
					fieldtype: "Button",
					icon: "icon-upload"
				});

				// bind change event
				$.each(me.filters, function(k, f) {
					f.$input.on("change", function() {
						me.refresh();
					});
				});

				// change options in user_permission link
				me.filters.doctype.$input.on("change", function() {
					me.filters.user_permission.df.options = me.get_doctype();
				});

				me.set_from_route();
				me.setup_download_upload();
			}
		});
	},
	setup_download_upload: function() {
		var me = this;
		me.download.$input.on("click", function() {
			window.location.href = frappe.urllib.get_base_url()
				+ "/api/method/frappe.core.page.user_permissions.user_permissions.get_user_permissions_csv";
		});

		me.upload.$input.on("click", function() {
			var d = new frappe.ui.Dialog({
				title: "Upload User Permissions",
				fields: [
					{
						fieldtype:"HTML",
						options: '

'+
							"
"+__("Upload CSV file containing all user permissions in the same format as Download.")+"
"+
							"
"+__("Any existing permission will be deleted / overwritten.")+"
"+
						'
'
					},
					{
						fieldtype:"Attach", fieldname:"attach",
					}
				],
				primary_action_label: __("Upload and Sync"),
				primary_action: function() {
					frappe.call({
						method:"frappe.core.page.user_permissions.user_permissions.import_user_permissions",
						args: {
							filedata: d.fields_dict.attach.get_value()
						},
						callback: function(r) {
							if(!r.exc) {
								msgprint("Permissions Updated");
								d.hide();
							}
						}
					});
				}
			});
			d.show();
		})
	},
	get_link_names: function() {
		return this.options.link_fields;
	},
	set_from_route: function() {
		var me = this;
		if(frappe.route_options && this.filters && !$.isEmptyObject(this.filters)) {
			$.each(frappe.route_options, function(key, value) {
				if(me.filters[key] && frappe.route_options[key]!=null)
					me.set_filter(key, value);
			});
			frappe.route_options = null;
		}
		this.refresh();
	},
	set_filter: function(key, value) {
		this.filters[key].$input.val(value);
	},
	get_user: function() {
		var user = this.filters.user.$input.val();
		return user=="Select User..." ? null : user;
	},
	get_doctype: function() {
		var doctype = this.filters.doctype.$input.val();
		return doctype=="Select DocType..." ? null : doctype;
	},
	get_user_permission: function() {
		// autosuggest hack!
		var user_permission = this.filters.user_permission.$input.val();
		return (user_permission === "%") ? null : user_permission;
	},
	render: function(prop_list) {
		this.body.empty();
		this.prop_list = prop_list;
		if(!prop_list || !prop_list.length) {
			this.body.html("
"+__("No User Permissions found.")+"
");
		} else {
			this.show_user_permissions_table();
		}
		this.show_add_user_permission();
	},
	refresh: function() {
		var me = this;
		if(!me.filters.user) {
			this.body.html("
"+__("Loading")+"...
");
			return;
		}
		if(!me.get_user() && !me.get_doctype()) {
			this.body.html("
"+__("Select User or DocType to start.")+"
");
			return;
		}
		// get permissions
		return frappe.call({
			module: "frappe.core",
			page: "user_permissions",
			method: "get_permissions",
			args: {
				parent: me.get_user(),
				defkey: me.get_doctype(),
				defvalue: me.get_user_permission()
			},
			callback: function(r) {
				me.render(r.message);
			}
		});
	},
	show_user_permissions_table: function() {
		var me = this;
		this.table = $("
\

\

\

").appendTo(this.body);

		$.each([[__("User"), 150], [__("DocType"), 150], [__("User Permission"),150], ["", 50]],
			function(i, col) {
			$("").html(col[0]).css("width", col[1]+"px")
				.appendTo(me.table.find("thead tr"));
		});

		$.each(this.prop_list, function(i, d) {
			var row = $("
").appendTo(me.table.find("tbody"));

			$("").html(''
				+d.parent+'').appendTo(row);
			$("").html(d.defkey).appendTo(row);
			$("").html(d.defvalue).appendTo(row);

			me.add_delete_button(row, d);
		});

	},
	add_delete_button: function(row, d) {
		var me = this;
		$("<button class='btn btn-small btn-default'></button>")
			.appendTo($("").appendTo(row))
			.attr("data-name", d.name)
			.attr("data-user", d.parent)
			.attr("data-defkey", d.defkey)
			.attr("data-defvalue", d.defvalue)
			.click(function() {
				return frappe.call({
					module: "frappe.core",
					page: "user_permissions",
					method: "remove",
					args: {
						name: $(this).attr("data-name"),
						user: $(this).attr("data-user"),
						defkey: $(this).attr("data-defkey"),
						defvalue: $(this).attr("data-defvalue")
					},
					callback: function(r) {
						if(r.exc) {
							msgprint(__("Did not remove"));
						} else {
							me.refresh();
						}
					}
				})
			});
	},

	show_add_user_permission: function() {
		var me = this;
		$("<button class='btn btn-info'>"+__("Add A User Permission")+"</button>")
			.appendTo($("
").appendTo(this.body))
			.click(function() {
				var d = new frappe.ui.Dialog({
					title: "Add New User Permission",
					fields: [
						{fieldtype:"Select", label:__("User"),
							options:me.options.users, reqd:1, fieldname:"user"},
						{fieldtype:"Select", label: __("DocType"), fieldname:"defkey",
							options:me.get_link_names(), reqd:1},
						{fieldtype:"Link", label:__("Value"), fieldname:"defvalue",
							options:'[Select]', reqd:1},
						{fieldtype:"Button", label: __("Add"), fieldname:"add"},
					]
				});
				if(me.get_user()) {
					d.set_value("user", me.get_user());
					d.get_input("user").prop("disabled", true);
				}
				if(me.get_doctype()) {
					d.set_value("defkey", me.get_doctype());
					d.get_input("defkey").prop("disabled", true);
				}
				if(me.get_user_permission()) {
					d.set_value("defvalue", me.get_user_permission());
					d.get_input("defvalue").prop("disabled", true);
				}

				d.fields_dict["defvalue"].get_query = function(txt) {
					return {
						doctype: d.get_value("defkey")
					}
				};

				d.get_input("add").click(function() {
					var args = d.get_values();
					if(!args) {
						return;
					}
					frappe.call({
						module: "frappe.core",
						page: "user_permissions",
						method: "add",
						args: args,
						callback: function(r) {
							if(r.exc) {
								msgprint("Did not add");
							} else {
								me.refresh();
							}
						}
					})
					d.hide();
				});
				d.show();
			});
	}
})
<commit_msg>user permissions, show buttons only for system manager<commit_after>frappe.pages['user-permissions'].onload = function(wrapper) {
	frappe.ui.make_app_page({
		parent: wrapper,
		title: "User Permissions Manager",
		icon: "icon-user",
		single_column: true
	});
	$(wrapper).find(".layout-main").html("

\

\

\

 "+__("Quick Help for User Permissions")+":
\

\

"
			+ __("Apart from Role based Permission Rules, you can apply User Permissions based on DocTypes.")
			+ "
"

			+ "
"
			+ __("These permissions will apply for all transactions where the permitted record is linked. For example, if Company C is added to User Permissions of user X, user X will only be able to see transactions that has company C as a linked value.")
			+ "
"

			+ "
"
			+ __("These will also be set as default values for those links, if only one such permission record is defined.")
			+ "
"

			+ "
"
			+ __("A user can be permitted to multiple records of the same DocType.")
			+ "
\

\

	\

");
	wrapper.user_permissions = new frappe.UserPermissions(wrapper);
}

frappe.pages['user-permissions'].refresh = function(wrapper) {
	wrapper.user_permissions.set_from_route();
}

frappe.UserPermissions = Class.extend({
	init: function(wrapper) {
		this.wrapper = wrapper;
		this.body = $(this.wrapper).find(".user-settings");
		this.filters = {};
		this.make();
		this.refresh();
	},
	make: function() {
		var me = this;
		return frappe.call({
			module:"frappe.core",
			page:"user_permissions",
			method: "get_users_and_links",
			callback: function(r) {
				me.options = r.message;

				me.filters.user = me.wrapper.appframe.add_field({
					fieldname: "user",
					label: __("User"),
					fieldtype: "Select",
					options: (["Select User..."].concat(r.message.users)).join("\n")
				});

				me.filters.doctype = me.wrapper.appframe.add_field({
					fieldname: "doctype",
					label: __("DocType"),
					fieldtype: "Select",
					options: (["Select DocType..."].concat(me.get_link_names())).join("\n")
				});

				me.filters.user_permission = me.wrapper.appframe.add_field({
					fieldname: "user_permission",
					label: __("Name"),
					fieldtype: "Link",
					options: "[Select]"
				});

				if(user_roles.indexOf("System Manager")!==-1) {
					me.download = me.wrapper.appframe.add_field({
						fieldname: "download",
						label: __("Download"),
						fieldtype: "Button",
						icon: "icon-download"
					});

					me.upload = me.wrapper.appframe.add_field({
						fieldname: "upload",
						label: __("Upload"),
						fieldtype: "Button",
						icon: "icon-upload"
					});
				}

				// bind change event
				$.each(me.filters, function(k, f) {
					f.$input.on("change", function() {
						me.refresh();
					});
				});

				// change options in user_permission link
				me.filters.doctype.$input.on("change", function() {
					me.filters.user_permission.df.options = me.get_doctype();
				});

				me.set_from_route();
				me.setup_download_upload();
			}
		});
	},
	setup_download_upload: function() {
		var me = this;
		me.download.$input.on("click", function() {
			window.location.href = frappe.urllib.get_base_url()
				+ "/api/method/frappe.core.page.user_permissions.user_permissions.get_user_permissions_csv";
		});

		me.upload.$input.on("click", function() {
			var d = new frappe.ui.Dialog({
				title: "Upload User Permissions",
				fields: [
					{
						fieldtype:"HTML",
						options: '

'+
							"
"+__("Upload CSV file containing all user permissions in the same format as Download.")+"
"+
							"
"+__("Any existing permission will be deleted / overwritten.")+"
"+
						'
'
					},
					{
						fieldtype:"Attach", fieldname:"attach",
					}
				],
				primary_action_label: __("Upload and Sync"),
				primary_action: function() {
					frappe.call({
						method:"frappe.core.page.user_permissions.user_permissions.import_user_permissions",
						args: {
							filedata: d.fields_dict.attach.get_value()
						},
						callback: function(r) {
							if(!r.exc) {
								msgprint("Permissions Updated");
								d.hide();
							}
						}
					});
				}
			});
			d.show();
		})
	},
	get_link_names: function() {
		return this.options.link_fields;
	},
	set_from_route: function() {
		var me = this;
		if(frappe.route_options && this.filters && !$.isEmptyObject(this.filters)) {
			$.each(frappe.route_options, function(key, value) {
				if(me.filters[key] && frappe.route_options[key]!=null)
					me.set_filter(key, value);
			});
			frappe.route_options = null;
		}
		this.refresh();
	},
	set_filter: function(key, value) {
		this.filters[key].$input.val(value);
	},
	get_user: function() {
		var user = this.filters.user.$input.val();
		return user=="Select User..." ? null : user;
	},
	get_doctype: function() {
		var doctype = this.filters.doctype.$input.val();
		return doctype=="Select DocType..." ? null : doctype;
	},
	get_user_permission: function() {
		// autosuggest hack!
		var user_permission = this.filters.user_permission.$input.val();
		return (user_permission === "%") ? null : user_permission;
	},
	render: function(prop_list) {
		this.body.empty();
		this.prop_list = prop_list;
		if(!prop_list || !prop_list.length) {
			this.body.html("
"+__("No User Permissions found.")+"
");
		} else {
			this.show_user_permissions_table();
		}
		this.show_add_user_permission();
	},
	refresh: function() {
		var me = this;
		if(!me.filters.user) {
			this.body.html("
"+__("Loading")+"...
");
			return;
		}
		if(!me.get_user() && !me.get_doctype()) {
			this.body.html("
"+__("Select User or DocType to start.")+"
");
			return;
		}
		// get permissions
		return frappe.call({
			module: "frappe.core",
			page: "user_permissions",
			method: "get_permissions",
			args: {
				parent: me.get_user(),
				defkey: me.get_doctype(),
				defvalue: me.get_user_permission()
			},
			callback: function(r) {
				me.render(r.message);
			}
		});
	},
	show_user_permissions_table: function() {
		var me = this;
		this.table = $("
\

\

\

").appendTo(this.body);

		$.each([[__("User"), 150], [__("DocType"), 150], [__("User Permission"),150], ["", 50]],
			function(i, col) {
			$("").html(col[0]).css("width", col[1]+"px")
				.appendTo(me.table.find("thead tr"));
		});

		$.each(this.prop_list, function(i, d) {
			var row = $("
").appendTo(me.table.find("tbody"));

			$("").html(''
				+d.parent+'').appendTo(row);
			$("").html(d.defkey).appendTo(row);
			$("").html(d.defvalue).appendTo(row);

			me.add_delete_button(row, d);
		});

	},
	add_delete_button: function(row, d) {
		var me = this;
		$("<button class='btn btn-small btn-default'></button>")
			.appendTo($("").appendTo(row))
			.attr("data-name", d.name)
			.attr("data-user", d.parent)
			.attr("data-defkey", d.defkey)
			.attr("data-defvalue", d.defvalue)
			.click(function() {
				return frappe.call({
					module: "frappe.core",
					page: "user_permissions",
					method: "remove",
					args: {
						name: $(this).attr("data-name"),
						user: $(this).attr("data-user"),
						defkey: $(this).attr("data-defkey"),
						defvalue: $(this).attr("data-defvalue")
					},
					callback: function(r) {
						if(r.exc) {
							msgprint(__("Did not remove"));
						} else {
							me.refresh();
						}
					}
				})
			});
	},

	show_add_user_permission: function() {
		var me = this;
		$("<button class='btn btn-info'>"+__("Add A User Permission")+"</button>")
			.appendTo($("
").appendTo(this.body))
			.click(function() {
				var d = new frappe.ui.Dialog({
					title: "Add New User Permission",
					fields: [
						{fieldtype:"Select", label:__("User"),
							options:me.options.users, reqd:1, fieldname:"user"},
						{fieldtype:"Select", label: __("DocType"), fieldname:"defkey",
							options:me.get_link_names(), reqd:1},
						{fieldtype:"Link", label:__("Value"), fieldname:"defvalue",
							options:'[Select]', reqd:1},
						{fieldtype:"Button", label: __("Add"), fieldname:"add"},
					]
				});
				if(me.get_user()) {
					d.set_value("user", me.get_user());
					d.get_input("user").prop("disabled", true);
				}
				if(me.get_doctype()) {
					d.set_value("defkey", me.get_doctype());
					d.get_input("defkey").prop("disabled", true);
				}
				if(me.get_user_permission()) {
					d.set_value("defvalue", me.get_user_permission());
					d.get_input("defvalue").prop("disabled", true);
				}

				d.fields_dict["defvalue"].get_query = function(txt) {
					return {
						doctype: d.get_value("defkey")
					}
				};

				d.get_input("add").click(function() {
					var args = d.get_values();
					if(!args) {
						return;
					}
					frappe.call({
						module: "frappe.core",
						page: "user_permissions",
						method: "add",
						args: args,
						callback: function(r) {
							if(r.exc) {
								msgprint("Did not add");
							} else {
								me.refresh();
							}
						}
					})
					d.hide();
				});
				d.show();
			});
	}
})
<|endoftext|>


<commit_before>define([
        '../ThirdParty/when',
        './Check',
        './defaultValue',
        './defined',
        './DeveloperError',
        './Request',
        './RequestErrorEvent',
        './RequestScheduler',
        './RuntimeError',
        './TrustedServers'
    ], function(
        when,
        Check,
        defaultValue,
        defined,
        DeveloperError,
        Request,
        RequestErrorEvent,
        RequestScheduler,
        RuntimeError,
        TrustedServers) {
    'use strict';

    /**
     * Asynchronously loads the given URL.  Returns a promise that will resolve to
     * the result once loaded, or reject if the URL failed to load.  The data is loaded
     * using XMLHttpRequest, which means that in order to make requests to another origin,
     * the server must have Cross-Origin Resource Sharing (CORS) headers enabled.
     *
     * @exports loadWithXhr
     *
     * @param {Object} options Object with the following properties:
     * @param {String} options.url The URL of the data.
     * @param {String} [options.responseType] The type of response.  This controls the type of item returned.
     * @param {String} [options.method='GET'] The HTTP method to use.
     * @param {String} [options.data] The data to send with the request, if any.
     * @param {Object} [options.headers] HTTP headers to send with the request, if any.
     * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server.
     * @param {Request} [options.request] The request object.
     * @returns {Promise.<Object>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority.
     *
     *
     * @example
     * // Load a single URL asynchronously. In real code, you should use loadBlob instead.
     * Cesium.loadWithXhr({
     *     url : 'some/url',
     *     responseType : 'blob'
     * }).then(function(blob) {
     *     // use the data
     * }).otherwise(function(error) {
     *     // an error occurred
     * });
     *
     * @see loadArrayBuffer
     * @see loadBlob
     * @see loadJson
     * @see loadText
     * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing}
     * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A}
     */
    function loadWithXhr(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        //>>includeStart('debug', pragmas.debug);
        Check.defined('options.url', options.url);
        //>>includeEnd('debug');

        var url = options.url;

        var responseType = options.responseType;
        var method = defaultValue(options.method, 'GET');
        var data = options.data;
        var headers = options.headers;
        var overrideMimeType = options.overrideMimeType;
        url = defaultValue(url, options.url);

        var request = defined(options.request) ? options.request : new Request();
        request.url = url;
        request.requestFunction = function() {
            var deferred = when.defer();
            var xhr = loadWithXhr.load(url, responseType, method, data, headers, deferred, overrideMimeType);
            if (defined(xhr) && defined(xhr.abort)) {
                request.cancelFunction = function() {
                    xhr.abort();
                };
            }
            return deferred.promise;
        };

        return RequestScheduler.request(request);
    }

    var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;

    function decodeDataUriText(isBase64, data) {
        var result = decodeURIComponent(data);
        if (isBase64) {
            return atob(result);
        }
        return result;
    }

    function decodeDataUriArrayBuffer(isBase64, data) {
        var byteString = decodeDataUriText(isBase64, data);
        var buffer = new ArrayBuffer(byteString.length);
        var view = new Uint8Array(buffer);
        for (var i = 0; i < byteString.length; i++) {
            view[i] = byteString.charCodeAt(i);
        }
        return buffer;
    }

    function decodeDataUri(dataUriRegexResult, responseType) {
        responseType = defaultValue(responseType, '');
        var mimeType = dataUriRegexResult[1];
        var isBase64 = !!dataUriRegexResult[2];
        var data = dataUriRegexResult[3];

        switch (responseType) {
            case '':
            case 'text':
                return decodeDataUriText(isBase64, data);
            case 'arraybuffer':
                return decodeDataUriArrayBuffer(isBase64, data);
            case 'blob':
                var buffer = decodeDataUriArrayBuffer(isBase64, data);
                return new Blob([buffer], {
                    type : mimeType
                });
            case 'document':
                var parser = new DOMParser();
                return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType);
            case 'json':
                return JSON.parse(decodeDataUriText(isBase64, data));
            default:
                //>>includeStart('debug', pragmas.debug);
                throw new DeveloperError('Unhandled responseType: ' + responseType);
                //>>includeEnd('debug');
        }
    }

    // This is broken out into a separate function so that it can be mocked for testing purposes.
    loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) {
        var dataUriRegexResult = dataUriRegex.exec(url);
        if (dataUriRegexResult !== null) {
            deferred.resolve(decodeDataUri(dataUriRegexResult, responseType));
            return;
        }

        var xhr = new XMLHttpRequest();

        if (TrustedServers.contains(url)) {
            xhr.withCredentials = true;
        }

        if (defined(overrideMimeType) && defined(xhr.overrideMimeType)) {
            xhr.overrideMimeType(overrideMimeType);
        }

        xhr.open(method, url, true);

        if (defined(headers)) {
            for (var key in headers) {
                if (headers.hasOwnProperty(key)) {
                    xhr.setRequestHeader(key, headers[key]);
                }
            }
        }

        if (defined(responseType)) {
            xhr.responseType = responseType;
        }

        xhr.onload = function() {
            if (xhr.status < 200 || xhr.status >= 300) {
                deferred.reject(new RequestErrorEvent(xhr.status, xhr.response, xhr.getAllResponseHeaders()));
                return;
            }

            var response = xhr.response;
            var browserResponseType = xhr.responseType;

            //All modern browsers will go into either the first if block or last else block.
            //Other code paths support older browsers that either do not support the supplied responseType
            //or do not support the xhr.response property.
            if (defined(response) && (!defined(responseType) || (browserResponseType === responseType))) {
                deferred.resolve(response);
            } else if ((responseType === 'json') && typeof response === 'string') {
                try {
                    deferred.resolve(JSON.parse(response));
                } catch (e) {
                    deferred.reject(e);
                }
            } else if ((browserResponseType === '' || browserResponseType === 'document') && defined(xhr.responseXML) && xhr.responseXML.hasChildNodes()) {
                deferred.resolve(xhr.responseXML);
            } else if ((browserResponseType === '' || browserResponseType === 'text') && defined(xhr.responseText)) {
                deferred.resolve(xhr.responseText);
            } else {
                deferred.reject(new RuntimeError('Invalid XMLHttpRequest response type.'));
            }
        };

        xhr.onerror = function(e) {
            deferred.reject(new RequestErrorEvent());
        };

        xhr.send(data);

        return xhr;
    };

    loadWithXhr.defaultLoad = loadWithXhr.load;

    return loadWithXhr;
});
<commit_msg>Allow local-running Cesium to load its resources<commit_after>define([
        '../ThirdParty/when',
        './Check',
        './defaultValue',
        './defined',
        './DeveloperError',
        './Request',
        './RequestErrorEvent',
        './RequestScheduler',
        './RuntimeError',
        './TrustedServers'
    ], function(
        when,
        Check,
        defaultValue,
        defined,
        DeveloperError,
        Request,
        RequestErrorEvent,
        RequestScheduler,
        RuntimeError,
        TrustedServers) {
    'use strict';

    /**
     * Asynchronously loads the given URL.  Returns a promise that will resolve to
     * the result once loaded, or reject if the URL failed to load.  The data is loaded
     * using XMLHttpRequest, which means that in order to make requests to another origin,
     * the server must have Cross-Origin Resource Sharing (CORS) headers enabled.
     *
     * @exports loadWithXhr
     *
     * @param {Object} options Object with the following properties:
     * @param {String} options.url The URL of the data.
     * @param {String} [options.responseType] The type of response.  This controls the type of item returned.
     * @param {String} [options.method='GET'] The HTTP method to use.
     * @param {String} [options.data] The data to send with the request, if any.
     * @param {Object} [options.headers] HTTP headers to send with the request, if any.
     * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server.
     * @param {Request} [options.request] The request object.
     * @returns {Promise.<Object>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority.
     *
     *
     * @example
     * // Load a single URL asynchronously. In real code, you should use loadBlob instead.
     * Cesium.loadWithXhr({
     *     url : 'some/url',
     *     responseType : 'blob'
     * }).then(function(blob) {
     *     // use the data
     * }).otherwise(function(error) {
     *     // an error occurred
     * });
     *
     * @see loadArrayBuffer
     * @see loadBlob
     * @see loadJson
     * @see loadText
     * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing}
     * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A}
     */
    function loadWithXhr(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        //>>includeStart('debug', pragmas.debug);
        Check.defined('options.url', options.url);
        //>>includeEnd('debug');

        var url = options.url;

        var responseType = options.responseType;
        var method = defaultValue(options.method, 'GET');
        var data = options.data;
        var headers = options.headers;
        var overrideMimeType = options.overrideMimeType;
        url = defaultValue(url, options.url);

        var request = defined(options.request) ? options.request : new Request();
        request.url = url;
        request.requestFunction = function() {
            var deferred = when.defer();
            var xhr = loadWithXhr.load(url, responseType, method, data, headers, deferred, overrideMimeType);
            if (defined(xhr) && defined(xhr.abort)) {
                request.cancelFunction = function() {
                    xhr.abort();
                };
            }
            return deferred.promise;
        };

        return RequestScheduler.request(request);
    }

    var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;

    function decodeDataUriText(isBase64, data) {
        var result = decodeURIComponent(data);
        if (isBase64) {
            return atob(result);
        }
        return result;
    }

    function decodeDataUriArrayBuffer(isBase64, data) {
        var byteString = decodeDataUriText(isBase64, data);
        var buffer = new ArrayBuffer(byteString.length);
        var view = new Uint8Array(buffer);
        for (var i = 0; i < byteString.length; i++) {
            view[i] = byteString.charCodeAt(i);
        }
        return buffer;
    }

    function decodeDataUri(dataUriRegexResult, responseType) {
        responseType = defaultValue(responseType, '');
        var mimeType = dataUriRegexResult[1];
        var isBase64 = !!dataUriRegexResult[2];
        var data = dataUriRegexResult[3];

        switch (responseType) {
            case '':
            case 'text':
                return decodeDataUriText(isBase64, data);
            case 'arraybuffer':
                return decodeDataUriArrayBuffer(isBase64, data);
            case 'blob':
                var buffer = decodeDataUriArrayBuffer(isBase64, data);
                return new Blob([buffer], {
                    type : mimeType
                });
            case 'document':
                var parser = new DOMParser();
                return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType);
            case 'json':
                return JSON.parse(decodeDataUriText(isBase64, data));
            default:
                //>>includeStart('debug', pragmas.debug);
                throw new DeveloperError('Unhandled responseType: ' + responseType);
                //>>includeEnd('debug');
        }
    }

    // This is broken out into a separate function so that it can be mocked for testing purposes.
    loadWithXhr.load = function(url, responseType, method, data, headers, deferred, overrideMimeType) {
        var dataUriRegexResult = dataUriRegex.exec(url);
        if (dataUriRegexResult !== null) {
            deferred.resolve(decodeDataUri(dataUriRegexResult, responseType));
            return;
        }

        var xhr = new XMLHttpRequest();

        if (TrustedServers.contains(url)) {
            xhr.withCredentials = true;
        }

        if (defined(overrideMimeType) && defined(xhr.overrideMimeType)) {
            xhr.overrideMimeType(overrideMimeType);
        }

        xhr.open(method, url, true);

        if (defined(headers)) {
            for (var key in headers) {
                if (headers.hasOwnProperty(key)) {
                    xhr.setRequestHeader(key, headers[key]);
                }
            }
        }

        if (defined(responseType)) {
            xhr.responseType = responseType;
        }

        var localFile = false;
        if (typeof url === 'string') {
            localFile = url.indexOf('file://') === 0;
        }

        xhr.onload = function() {
            if ((xhr.status < 200 || xhr.status >= 300) && !(localFile && xhr.status === 0)) {
                deferred.reject(new RequestErrorEvent(xhr.status, xhr.response, xhr.getAllResponseHeaders()));
                return;
            }

            var response = xhr.response;
            var browserResponseType = xhr.responseType;

            //All modern browsers will go into either the first if block or last else block.
            //Other code paths support older browsers that either do not support the supplied responseType
            //or do not support the xhr.response property.
            if (defined(response) && (!defined(responseType) || (browserResponseType === responseType))) {
                deferred.resolve(response);
            } else if ((responseType === 'json') && typeof response === 'string') {
                try {
                    deferred.resolve(JSON.parse(response));
                } catch (e) {
                    deferred.reject(e);
                }
            } else if ((browserResponseType === '' || browserResponseType === 'document') && defined(xhr.responseXML) && xhr.responseXML.hasChildNodes()) {
                deferred.resolve(xhr.responseXML);
            } else if ((browserResponseType === '' || browserResponseType === 'text') && defined(xhr.responseText)) {
                deferred.resolve(xhr.responseText);
            } else {
                deferred.reject(new RuntimeError('Invalid XMLHttpRequest response type.'));
            }
        };

        xhr.onerror = function(e) {
            deferred.reject(new RequestErrorEvent());
        };

        xhr.send(data);

        return xhr;
    };

    loadWithXhr.defaultLoad = loadWithXhr.load;

    return loadWithXhr;
});
<|endoftext|>