You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2013/06/26 23:18:30 UTC
svn commit: r1497102 - in
/incubator/ambari/branches/branch-1.2.5/ambari-web: ./ app/
app/controllers/main/admin/security/add/ app/data/ app/styles/ app/utils/
vendor/scripts/
Author: yusaku
Date: Wed Jun 26 21:18:30 2013
New Revision: 1497102
URL: http://svn.apache.org/r1497102
Log:
AMBARI-2491. Security Wizard: show which principals and keytabs need to be created on which hosts. (Andrii Tkach via yusaku)
Added:
incubator/ambari/branches/branch-1.2.5/ambari-web/vendor/scripts/FileSaver.js
Modified:
incubator/ambari/branches/branch-1.2.5/ambari-web/app/controllers/main/admin/security/add/step2.js
incubator/ambari/branches/branch-1.2.5/ambari-web/app/data/secure_properties.js
incubator/ambari/branches/branch-1.2.5/ambari-web/app/messages.js
incubator/ambari/branches/branch-1.2.5/ambari-web/app/styles/application.less
incubator/ambari/branches/branch-1.2.5/ambari-web/app/utils/string_utils.js
incubator/ambari/branches/branch-1.2.5/ambari-web/config.coffee
Modified: incubator/ambari/branches/branch-1.2.5/ambari-web/app/controllers/main/admin/security/add/step2.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2.5/ambari-web/app/controllers/main/admin/security/add/step2.js?rev=1497102&r1=1497101&r2=1497102&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2.5/ambari-web/app/controllers/main/admin/security/add/step2.js (original)
+++ incubator/ambari/branches/branch-1.2.5/ambari-web/app/controllers/main/admin/security/add/step2.js Wed Jun 26 21:18:30 2013
@@ -17,6 +17,8 @@
*/
var App = require('app');
+var stringUtils = require('utils/string_utils');
+
App.MainAdminSecurityAddStep2Controller = Em.Controller.extend({
name: 'mainAdminSecurityAddStep2Controller',
@@ -150,13 +152,110 @@ App.MainAdminSecurityAddStep2Controller
}
},
+ showHostPrincipalKeytabList: function(){
+ App.ModalPopup.show({
+ self: this,
+ header: Em.I18n.t('admin.security.step2.popup.header'),
+ primary: Em.I18n.t('common.proceed'),
+ downloadCsv: Em.I18n.t('admin.security.step2.popup.downloadCSV'),
+ classNames: ['sixty-percent-width-modal'],
+ csvContent: [],
+ onDownloadCsv: function(){
+ var blob = new Blob([this.get('csvContent')], {type: "text/csv;charset=utf-8"});
+ saveAs(blob, "host-principal-keytab-list.csv");
+ },
+ onPrimary: function(){
+ this.hide();
+ App.router.send('next');
+ },
+ buildCsvContent: function(data){
+ this.set('csvContent', stringUtils.arrayToCSV(data));
+ },
+ bodyClass: Em.View.extend({
+ componentsToDisplay: ['NAMENODE', 'SECONDARY_NAMENODE', 'DATANODE', 'JOBTRACKER', 'ZOOKEEPER_SERVER', 'HIVE_SERVER', 'TASKTRACKER',
+ 'OOZIE_SERVER', 'NAGIOS_SERVER', 'HBASE_MASTER', 'HBASE_REGIONSERVER'],
+ hostComponents: function(){
+ var componentsToDisplay = this.get('componentsToDisplay');
+ var configs = this.get('parentView.self.stepConfigs');
+ var hosts = App.Host.find();
+ var result = [];
+ hosts.forEach(function(host){
+ host.get('hostComponents').forEach(function(hostComponent){
+ if(componentsToDisplay.contains(hostComponent.get('componentName'))){
+ var serviceConfigs = configs.findProperty('serviceName', hostComponent.get('service.serviceName')).get('configs');
+ var principal, keytab;
+ serviceConfigs.forEach(function(config){
+ if (config.get('component') && config.get('component') === hostComponent.get('componentName')) {
+ if (config.get('name').substr(-15, 15) === '_principal_name') {
+ principal = config.get('value').replace('_HOST', host.get('hostName')) + config.get('unit');
+ } else if (config.get('name').substr(-7, 7) === '_keytab' || config.get('name').substr(-12, 12) === '_keytab_path') {
+ keytab = config.get('value');
+ }
+ } else if (config.get('components') && config.get('components').contains(hostComponent.get('componentName'))) {
+ if (config.get('name').substr(-15, 15) === '_principal_name') {
+ principal = config.get('value').replace('_HOST', host.get('hostName')) + config.get('unit');
+ } else if (config.get('name').substr(-7, 7) === '_keytab' || config.get('name').substr(-12, 12) === '_keytab_path') {
+ keytab = config.get('value');
+ }
+ }
+ });
+
+ result.push({
+ host: host.get('hostName'),
+ component: hostComponent.get('displayName'),
+ principal: principal,
+ keytab: keytab
+ });
+ }
+ });
+ });
+ this.get('parentView').buildCsvContent(result);
+ return result;
+ }.property(),
+ template: Em.Handlebars.compile([
+ '<div class="alert alert-info">{{t admin.security.step2.popup.notice}}</div>',
+ '<div class="long-popup-list">',
+ '<table class="table table-bordered table-striped">',
+ '<thead>',
+ '<tr>',
+ '<th>{{t common.host}}</th>',
+ '<th>{{t common.component}}</th>',
+ '<th>{{t admin.security.step2.popup.table.principal}}</th>',
+ '<th>{{t admin.security.step2.popup.table.keytab}}</th>',
+ '</tr>',
+ '</thead>',
+ '<tbody>',
+ '{{#each hostComponent in view.hostComponents}}',
+ '<tr>',
+ '<td>{{hostComponent.host}}</td>',
+ '<td>{{hostComponent.component}}</td>',
+ '<td>{{hostComponent.principal}}</td>',
+ '<td>{{hostComponent.keytab}}</td>',
+ '</tr>',
+ '{{/each}}',
+ '</tbody>',
+ '</table>',
+ '</div>'
+ ].join(''))
+ }),
+ footerClass: Em.View.extend({
+ classNames: ['modal-footer'],
+ template: Em.Handlebars.compile([
+ '{{#if view.parentView.downloadCsv}}<a class="btn btn-info" {{action onDownloadCsv target="view.parentView"}}>{{view.parentView.downloadCsv}}</a> {{/if}}',
+ '{{#if view.parentView.secondary}}<a class="btn" {{action onSecondary target="view.parentView"}}>{{view.parentView.secondary}}</a> {{/if}}',
+ '{{#if view.parentView.primary}}<a class="btn btn-success" {{action onPrimary target="view.parentView"}}>{{view.parentView.primary}}</a>{{/if}}'
+ ].join(''))
+ })
+ });
+ },
+
/**
* submit and move to step3
*/
submit: function () {
if (!this.get('isSubmitDisabled')) {
- App.router.send('next');
+ this.showHostPrincipalKeytabList();
}
}
Modified: incubator/ambari/branches/branch-1.2.5/ambari-web/app/data/secure_properties.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2.5/ambari-web/app/data/secure_properties.js?rev=1497102&r1=1497101&r2=1497102&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2.5/ambari-web/app/data/secure_properties.js (original)
+++ incubator/ambari/branches/branch-1.2.5/ambari-web/app/data/secure_properties.js Wed Jun 26 21:18:30 2013
@@ -159,7 +159,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "HDFS",
- "category": "NameNode"
+ "category": "NameNode",
+ "components": ["NAMENODE", "SECONDARY_NAMENODE"]
},
{
"id": "puppet var",
@@ -172,7 +173,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "HDFS",
- "category": "NameNode"
+ "category": "NameNode",
+ "components": ["NAMENODE", "SECONDARY_NAMENODE"]
},
{
"id": "puppet var",
@@ -211,7 +213,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "HDFS",
- "category": "DataNode"
+ "category": "DataNode",
+ "component": "DATANODE"
},
{
"id": "puppet var",
@@ -224,7 +227,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "HDFS",
- "category": "DataNode"
+ "category": "DataNode",
+ "component": "DATANODE"
},
//MAPREDUCE
{
@@ -238,7 +242,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "MAPREDUCE",
- "category": "JobTracker"
+ "category": "JobTracker",
+ "component": "JOBTRACKER"
},
{
"id": "puppet var",
@@ -251,7 +256,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "MAPREDUCE",
- "category": "JobTracker"
+ "category": "JobTracker",
+ "component": "JOBTRACKER"
},
{
"id": "puppet var",
@@ -264,7 +270,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "MAPREDUCE",
- "category": "TaskTracker"
+ "category": "TaskTracker",
+ "component": "TASKTRACKER"
},
{
"id": "puppet var",
@@ -277,7 +284,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "MAPREDUCE",
- "category": "TaskTracker"
+ "category": "TaskTracker",
+ "component": "TASKTRACKER"
},
//HBASE
@@ -292,7 +300,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "HBASE",
- "category": "HBase"
+ "category": "HBase",
+ "components": ["HBASE_MASTER", "HBASE_REGIONSERVER"]
},
{
"id": "puppet var",
@@ -305,7 +314,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "HBASE",
- "category": "HBase"
+ "category": "HBase",
+ "components": ["HBASE_MASTER", "HBASE_REGIONSERVER"]
},
//HIVE
@@ -333,7 +343,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "HIVE",
- "category": "Hive Metastore"
+ "category": "Hive Metastore",
+ "component": "HIVE_SERVER"
},
{
"id": "puppet var",
@@ -346,7 +357,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "HIVE",
- "category": "Hive Metastore"
+ "category": "Hive Metastore",
+ "component": "HIVE_SERVER"
},
@@ -375,7 +387,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "OOZIE",
- "category": "Oozie Server"
+ "category": "Oozie Server",
+ "component": "OOZIE_SERVER"
},
{
"id": "puppet var",
@@ -388,7 +401,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "OOZIE",
- "category": "Oozie Server"
+ "category": "Oozie Server",
+ "component": "OOZIE_SERVER"
},
//ZooKeeper
@@ -403,7 +417,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "ZOOKEEPER",
- "category": "ZooKeeper Server"
+ "category": "ZooKeeper Server",
+ "component": "ZOOKEEPER_SERVER"
},
{
"id": "puppet var",
@@ -416,7 +431,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "ZOOKEEPER",
- "category": "ZooKeeper Server"
+ "category": "ZooKeeper Server",
+ "component": "ZOOKEEPER_SERVER"
},
//NAGIOS
{
@@ -443,7 +459,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "NAGIOS",
- "category": "Nagios Server"
+ "category": "Nagios Server",
+ "component": "NAGIOS_SERVER"
},
{
"id": "puppet var",
@@ -456,7 +473,8 @@ module.exports =
"isVisible": true,
"isOverridable": false,
"serviceName": "NAGIOS",
- "category": "Nagios Server"
+ "category": "Nagios Server",
+ "component": "NAGIOS_SERVER"
}
]
Modified: incubator/ambari/branches/branch-1.2.5/ambari-web/app/messages.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2.5/ambari-web/app/messages.js?rev=1497102&r1=1497101&r2=1497102&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2.5/ambari-web/app/messages.js (original)
+++ incubator/ambari/branches/branch-1.2.5/ambari-web/app/messages.js Wed Jun 26 21:18:30 2013
@@ -89,6 +89,7 @@ Em.I18n.translations = {
'common.diskUsage':'Disk Usage',
'common.loadAvg':'Load Avg',
'common.components':'Components',
+ 'common.component':'Component',
'common.quickLinks':'Quick Links',
'common.save':'Save',
'common.servers':'Servers',
@@ -135,6 +136,7 @@ Em.I18n.translations = {
'common.use': 'Use',
'common.stacks': 'Stacks',
'common.reset': 'Reset',
+ 'common.proceed': 'Proceed',
'requestInfo.installComponents':'Install Components',
'requestInfo.installServices':'Install Services',
@@ -601,6 +603,13 @@ Em.I18n.translations = {
'admin.security.step1.body.instruction3': 'Create Kerberos principals for Hadoop services and hosts',
'admin.security.step1.body.instruction4': 'Generate keytabs for each principal and place on the appropriate hosts',
'admin.security.step2.body.header': 'Configure Kerberos security properties',
+ 'admin.security.step2.popup.header': 'Manual Steps Required Before Proceeding',
+ 'admin.security.step2.popup.notice': 'You need to create the following principals and keytabs on the hosts shown.<br />'+
+ 'You can download the list as a CSV file and use it to create a script to generate the principals and keytabs.' +
+ 'Once the principals and keytabs have been created, click on <i>Proceed</i> to continue. If you need to make configuration changes, click <i>Cancel</i>.',
+ 'admin.security.step2.popup.table.principal': 'Principal',
+ 'admin.security.step2.popup.table.keytab': 'Keytab',
+ 'admin.security.step2.popup.downloadCSV': 'Download CSV',
'admin.security.step3.body.header': 'Applying kerberos security to the cluster',
'admin.security.step3.body.success.header' : 'Kerberos-based security has been enabled on your cluster. Please wait while services are started in secure mode.',
'admin.security.step3.body.failure.header' : 'Failed to enable Kerberos-based security on your cluster. Your cluster will keep running in non-secure mode.',
Modified: incubator/ambari/branches/branch-1.2.5/ambari-web/app/styles/application.less
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2.5/ambari-web/app/styles/application.less?rev=1497102&r1=1497101&r2=1497102&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2.5/ambari-web/app/styles/application.less (original)
+++ incubator/ambari/branches/branch-1.2.5/ambari-web/app/styles/application.less Wed Jun 26 21:18:30 2013
@@ -935,6 +935,11 @@ a:focus {
max-height: 544px;
top: 5%;
}
+ .long-popup-list {
+ height: 280px;
+ overflow: auto;
+ width: 100%;
+ }
.modal-body {
Modified: incubator/ambari/branches/branch-1.2.5/ambari-web/app/utils/string_utils.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2.5/ambari-web/app/utils/string_utils.js?rev=1497102&r1=1497101&r2=1497102&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2.5/ambari-web/app/utils/string_utils.js (original)
+++ incubator/ambari/branches/branch-1.2.5/ambari-web/app/utils/string_utils.js Wed Jun 26 21:18:30 2013
@@ -105,5 +105,23 @@ module.exports = {
isSingleLine: function(string){
return string.trim().indexOf("\n") == -1;
+ },
+ /**
+ * transform array of objects into CSV format content
+ * @param array
+ * @return {Array}
+ */
+ arrayToCSV: function(array){
+ var content = "";
+ array.forEach(function(item){
+ var row = [];
+ for(var i in item){
+ if(item.hasOwnProperty(i)){
+ row.push(item[i]);
+ }
+ }
+ content += row.join(',') + '\n';
+ });
+ return content;
}
}
Modified: incubator/ambari/branches/branch-1.2.5/ambari-web/config.coffee
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2.5/ambari-web/config.coffee?rev=1497102&r1=1497101&r2=1497102&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2.5/ambari-web/config.coffee (original)
+++ incubator/ambari/branches/branch-1.2.5/ambari-web/config.coffee Wed Jun 26 21:18:30 2013
@@ -55,7 +55,8 @@ exports.config =
'vendor/scripts/workflow_visualization.js',
'vendor/scripts/rickshaw.js',
'vendor/scripts/spin.js',
- 'vendor/scripts/jquery.flexibleArea.js'
+ 'vendor/scripts/jquery.flexibleArea.js',
+ 'vendor/scripts/FileSaver.js'
]
stylesheets:
Added: incubator/ambari/branches/branch-1.2.5/ambari-web/vendor/scripts/FileSaver.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2.5/ambari-web/vendor/scripts/FileSaver.js?rev=1497102&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2.5/ambari-web/vendor/scripts/FileSaver.js (added)
+++ incubator/ambari/branches/branch-1.2.5/ambari-web/vendor/scripts/FileSaver.js Wed Jun 26 21:18:30 2013
@@ -0,0 +1,216 @@
+/* FileSaver.js
+ * A saveAs() FileSaver implementation.
+ * 2013-01-23
+ *
+ * By Eli Grey, http://eligrey.com
+ * License: X11/MIT
+ * See LICENSE.md
+ */
+
+/*global self */
+/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
+ plusplus: true */
+
+/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
+
+var saveAs = saveAs
+ || (navigator.msSaveBlob && navigator.msSaveBlob.bind(navigator))
+ || (function(view) {
+ "use strict";
+ var
+ doc = view.document
+ // only get URL when necessary in case BlobBuilder.js hasn't overridden it yet
+ , get_URL = function() {
+ return view.URL || view.webkitURL || view;
+ }
+ , URL = view.URL || view.webkitURL || view
+ , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
+ , can_use_save_link = "download" in save_link
+ , click = function(node) {
+ var event = doc.createEvent("MouseEvents");
+ event.initMouseEvent(
+ "click", true, false, view, 0, 0, 0, 0, 0
+ , false, false, false, false, 0, null
+ );
+ node.dispatchEvent(event);
+ }
+ , webkit_req_fs = view.webkitRequestFileSystem
+ , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
+ , throw_outside = function (ex) {
+ (view.setImmediate || view.setTimeout)(function() {
+ throw ex;
+ }, 0);
+ }
+ , force_saveable_type = "application/octet-stream"
+ , fs_min_size = 0
+ , deletion_queue = []
+ , process_deletion_queue = function() {
+ var i = deletion_queue.length;
+ while (i--) {
+ var file = deletion_queue[i];
+ if (typeof file === "string") { // file is an object URL
+ URL.revokeObjectURL(file);
+ } else { // file is a File
+ file.remove();
+ }
+ }
+ deletion_queue.length = 0; // clear queue
+ }
+ , dispatch = function(filesaver, event_types, event) {
+ event_types = [].concat(event_types);
+ var i = event_types.length;
+ while (i--) {
+ var listener = filesaver["on" + event_types[i]];
+ if (typeof listener === "function") {
+ try {
+ listener.call(filesaver, event || filesaver);
+ } catch (ex) {
+ throw_outside(ex);
+ }
+ }
+ }
+ }
+ , FileSaver = function(blob, name) {
+ // First try a.download, then web filesystem, then object URLs
+ var
+ filesaver = this
+ , type = blob.type
+ , blob_changed = false
+ , object_url
+ , target_view
+ , get_object_url = function() {
+ var object_url = get_URL().createObjectURL(blob);
+ deletion_queue.push(object_url);
+ return object_url;
+ }
+ , dispatch_all = function() {
+ dispatch(filesaver, "writestart progress write writeend".split(" "));
+ }
+ // on any filesys errors revert to saving with object URLs
+ , fs_error = function() {
+ // don't create more object URLs than needed
+ if (blob_changed || !object_url) {
+ object_url = get_object_url(blob);
+ }
+ if (target_view) {
+ target_view.location.href = object_url;
+ } else {
+ window.open(object_url, "_blank");
+ }
+ filesaver.readyState = filesaver.DONE;
+ dispatch_all();
+ }
+ , abortable = function(func) {
+ return function() {
+ if (filesaver.readyState !== filesaver.DONE) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+ , create_if_not_found = {create: true, exclusive: false}
+ , slice
+ ;
+ filesaver.readyState = filesaver.INIT;
+ if (!name) {
+ name = "download";
+ }
+ if (can_use_save_link) {
+ object_url = get_object_url(blob);
+ save_link.href = object_url;
+ save_link.download = name;
+ click(save_link);
+ filesaver.readyState = filesaver.DONE;
+ dispatch_all();
+ return;
+ }
+ // Object and web filesystem URLs have a problem saving in Google Chrome when
+ // viewed in a tab, so I force save with application/octet-stream
+ // http://code.google.com/p/chromium/issues/detail?id=91158
+ if (view.chrome && type && type !== force_saveable_type) {
+ slice = blob.slice || blob.webkitSlice;
+ blob = slice.call(blob, 0, blob.size, force_saveable_type);
+ blob_changed = true;
+ }
+ // Since I can't be sure that the guessed media type will trigger a download
+ // in WebKit, I append .download to the filename.
+ // https://bugs.webkit.org/show_bug.cgi?id=65440
+ if (webkit_req_fs && name !== "download") {
+ name += ".download";
+ }
+ if (type === force_saveable_type || webkit_req_fs) {
+ target_view = view;
+ }
+ if (!req_fs) {
+ fs_error();
+ return;
+ }
+ fs_min_size += blob.size;
+ req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
+ fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
+ var save = function() {
+ dir.getFile(name, create_if_not_found, abortable(function(file) {
+ file.createWriter(abortable(function(writer) {
+ writer.onwriteend = function(event) {
+ target_view.location.href = file.toURL();
+ deletion_queue.push(file);
+ filesaver.readyState = filesaver.DONE;
+ dispatch(filesaver, "writeend", event);
+ };
+ writer.onerror = function() {
+ var error = writer.error;
+ if (error.code !== error.ABORT_ERR) {
+ fs_error();
+ }
+ };
+ "writestart progress write abort".split(" ").forEach(function(event) {
+ writer["on" + event] = filesaver["on" + event];
+ });
+ writer.write(blob);
+ filesaver.abort = function() {
+ writer.abort();
+ filesaver.readyState = filesaver.DONE;
+ };
+ filesaver.readyState = filesaver.WRITING;
+ }), fs_error);
+ }), fs_error);
+ };
+ dir.getFile(name, {create: false}, abortable(function(file) {
+ // delete file if it already exists
+ file.remove();
+ save();
+ }), abortable(function(ex) {
+ if (ex.code === ex.NOT_FOUND_ERR) {
+ save();
+ } else {
+ fs_error();
+ }
+ }));
+ }), fs_error);
+ }), fs_error);
+ }
+ , FS_proto = FileSaver.prototype
+ , saveAs = function(blob, name) {
+ return new FileSaver(blob, name);
+ }
+ ;
+ FS_proto.abort = function() {
+ var filesaver = this;
+ filesaver.readyState = filesaver.DONE;
+ dispatch(filesaver, "abort");
+ };
+ FS_proto.readyState = FS_proto.INIT = 0;
+ FS_proto.WRITING = 1;
+ FS_proto.DONE = 2;
+
+ FS_proto.error =
+ FS_proto.onwritestart =
+ FS_proto.onprogress =
+ FS_proto.onwrite =
+ FS_proto.onabort =
+ FS_proto.onerror =
+ FS_proto.onwriteend =
+ null;
+
+ view.addEventListener("unload", process_deletion_queue, false);
+ return saveAs;
+}(self));