<commit_before>/*jshint esversion: 6,node: true,-W041: false */
'use strict';
var debug = require('debug')('FakeGatoStorageDrive');
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
var path = require('path');
var os = require('os');

module.exports = {
    drive: drive
};

// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/drive-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/drive'];
var TOKEN_DIR = path.join((process.env.HOME || process.env.HOMEPATH ||
    process.env.USERPROFILE || os.homedir()) , '.homebridge');
var TOKEN_PATH = path.join(TOKEN_DIR , 'drive-nodejs-quickstart.json');
var SECRET_PATH = path.join(TOKEN_DIR , 'client_secret.json');
var auth;

function drive(params) {
	if(params && params.keyPath) {
		TOKEN_DIR = params.keyPath;
		TOKEN_PATH = path.join(TOKEN_DIR , 'drive-nodejs-quickstart.json');
		SECRET_PATH = path.join(TOKEN_DIR , 'client_secret.json');
	}

    // Load client secrets from a local file.
    fs.readFile(SECRET_PATH, function processClientSecrets(err, content) {
        if (err) {
            console.log('Error loading client secret file, please follow the instructions in the README!!!' , err);
            return;
        }
        // Authorize a client with the loaded credentials, then call the
        // Drive API.
        authorize(JSON.parse(content), function(authenticated) {

            auth = authenticated;
            debug("Authenticated",content,params);
			if(params && typeof(params.callback) === 'function') params.callback();
        });
    });

}

drive.prototype.writeFile = function(folder, name, data, cb) {
    // get folder ID

    if (auth) {
		debug("getFolder",folder);
        getFolder(folder,function(err, folder) {
            debug("upload",name);
            myUploadFile(folder, name, data, cb);
        });
    } else {
		debug("NO AUTH YET (Not normal)");
		var thisDrive=this;
		setTimeout(function(){
			thisDrive.writeFile(folder, name, data, cb);
		},100);
	}
};
drive.prototype.readFile = function(folder, name, cb) {

    if (auth) {
		debug("getFolder",folder);
		getFolder(folder,function(err, folder) {
			debug("download %s/%s",folder,name);
			myDownloadFile(folder, name, cb);
		});
    } else {
		debug("NO AUTH YET (Not normal)");
		var thisDrive=this;
		setTimeout(function(){
			thisDrive.readFile(folder, name, cb);
		},100);
	}
};
drive.prototype.deleteFile = function(folder, name, cb) {

    if (auth) {
		debug("getFolder",folder);
		getFolder(folder,function(err, folder) {
			debug("delete",name);
			myDeleteFile(folder, name, cb);
		});
    } else {
		debug("NO AUTH YET (Not normal)");
		var thisDrive=this;
		setTimeout(function(){
			thisDrive.deleteFile(folder, name, cb);
		},100);
	}
};

function getFolder(folder,cb) {
    var drive = google.drive('v3');
    debug("getFolder",folder);
    drive.files.list({
        q: "mimeType='application/vnd.google-apps.folder' and name = '"+folder+"' and trashed = false",
        fields: 'nextPageToken, files(id, name)',
        spaces: 'drive',
        auth: auth
    }, function(err, res) {
        if (err) {
          debug("getFolder - err",err);
            cb(err);
        } else {
            if (res.files.length > 0) {
              if(res.files.length > 1) {
                debug("Multiple folders with same name, taking the first one",folder,'in',res.files);
              }
                    debug('Found Folder: ', res.files[0].name, res.files[0].id);
                    cb(null, res.files[0].id);
            } else {
                var fileMetadata = {
                    'name': folder,
                    'mimeType': 'application/vnd.google-apps.folder'
                };
                drive.files.create({
                    resource: fileMetadata,
                    fields: 'id',
                    auth: auth
                }, function(err, file) {
                    if (err) {
                        // Handle error
                        debug(err);
                    } else {
                        debug("Created Folder", file.id);
                        cb(null, file.id);
                    }
                });
            }
        }
    });
}

function getFileID(folder, name, cb) {
    var drive = google.drive('v3');
	debug("GET FILE ID : %s/%s",folder,name);
    drive.files.list({
        q: "name = '"+name+"' and trashed = false and '"+folder+"' in parents",
        fields: 'files(id, name)',
        spaces: 'drive',
		//parents: [folder],
        auth: auth
    }, function(err,result){
		debug("GET FILE ID result",result,err);
		cb(err,result);
	});
}

function myUploadFile(folder, name, data, cb) {
    var drive = google.drive('v3');

    debug("upload File %s\%s", folder, name);

    var fileMetadata = {
        'name': name,
        parents: [folder]
    };
    var media = {
        mimeType: 'application/json',
        body: JSON.stringify(data)
    };
	getFileID(folder, name, function(err,result) {
	//	debug("fileID for %s/%s is :",folder,name,result,err);
		if(result && result.files && result.files.length === 1) {
			drive.files.update({
				fileId: result.files[0].id,
        media: media,
				auth: auth
			},function(err, file){
				if(err)
        {
					debug('FILEUPDATE :',err);
        } else {
          debug('myUploadFile - update success',name);
        }
				cb(err,file);
			});
		} else {
			debug("no file found, creating",name,fileMetadata,media);
			drive.files.create({
				resource: fileMetadata,
				media: media,
				fields: 'id',
				auth: auth
			}, function(err,file){
				if(err)
        {
					debug('FILECREATE :',file,err);
        } else {
          debug('myUploadFile - create success',name);
        }
				cb(err,file);
			});
		}

	});

}

function myDownloadFile(folder, name, cb) {
    var drive = google.drive('v3');

    debug("download file", folder, name);

	getFileID(folder, name, function(err,result){
		if(result && result.files && result.files.length) {
			if(result.files.length > 1) {
				debug("Multiple files with same name, taking the first one",name,'in',result.files);
			}
			drive.files.get({
				fileId: result.files[0].id,
				alt: 'media',
				auth: auth
			},function(err,success){
				if(err) debug("ERROR downloading",err);
				else debug("SUCCESS downloading",success);
				cb(err,success);
			});
		} else {
			debug("no file found",name);
			cb(new Error("File not found"),false);
		}
	});

}

function myDeleteFile(folder, name, cb) {
    var drive = google.drive('v3');

    debug("delete file", folder, name);

	getFileID(folder, name, function(err,result){
		if(result && result.files && result.files.length) {
			if(result.files.length > 1) {
				debug("Multiple files with same name, taking the first one",name,'in',result.files);
			}
			drive.files.delete({
				fileId: result.files[0].id,
				auth: auth
			},function(err,success){
				if(err) debug("ERROR deleting",err);
				else debug("SUCCESS deleting",success);
				cb(err,success);
			});
		} else {
			debug("no file found",name);
			cb(null,false);
		}
	});

}

// This is all from the Google Drive Quickstart

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 *
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
    var clientSecret = credentials.installed.client_secret;
    var clientId = credentials.installed.client_id;
    var redirectUrl = credentials.installed.redirect_uris[0];
    var auth = new googleAuth();
    var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

    // Check if we have previously stored a token.
    fs.readFile(TOKEN_PATH, function(err, token) {
        if (err) {
            getNewToken(oauth2Client, callback);
        } else {
            oauth2Client.credentials = JSON.parse(token);
            callback(oauth2Client);
        }
    });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 *
 * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback to call with the authorized
 *     client.
 */
function getNewToken(oauth2Client, callback) {
    var authUrl = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: SCOPES
    });
    console.log('Authorize this app by visiting this url: ', authUrl);
    var rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    rl.question('Enter the code from that page here: ', function(code) {
        rl.close();
        oauth2Client.getToken(code, function(err, token) {
            if (err) {
                console.log('Error while trying to retrieve access token', err);
                return;
            }
            oauth2Client.credentials = token;
            storeToken(token);
            callback(oauth2Client);
        });
    });
}

/**
 * Store token to disk be used in later program executions.
 *
 * @param {Object} token The token to store to disk.
 */
function storeToken(token) {
    try {
        fs.mkdirSync(TOKEN_DIR);
    } catch (err) {
        if (err.code != 'EEXIST') {
            throw err;
        }
    }
    fs.writeFile(TOKEN_PATH, JSON.stringify(token));
    console.log('Token stored to ' + TOKEN_PATH);
}

/**
 * Lists the names and IDs of up to 10 files.
 *
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
function listFiles(auth) {
    var drive = google.drive('v3');
    drive.files.list({
        auth: auth,
        pageSize: 30,
        fields: "nextPageToken, files(id, name)"
    }, function(err, response) {
        if (err) {
            console.log('The API returned an error: ' + err);
            return;
        }
        var files = response.files;
        if (files.length == 0) {
            debug('No files found.');
        } else {
            debug('Files:');
            for (var i = 0; i < files.length; i++) {
                var file = files[i];
                debug('%s (%s)', JSON.stringify(file, null, 2), file.name, file.id);
            }
        }
    });
}

function uploadFile(auth) {
    var drive = google.drive('v3');

    var fetchPage = function(pageToken, pageFn, callback) {
        drive.files.list({
            q: "mimeType='application/vnd.google-apps.folder' and name = 'Camera Pictures'",
            fields: 'nextPageToken, files(id, name)',
            spaces: 'drive',
            pageToken: pageToken,
            auth: auth
        }, function(err, res) {
            if (err) {
                callback(err);
            } else {
                res.files.forEach(function(file) {
                    debug('Found file: ', file.name, file.id);
                });
                if (res.nextPageToken) {
                    debug("Page token", res.nextPageToken);
                    pageFn(res.nextPageToken, pageFn, callback);
                } else {
                    callback();
                }
            }
        });
    };
    fetchPage(null, fetchPage, function(err) {
        if (err) {
            // Handle error
            console.log(err);
        } else {
            // All pages fetched
        }
    });

    var fileMetadata = {
        'name': 'Camera Pictures',
        'mimeType': 'application/vnd.google-apps.folder'
    };
    drive.files.create({
        resource: fileMetadata,
        fields: 'id',
        auth: auth
    }, function(err, file) {
        if (err) {
            // Handle error
            console.log(err);
        } else {
            debug('Folder Id: ', file.id);

            var fileMetadata = {
                'name': 'photo.jpg',
                parents: [file.id]
            };
            var media = {
                mimeType: 'image/jpeg',
                body: fs.createReadStream('photo.jpg')
            };

            drive.files.create({
                resource: fileMetadata,
                media: media,
                fields: 'id',
                auth: auth
            }, function(err, file) {
                if (err) {
                    // Handle error
                    console.log(err);
                } else {
                    debug('File Id: ', file.id);
                }
            });

        }
    });

}
<commit_msg>Fakegato storage (#27)<commit_after>/*jshint esversion: 6,node: true,-W041: false */
'use strict';
var debug = require('debug')('FakeGatoStorageDrive');
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
var path = require('path');
var os = require('os');

module.exports = {
    drive: drive
};

// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/drive-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/drive'];
var TOKEN_DIR = path.join((process.env.HOME || process.env.HOMEPATH ||
    process.env.USERPROFILE || os.homedir()) , '.homebridge');
var TOKEN_PATH = path.join(TOKEN_DIR , 'drive-nodejs-quickstart.json');
var SECRET_PATH = path.join(TOKEN_DIR , 'client_secret.json');
var auth;

function drive(params) {
	if(params && params.keyPath) {
		TOKEN_DIR = params.keyPath;
		TOKEN_PATH = path.join(TOKEN_DIR , 'drive-nodejs-quickstart.json');
		SECRET_PATH = path.join(TOKEN_DIR , 'client_secret.json');
	}

    // Load client secrets from a local file.
    fs.readFile(SECRET_PATH, function processClientSecrets(err, content) {
        if (err) {
            console.log('Error loading client secret file, please follow the instructions in the README!!!' , err);
            return;
        }
        // Authorize a client with the loaded credentials, then call the
        // Drive API.
        authorize(JSON.parse(content), function(authenticated) {

            auth = authenticated;
            debug("Authenticated",content,params);
			if(params && typeof(params.callback) === 'function') params.callback();
        });
    });

}

drive.prototype.writeFile = function(folder, name, data, cb) {
    // get folder ID
    if (auth) {
		if(this.updating!==true) {
			this.updating=true;
			debug("getFolder",folder);
			getFolder(folder,function(err, folder) {
				debug("upload",name);
				myUploadFile(folder, name, data, function(){
					this.updating=false;
					cb(arguments);
				}.bind(this));
			});
		} else {
			setTimeout(function(){
				this.writeFile(folder, name, data, cb);
			}.bind(this),100);
		}
    } else {
		debug("NO AUTH YET (Not normal)");
		setTimeout(function(){
			this.writeFile(folder, name, data, cb);
		}.bind(this),100);
	}
};
drive.prototype.readFile = function(folder, name, cb) {
    if (auth) {
		debug("getFolder",folder);
		getFolder(folder,function(err, folder) {
			debug("download %s/%s",folder,name);
			myDownloadFile(folder, name, cb);
		});
    } else {
		debug("NO AUTH YET (Not normal)");
		setTimeout(function(){
			this.readFile(folder, name, cb);
		}.bind(this),100);
	}
};
drive.prototype.deleteFile = function(folder, name, cb) {

    if (auth) {
		debug("getFolder",folder);
		getFolder(folder,function(err, folder) {
			debug("delete",name);
			myDeleteFile(folder, name, cb);
		});
    } else {
		debug("NO AUTH YET (Not normal)");
		setTimeout(function(){
			this.deleteFile(folder, name, cb);
		}.bind(this),100);
	}
};

function getFolder(folder,cb) {
    var drive = google.drive('v3');
    debug("getFolder",folder);
    drive.files.list({
        q: "mimeType='application/vnd.google-apps.folder' and name = '"+folder+"' and trashed = false",
        fields: 'nextPageToken, files(id, name)',
        spaces: 'drive',
        auth: auth
    }, function(err, res) {
        if (err) {
          debug("getFolder - err",err);
            cb(err);
        } else {
            if (res.files.length > 0) {
              if(res.files.length > 1) {
                debug("Multiple folders with same name, taking the first one",folder,'in',res.files);
              }
                    debug('Found Folder: ', res.files[0].name, res.files[0].id);
                    cb(null, res.files[0].id);
            } else {
                var fileMetadata = {
                    'name': folder,
                    'mimeType': 'application/vnd.google-apps.folder'
                };
                drive.files.create({
                    resource: fileMetadata,
                    fields: 'id',
                    auth: auth
                }, function(err, file) {
                    if (err) {
                        // Handle error
                        debug(err);
                    } else {
                        debug("Created Folder", file.id);
                        cb(null, file.id);
                    }
                });
            }
        }
    });
}

function getFileID(folder, name, cb) {
    var drive = google.drive('v3');
	debug("GET FILE ID : %s/%s",folder,name);
    drive.files.list({
        q: "name = '"+name+"' and trashed = false and '"+folder+"' in parents",
        fields: 'files(id, name)',
        spaces: 'drive',
		//parents: [folder],
        auth: auth
    }, function(err,result){
		debug("GET FILE ID result",result,err);
		cb(err,result);
	});
}

function myUploadFile(folder, name, data, cb) {
    var drive = google.drive('v3');

    debug("upload File %s\%s", folder, name);

    var fileMetadata = {
        'name': name,
        parents: [folder]
    };
    var media = {
        mimeType: 'application/json',
        body: JSON.stringify(data)
    };
	getFileID(folder, name, function(err,result) {
	//	debug("fileID for %s/%s is :",folder,name,result,err);
		if(result && result.files && result.files.length === 1) {
			drive.files.update({
				fileId: result.files[0].id,
        media: media,
				auth: auth
			},function(err, file){
				if(err)
        {
					debug('FILEUPDATE :',err);
        } else {
          debug('myUploadFile - update success',name);
        }
				cb(err,file);
			});
		} else {
			debug("no file found, creating",name,fileMetadata,media);
			drive.files.create({
				resource: fileMetadata,
				media: media,
				fields: 'id',
				auth: auth
			}, function(err,file){
				if(err)
        {
					debug('FILECREATE :',file,err);
        } else {
          debug('myUploadFile - create success',name);
        }
				cb(err,file);
			});
		}

	});

}

function myDownloadFile(folder, name, cb) {
    var drive = google.drive('v3');

    debug("download file", folder, name);

	getFileID(folder, name, function(err,result){
		if(result && result.files && result.files.length) {
			if(result.files.length > 1) {
				debug("Multiple files with same name, taking the first one",name,'in',result.files);
			}
			drive.files.get({
				fileId: result.files[0].id,
				alt: 'media',
				auth: auth
			},function(err,success){
				if(err) debug("ERROR downloading",err);
				else debug("SUCCESS downloading",success);
				cb(err,success);
			});
		} else {
			debug("no file found",name);
			cb(new Error("File not found"),false);
		}
	});

}

function myDeleteFile(folder, name, cb) {
    var drive = google.drive('v3');

    debug("delete file", folder, name);

	getFileID(folder, name, function(err,result){
		if(result && result.files && result.files.length) {
			if(result.files.length > 1) {
				debug("Multiple files with same name, taking the first one",name,'in',result.files);
			}
			drive.files.delete({
				fileId: result.files[0].id,
				auth: auth
			},function(err,success){
				if(err) debug("ERROR deleting",err);
				else debug("SUCCESS deleting",success);
				cb(err,success);
			});
		} else {
			debug("no file found",name);
			cb(null,false);
		}
	});

}

// This is all from the Google Drive Quickstart

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 *
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
    var clientSecret = credentials.installed.client_secret;
    var clientId = credentials.installed.client_id;
    var redirectUrl = credentials.installed.redirect_uris[0];
    var auth = new googleAuth();
    var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

    // Check if we have previously stored a token.
    fs.readFile(TOKEN_PATH, function(err, token) {
        if (err) {
            getNewToken(oauth2Client, callback);
        } else {
            oauth2Client.credentials = JSON.parse(token);
            callback(oauth2Client);
        }
    });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 *
 * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback to call with the authorized
 *     client.
 */
function getNewToken(oauth2Client, callback) {
    var authUrl = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: SCOPES
    });
    console.log('Authorize this app by visiting this url: ', authUrl);
    var rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    rl.question('Enter the code from that page here: ', function(code) {
        rl.close();
        oauth2Client.getToken(code, function(err, token) {
            if (err) {
                console.log('Error while trying to retrieve access token', err);
                return;
            }
            oauth2Client.credentials = token;
            storeToken(token);
            callback(oauth2Client);
        });
    });
}

/**
 * Store token to disk be used in later program executions.
 *
 * @param {Object} token The token to store to disk.
 */
function storeToken(token) {
    try {
        fs.mkdirSync(TOKEN_DIR);
    } catch (err) {
        if (err.code != 'EEXIST') {
            throw err;
        }
    }
    fs.writeFile(TOKEN_PATH, JSON.stringify(token));
    console.log('Token stored to ' + TOKEN_PATH);
}

/**
 * Lists the names and IDs of up to 10 files.
 *
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
function listFiles(auth) {
    var drive = google.drive('v3');
    drive.files.list({
        auth: auth,
        pageSize: 30,
        fields: "nextPageToken, files(id, name)"
    }, function(err, response) {
        if (err) {
            console.log('The API returned an error: ' + err);
            return;
        }
        var files = response.files;
        if (files.length == 0) {
            debug('No files found.');
        } else {
            debug('Files:');
            for (var i = 0; i < files.length; i++) {
                var file = files[i];
                debug('%s (%s)', JSON.stringify(file, null, 2), file.name, file.id);
            }
        }
    });
}

function uploadFile(auth) {
    var drive = google.drive('v3');

    var fetchPage = function(pageToken, pageFn, callback) {
        drive.files.list({
            q: "mimeType='application/vnd.google-apps.folder' and name = 'Camera Pictures'",
            fields: 'nextPageToken, files(id, name)',
            spaces: 'drive',
            pageToken: pageToken,
            auth: auth
        }, function(err, res) {
            if (err) {
                callback(err);
            } else {
                res.files.forEach(function(file) {
                    debug('Found file: ', file.name, file.id);
                });
                if (res.nextPageToken) {
                    debug("Page token", res.nextPageToken);
                    pageFn(res.nextPageToken, pageFn, callback);
                } else {
                    callback();
                }
            }
        });
    };
    fetchPage(null, fetchPage, function(err) {
        if (err) {
            // Handle error
            console.log(err);
        } else {
            // All pages fetched
        }
    });

    var fileMetadata = {
        'name': 'Camera Pictures',
        'mimeType': 'application/vnd.google-apps.folder'
    };
    drive.files.create({
        resource: fileMetadata,
        fields: 'id',
        auth: auth
    }, function(err, file) {
        if (err) {
            // Handle error
            console.log(err);
        } else {
            debug('Folder Id: ', file.id);

            var fileMetadata = {
                'name': 'photo.jpg',
                parents: [file.id]
            };
            var media = {
                mimeType: 'image/jpeg',
                body: fs.createReadStream('photo.jpg')
            };

            drive.files.create({
                resource: fileMetadata,
                media: media,
                fields: 'id',
                auth: auth
            }, function(err, file) {
                if (err) {
                    // Handle error
                    console.log(err);
                } else {
                    debug('File Id: ', file.id);
                }
            });

        }
    });

}
<|endoftext|>


<commit_before>var ms = require("mustache");
var fs = require('fs');

Blend.defineClass('Logger', {
    singleton: true,
    errors: null,
    /**
     * @type {colors} reference to the colors package
     */
    colors: null,
    /**
     * Class ctor
     * @override
     */
    init: function () {
        var me = this;
        me.callParent.apply(me, arguments);
        var me = this;
        if (!me.colors) {
            me.colors = require('colors');
        }
    },
    dumpErrors: function (filename) {
        var me = this;
        if (FileUtils.ensurePath(filename)) {
            fs.writeFileSync(filename, ms.render('{{#lines}}
{{.}}
{{/lines}}', {
                lines: me.errors
            }));
        }
    },
    /**
     * Clears the errors array
     */
    clearErrors: function () {
        var me = this;
        me.errors = [];
    },
    /**
     * Logs a error message
     */
    error: function () {
        var me = this, message = me.createMessage('ERROR', arguments);
        me.errors = me.errors || [];
        me.errors.push(message);
        console.error(message.white.bgRed);
    },
    /**
     * Logs a warning message
     */
    warn: function () {
        var me = this;
        console.log(me.createMessage('WARN', arguments).white.bgMagenta);
    },
    /**
     * Logs an info message
     */
    info: function () {
        var me = this;
        console.log(me.createMessage('INFO', arguments));
    },
    /**
     * Creates a message to be logged to the console
     * @param {String} type INFO/ERROR/WARN
     * @param {String[]} pargs actuall message
     * @returns {String}
     */
    createMessage: function (type, pargs) {
        var me = this, args = [];
        Blend.foreach(pargs, function (itm) {
            args.push(itm);
        });
        return (type + "\t" + args.join(' ').trim());
    }
});
<commit_msg>Logger called toString on log parameters<commit_after>var ms = require("mustache");
var fs = require('fs');

Blend.defineClass('Logger', {
    singleton: true,
    errors: null,
    /**
     * @type {colors} reference to the colors package
     */
    colors: null,
    /**
     * Class ctor
     * @override
     */
    init: function () {
        var me = this;
        me.callParent.apply(me, arguments);
        var me = this;
        if (!me.colors) {
            me.colors = require('colors');
        }
    },
    dumpErrors: function (filename) {
        var me = this;
        if (FileUtils.ensurePath(filename)) {
            fs.writeFileSync(filename, ms.render('{{#lines}}
{{.}}
{{/lines}}', {
                lines: me.errors
            }));
        }
    },
    /**
     * Clears the errors array
     */
    clearErrors: function () {
        var me = this;
        me.errors = [];
    },
    /**
     * Logs a error message
     */
    error: function () {
        var me = this, message = me.createMessage('ERROR', arguments);
        me.errors = me.errors || [];
        me.errors.push(message);
        console.error(message.white.bgRed);
    },
    /**
     * Logs a warning message
     */
    warn: function () {
        var me = this;
        console.log(me.createMessage('WARN', arguments).white.bgMagenta);
    },
    /**
     * Logs an info message
     */
    info: function () {
        var me = this;
        console.log(me.createMessage('INFO', arguments));
    },
    /**
     * Creates a message to be logged to the console
     * @param {String} type INFO/ERROR/WARN
     * @param {String[]} pargs actuall message
     * @returns {String}
     */
    createMessage: function (type, pargs) {
        var me = this, args = [];
        Blend.foreach(pargs, function (itm) {
            args.push(itm.toString());
        });
        return (type + "\t" + args.join(' ').trim());
    }
});
<|endoftext|>


<commit_before>(function ($) {
    $.fn.app.userStatistic = function (method) {

        var sandbox;

        var methods = {
            init: function (options) {
                sandbox = $.extend(true, {}, this.app.userStatistic.defaults, options);
                return this.each(function () {
                    var $this = $(this);
                    helpers.initPage($this);
                });
            }
        };

        var helpers = {
            initPage: function(e) {
                $(".notification-box div").each(function() {
                    var _class = $(this).attr('class');
                    var content = $('.' + _class + '-l').html();
                    $(this).qtip({
                        content: content
                    });
                });
            }
        };

        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        }
        else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        }
        else {
            $.error('Method ' + method + ' does not exist in app.userStatistic plugin.');
        }
    };

    $.fn.app.userStatistic.defaults = {
    };
})(jQuery);

var app = app || {};

$(document).ready(function () {
    console.log(app);
    if (typeof app.userStatistic != 'undefined') {
        $('body').app('userStatistic', app.userStatistic);
    }
});<commit_msg>Убран дебаг<commit_after>(function ($) {
    $.fn.app.userStatistic = function (method) {

        var sandbox;

        var methods = {
            init: function (options) {
                sandbox = $.extend(true, {}, this.app.userStatistic.defaults, options);
                return this.each(function () {
                    var $this = $(this);
                    helpers.initPage($this);
                });
            }
        };

        var helpers = {
            initPage: function(e) {
                $(".notification-box div").each(function() {
                    var _class = $(this).attr('class');
                    var content = $('.' + _class + '-l').html();
                    $(this).qtip({
                        content: content
                    });
                });
            }
        };

        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        }
        else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        }
        else {
            $.error('Method ' + method + ' does not exist in app.userStatistic plugin.');
        }
    };

    $.fn.app.userStatistic.defaults = {
    };
})(jQuery);

var app = app || {};

$(document).ready(function () {
    if (typeof app.userStatistic != 'undefined') {
        $('body').app('userStatistic', app.userStatistic);
    }
});<|endoftext|>


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

/**
 * @module Etcher
 */

'use strict'

/* eslint-disable no-var */

var angular = require('angular')

/* eslint-enable no-var */

const electron = require('electron')
const Bluebird = require('bluebird')
const semver = require('semver')
const EXIT_CODES = require('../../shared/exit-codes')
const messages = require('../../shared/messages')
const s3Packages = require('../../shared/s3-packages')
const release = require('../../shared/release')
const store = require('./models/store')
const errors = require('../../shared/errors')
const packageJSON = require('../../../package.json')
const flashState = require('./models/flash-state')
const settings = require('./models/settings')
const windowProgress = require('./os/window-progress')
const analytics = require('./modules/analytics')
const updateNotifier = require('./components/update-notifier')
const availableDrives = require('./models/available-drives')
const selectionState = require('./models/selection-state')
const driveScanner = require('./modules/drive-scanner')
const osDialog = require('./os/dialog')
const exceptionReporter = require('./modules/exception-reporter')
const updateLock = require('./modules/update-lock')

/* eslint-disable lodash/prefer-lodash-method */

// Enable debug information from all modules that use `debug`
// See https://github.com/visionmedia/debug#browser-support
//
// Enable drivelist debugging information
// See https://github.com/resin-io-modules/drivelist
process.env.DRIVELIST_DEBUG = /drivelist|^\*$/i.test(process.env.DEBUG) ? '1' : ''
window.localStorage.debug = process.env.DEBUG

/**
 * @summary Environment variable blacklisted drives by path
 * @type {Array<String>}
 * @constant
 * @private
 */
const BLACKLISTED_DRIVES = settings.get('driveBlacklist')

const app = angular.module('Etcher', [
  require('angular-ui-router'),
  require('angular-ui-bootstrap'),
  require('angular-if-state'),

  // Components
  require('./components/svg-icon'),
  require('./components/warning-modal/warning-modal'),
  require('./components/safe-webview'),
  require('./components/file-selector'),

  // Pages
  require('./pages/main/main'),
  require('./pages/finish/finish'),
  require('./pages/settings/settings'),

  // OS
  require('./os/open-external/open-external'),
  require('./os/dropzone/dropzone'),

  // Utils
  require('./utils/manifest-bind/manifest-bind')
])

app.run(() => {
  console.log([
    ' _____ _       _',
    '|  ___| |     | |',
    '| |__ | |_ ___| |__   ___ _ __',
    '|  __|| __/ __| \'_ \\ / _ \\ \'__|',
    '| |___| || (__| | | |  __/ |',
    '\\____/ \\__\\___|_| |_|\\___|_|',
    '',
    'Interested in joining the Etcher team?',
    'Drop us a line at join+etcher@resin.io',
    '',
    `Version = ${packageJSON.version}, Type = ${packageJSON.packageType}`
  ].join('\n'))
})

app.run(() => {
  const currentVersion = packageJSON.version

  analytics.logEvent('Application start', {
    packageType: packageJSON.packageType,
    version: currentVersion
  })

  const shouldCheckForUpdates = updateNotifier.shouldCheckForUpdates({
    currentVersion,
    lastSleptUpdateNotifier: settings.get('lastSleptUpdateNotifier'),
    lastSleptUpdateNotifierVersion: settings.get('lastSleptUpdateNotifierVersion')
  })

  const isStableRelease = release.isStableRelease(currentVersion)
  const updatesEnabled = settings.get('updatesEnabled')

  if (!shouldCheckForUpdates || !updatesEnabled) {
    analytics.logEvent('Not checking for updates', {
      shouldCheckForUpdates,
      updatesEnabled,
      stable: isStableRelease
    })

    return Bluebird.resolve()
  }

  const updateSemverRange = packageJSON.updates.semverRange
  const includeUnstableChannel = settings.get('includeUnstableUpdateChannel')

  analytics.logEvent('Checking for updates', {
    currentVersion,
    stable: isStableRelease,
    updateSemverRange,
    includeUnstableChannel
  })

  return s3Packages.getLatestVersion(release.getReleaseType(currentVersion), {
    range: updateSemverRange,
    includeUnstableChannel
  }).then((latestVersion) => {
    if (semver.gte(currentVersion, latestVersion || '0.0.0')) {
      analytics.logEvent('Update notification skipped', {
        reason: 'Latest version'
      })
      return Bluebird.resolve()
    }

    // In case the internet connection is not good and checking the
    // latest published version takes too long, only show notify
    // the user about the new version if he didn't start the flash
    // process (e.g: selected an image), otherwise such interruption
    // might be annoying.
    if (selectionState.hasImage()) {
      analytics.logEvent('Update notification skipped', {
        reason: 'Image selected'
      })
      return Bluebird.resolve()
    }

    analytics.logEvent('Notifying update', {
      latestVersion
    })

    return updateNotifier.notify(latestVersion, {
      allowSleepUpdateCheck: isStableRelease
    })

  // If the error is an update user error, then we don't want
  // to bother users each time they open the app.
  // See: https://github.com/resin-io/etcher/issues/1525
  }).catch((error) => {
    return errors.isUserError(error) && error.code === 'UPDATE_USER_ERROR'
  }, (error) => {
    analytics.logEvent('Update check user error', {
      title: errors.getTitle(error),
      description: errors.getDescription(error)
    })
  }).catch(exceptionReporter.report)
})

app.run(() => {
  store.observe(() => {
    if (!flashState.isFlashing()) {
      return
    }

    const currentFlashState = flashState.getFlashState()
    const stateType = !currentFlashState.flashing && currentFlashState.verifying
      ? `Verifying ${currentFlashState.verifying}`
      : `Flashing ${currentFlashState.flashing}`

    // NOTE: There is usually a short time period between the `isFlashing()`
    // property being set, and the flashing actually starting, which
    // might cause some non-sense flashing state logs including
    // `undefined` values.
    analytics.logDebug(
      `${stateType} devices, ` +
      `${currentFlashState.percentage}% at ${currentFlashState.speed} MB/s ` +
      `(total ${currentFlashState.totalSpeed} MB/s) ` +
      `eta in ${currentFlashState.eta}s ` +
      `with ${currentFlashState.failed} failed devices`
    )

    windowProgress.set(currentFlashState)
  })
})

app.run(($timeout) => {
  driveScanner.on('devices', (drives) => {
    // Safely trigger a digest cycle.
    // In some cases, AngularJS doesn't acknowledge that the
    // available drives list has changed, and incorrectly
    // keeps asking the user to "Connect a drive".
    $timeout(() => {
      if (BLACKLISTED_DRIVES.length) {
        const allowedDrives = drives.filter((drive) => {
          return !BLACKLISTED_DRIVES.includes(drive.devicePath)
        })
        availableDrives.setDrives(allowedDrives)
      } else {
        availableDrives.setDrives(drives)
      }
    })
  })

  driveScanner.on('error', (error) => {
    // Stop the drive scanning loop in case of errors,
    // otherwise we risk presenting the same error over
    // and over again to the user, while also heavily
    // spamming our error reporting service.
    driveScanner.stop()

    return exceptionReporter.report(error)
  })

  driveScanner.start()
})

app.run(($window) => {
  let popupExists = false

  $window.addEventListener('beforeunload', (event) => {
    if (!flashState.isFlashing() || popupExists) {
      analytics.logEvent('Close application', {
        isFlashing: flashState.isFlashing()
      })
      return
    }

    // Don't close window while flashing
    event.returnValue = false

    // Don't open any more popups
    popupExists = true

    analytics.logEvent('Close attempt while flashing')

    osDialog.showWarning({
      confirmationLabel: 'Yes, quit',
      rejectionLabel: 'Cancel',
      title: 'Are you sure you want to close Etcher?',
      description: messages.warning.exitWhileFlashing()
    }).then((confirmed) => {
      if (confirmed) {
        analytics.logEvent('Close confirmed while flashing', {
          uuid: flashState.getFlashUuid()
        })

        // This circumvents the 'beforeunload' event unlike
        // electron.remote.app.quit() which does not.
        electron.remote.process.exit(EXIT_CODES.SUCCESS)
      }

      analytics.logEvent('Close rejected while flashing')
      popupExists = false
    }).catch(exceptionReporter.report)
  })

  /**
   * @summary Helper fn for events
   * @function
   * @private
   * @example
   * window.addEventListener('click', extendLock)
   */
  const extendLock = () => {
    updateLock.extend()
  }

  $window.addEventListener('click', extendLock)
  $window.addEventListener('touchstart', extendLock)

  // Initial update lock acquisition
  extendLock()
})

app.run(($rootScope) => {
  $rootScope.$on('$stateChangeSuccess', (event, toState, toParams, fromState) => {
    // Ignore first navigation
    if (!fromState.name) {
      return
    }

    analytics.logEvent('Navigate', {
      to: toState.name,
      from: fromState.name
    })
  })
})

app.config(($urlRouterProvider) => {
  $urlRouterProvider.otherwise('/main')
})

app.config(($provide) => {
  $provide.decorator('$exceptionHandler', ($delegate) => {
    return (exception, cause) => {
      exceptionReporter.report(exception)
      $delegate(exception, cause)
    }
  })
})

app.controller('HeaderController', function (OSOpenExternalService) {
  /**
   * @summary Open help page
   * @function
   * @public
   *
   * @description
   * This application will open either the image's support url, declared
   * in the archive `manifest.json`, or the default Etcher help page.
   *
   * @example
   * HeaderController.openHelpPage();
   */
  this.openHelpPage = () => {
    const DEFAULT_SUPPORT_URL = 'https://github.com/resin-io/etcher/blob/master/SUPPORT.md'
    const supportUrl = selectionState.getImageSupportUrl() || DEFAULT_SUPPORT_URL
    OSOpenExternalService.open(supportUrl)
  }

  /**
   * @summary Whether to show the help link
   * @function
   * @public
   *
   * @returns {Boolean}
   *
   * @example
   * HeaderController.shouldShowHelp()
   */
  this.shouldShowHelp = () => {
    return !settings.get('disableExternalLinks')
  }
})

app.controller('StateController', function ($rootScope, $scope) {
  const unregisterStateChange = $rootScope.$on('$stateChangeSuccess', (event, toState, toParams, fromState) => {
    this.previousName = fromState.name
    this.currentName = toState.name
  })

  $scope.$on('$destroy', unregisterStateChange)

  /**
   * @summary Get the previous state name
   * @function
   * @public
   *
   * @returns {String} previous state name
   *
   * @example
   * if (StateController.previousName === 'main') {
   *   console.log('We left the main screen!');
   * }
   */
  this.previousName = null

  /**
   * @summary Get the current state name
   * @function
   * @public
   *
   * @returns {String} current state name
   *
   * @example
   * if (StateController.currentName === 'main') {
   *   console.log('We are on the main screen!');
   * }
   */
  this.currentName = null
})

// Handle keyboard shortcut to open the settings
app.run(($state) => {
  electron.ipcRenderer.on('menu:preferences', () => {
    $state.go('settings')
  })
})

// Ensure user settings are loaded before
// we bootstrap the Angular.js application
angular.element(document).ready(() => {
  settings.load().then(() => {
    angular.bootstrap(document, [ 'Etcher' ])
  }).catch(exceptionReporter.report)
})
<commit_msg>fix(gui): Fix error with empty drive blacklist<commit_after>/*
 * Copyright 2016 resin.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @module Etcher
 */

'use strict'

/* eslint-disable no-var */

var angular = require('angular')

/* eslint-enable no-var */

const electron = require('electron')
const Bluebird = require('bluebird')
const semver = require('semver')
const EXIT_CODES = require('../../shared/exit-codes')
const messages = require('../../shared/messages')
const s3Packages = require('../../shared/s3-packages')
const release = require('../../shared/release')
const store = require('./models/store')
const errors = require('../../shared/errors')
const packageJSON = require('../../../package.json')
const flashState = require('./models/flash-state')
const settings = require('./models/settings')
const windowProgress = require('./os/window-progress')
const analytics = require('./modules/analytics')
const updateNotifier = require('./components/update-notifier')
const availableDrives = require('./models/available-drives')
const selectionState = require('./models/selection-state')
const driveScanner = require('./modules/drive-scanner')
const osDialog = require('./os/dialog')
const exceptionReporter = require('./modules/exception-reporter')
const updateLock = require('./modules/update-lock')

/* eslint-disable lodash/prefer-lodash-method */

// Enable debug information from all modules that use `debug`
// See https://github.com/visionmedia/debug#browser-support
//
// Enable drivelist debugging information
// See https://github.com/resin-io-modules/drivelist
process.env.DRIVELIST_DEBUG = /drivelist|^\*$/i.test(process.env.DEBUG) ? '1' : ''
window.localStorage.debug = process.env.DEBUG

const app = angular.module('Etcher', [
  require('angular-ui-router'),
  require('angular-ui-bootstrap'),
  require('angular-if-state'),

  // Components
  require('./components/svg-icon'),
  require('./components/warning-modal/warning-modal'),
  require('./components/safe-webview'),
  require('./components/file-selector'),

  // Pages
  require('./pages/main/main'),
  require('./pages/finish/finish'),
  require('./pages/settings/settings'),

  // OS
  require('./os/open-external/open-external'),
  require('./os/dropzone/dropzone'),

  // Utils
  require('./utils/manifest-bind/manifest-bind')
])

app.run(() => {
  console.log([
    ' _____ _       _',
    '|  ___| |     | |',
    '| |__ | |_ ___| |__   ___ _ __',
    '|  __|| __/ __| \'_ \\ / _ \\ \'__|',
    '| |___| || (__| | | |  __/ |',
    '\\____/ \\__\\___|_| |_|\\___|_|',
    '',
    'Interested in joining the Etcher team?',
    'Drop us a line at join+etcher@resin.io',
    '',
    `Version = ${packageJSON.version}, Type = ${packageJSON.packageType}`
  ].join('\n'))
})

app.run(() => {
  const currentVersion = packageJSON.version

  analytics.logEvent('Application start', {
    packageType: packageJSON.packageType,
    version: currentVersion
  })

  const shouldCheckForUpdates = updateNotifier.shouldCheckForUpdates({
    currentVersion,
    lastSleptUpdateNotifier: settings.get('lastSleptUpdateNotifier'),
    lastSleptUpdateNotifierVersion: settings.get('lastSleptUpdateNotifierVersion')
  })

  const isStableRelease = release.isStableRelease(currentVersion)
  const updatesEnabled = settings.get('updatesEnabled')

  if (!shouldCheckForUpdates || !updatesEnabled) {
    analytics.logEvent('Not checking for updates', {
      shouldCheckForUpdates,
      updatesEnabled,
      stable: isStableRelease
    })

    return Bluebird.resolve()
  }

  const updateSemverRange = packageJSON.updates.semverRange
  const includeUnstableChannel = settings.get('includeUnstableUpdateChannel')

  analytics.logEvent('Checking for updates', {
    currentVersion,
    stable: isStableRelease,
    updateSemverRange,
    includeUnstableChannel
  })

  return s3Packages.getLatestVersion(release.getReleaseType(currentVersion), {
    range: updateSemverRange,
    includeUnstableChannel
  }).then((latestVersion) => {
    if (semver.gte(currentVersion, latestVersion || '0.0.0')) {
      analytics.logEvent('Update notification skipped', {
        reason: 'Latest version'
      })
      return Bluebird.resolve()
    }

    // In case the internet connection is not good and checking the
    // latest published version takes too long, only show notify
    // the user about the new version if he didn't start the flash
    // process (e.g: selected an image), otherwise such interruption
    // might be annoying.
    if (selectionState.hasImage()) {
      analytics.logEvent('Update notification skipped', {
        reason: 'Image selected'
      })
      return Bluebird.resolve()
    }

    analytics.logEvent('Notifying update', {
      latestVersion
    })

    return updateNotifier.notify(latestVersion, {
      allowSleepUpdateCheck: isStableRelease
    })

  // If the error is an update user error, then we don't want
  // to bother users each time they open the app.
  // See: https://github.com/resin-io/etcher/issues/1525
  }).catch((error) => {
    return errors.isUserError(error) && error.code === 'UPDATE_USER_ERROR'
  }, (error) => {
    analytics.logEvent('Update check user error', {
      title: errors.getTitle(error),
      description: errors.getDescription(error)
    })
  }).catch(exceptionReporter.report)
})

app.run(() => {
  store.observe(() => {
    if (!flashState.isFlashing()) {
      return
    }

    const currentFlashState = flashState.getFlashState()
    const stateType = !currentFlashState.flashing && currentFlashState.verifying
      ? `Verifying ${currentFlashState.verifying}`
      : `Flashing ${currentFlashState.flashing}`

    // NOTE: There is usually a short time period between the `isFlashing()`
    // property being set, and the flashing actually starting, which
    // might cause some non-sense flashing state logs including
    // `undefined` values.
    analytics.logDebug(
      `${stateType} devices, ` +
      `${currentFlashState.percentage}% at ${currentFlashState.speed} MB/s ` +
      `(total ${currentFlashState.totalSpeed} MB/s) ` +
      `eta in ${currentFlashState.eta}s ` +
      `with ${currentFlashState.failed} failed devices`
    )

    windowProgress.set(currentFlashState)
  })
})

app.run(($timeout) => {
  driveScanner.on('devices', (drives) => {
    const BLACKLISTED_DRIVES = settings.has('driveBlacklist')
      ? settings.get('driveBlacklist').split(',')
      : []

    // Safely trigger a digest cycle.
    // In some cases, AngularJS doesn't acknowledge that the
    // available drives list has changed, and incorrectly
    // keeps asking the user to "Connect a drive".
    $timeout(() => {
      if (BLACKLISTED_DRIVES.length) {
        const allowedDrives = drives.filter((drive) => {
          return !BLACKLISTED_DRIVES.includes(drive.devicePath)
        })
        availableDrives.setDrives(allowedDrives)
      } else {
        availableDrives.setDrives(drives)
      }
    })
  })

  driveScanner.on('error', (error) => {
    // Stop the drive scanning loop in case of errors,
    // otherwise we risk presenting the same error over
    // and over again to the user, while also heavily
    // spamming our error reporting service.
    driveScanner.stop()

    return exceptionReporter.report(error)
  })

  driveScanner.start()
})

app.run(($window) => {
  let popupExists = false

  $window.addEventListener('beforeunload', (event) => {
    if (!flashState.isFlashing() || popupExists) {
      analytics.logEvent('Close application', {
        isFlashing: flashState.isFlashing()
      })
      return
    }

    // Don't close window while flashing
    event.returnValue = false

    // Don't open any more popups
    popupExists = true

    analytics.logEvent('Close attempt while flashing')

    osDialog.showWarning({
      confirmationLabel: 'Yes, quit',
      rejectionLabel: 'Cancel',
      title: 'Are you sure you want to close Etcher?',
      description: messages.warning.exitWhileFlashing()
    }).then((confirmed) => {
      if (confirmed) {
        analytics.logEvent('Close confirmed while flashing', {
          uuid: flashState.getFlashUuid()
        })

        // This circumvents the 'beforeunload' event unlike
        // electron.remote.app.quit() which does not.
        electron.remote.process.exit(EXIT_CODES.SUCCESS)
      }

      analytics.logEvent('Close rejected while flashing')
      popupExists = false
    }).catch(exceptionReporter.report)
  })

  /**
   * @summary Helper fn for events
   * @function
   * @private
   * @example
   * window.addEventListener('click', extendLock)
   */
  const extendLock = () => {
    updateLock.extend()
  }

  $window.addEventListener('click', extendLock)
  $window.addEventListener('touchstart', extendLock)

  // Initial update lock acquisition
  extendLock()
})

app.run(($rootScope) => {
  $rootScope.$on('$stateChangeSuccess', (event, toState, toParams, fromState) => {
    // Ignore first navigation
    if (!fromState.name) {
      return
    }

    analytics.logEvent('Navigate', {
      to: toState.name,
      from: fromState.name
    })
  })
})

app.config(($urlRouterProvider) => {
  $urlRouterProvider.otherwise('/main')
})

app.config(($provide) => {
  $provide.decorator('$exceptionHandler', ($delegate) => {
    return (exception, cause) => {
      exceptionReporter.report(exception)
      $delegate(exception, cause)
    }
  })
})

app.controller('HeaderController', function (OSOpenExternalService) {
  /**
   * @summary Open help page
   * @function
   * @public
   *
   * @description
   * This application will open either the image's support url, declared
   * in the archive `manifest.json`, or the default Etcher help page.
   *
   * @example
   * HeaderController.openHelpPage();
   */
  this.openHelpPage = () => {
    const DEFAULT_SUPPORT_URL = 'https://github.com/resin-io/etcher/blob/master/SUPPORT.md'
    const supportUrl = selectionState.getImageSupportUrl() || DEFAULT_SUPPORT_URL
    OSOpenExternalService.open(supportUrl)
  }

  /**
   * @summary Whether to show the help link
   * @function
   * @public
   *
   * @returns {Boolean}
   *
   * @example
   * HeaderController.shouldShowHelp()
   */
  this.shouldShowHelp = () => {
    return !settings.get('disableExternalLinks')
  }
})

app.controller('StateController', function ($rootScope, $scope) {
  const unregisterStateChange = $rootScope.$on('$stateChangeSuccess', (event, toState, toParams, fromState) => {
    this.previousName = fromState.name
    this.currentName = toState.name
  })

  $scope.$on('$destroy', unregisterStateChange)

  /**
   * @summary Get the previous state name
   * @function
   * @public
   *
   * @returns {String} previous state name
   *
   * @example
   * if (StateController.previousName === 'main') {
   *   console.log('We left the main screen!');
   * }
   */
  this.previousName = null

  /**
   * @summary Get the current state name
   * @function
   * @public
   *
   * @returns {String} current state name
   *
   * @example
   * if (StateController.currentName === 'main') {
   *   console.log('We are on the main screen!');
   * }
   */
  this.currentName = null
})

// Handle keyboard shortcut to open the settings
app.run(($state) => {
  electron.ipcRenderer.on('menu:preferences', () => {
    $state.go('settings')
  })
})

// Ensure user settings are loaded before
// we bootstrap the Angular.js application
angular.element(document).ready(() => {
  settings.load().then(() => {
    angular.bootstrap(document, [ 'Etcher' ])
  }).catch(exceptionReporter.report)
})
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

module.exports = {
    kernel_info_request: kernel_info_request,
    execute_request: execute_request,
    complete_request: complete_request,
    object_info_request: object_info_request,
    shutdown_request: shutdown_request,
};

var DEBUG = global.DEBUG || false;

var console = require("console");

var doc = require("./mdn.js"); // Documentation for Javascript builtins

/**
 * IPython protocol version
 * @member {Array.Number}
 * @private
 */
var protocolVersion = [4, 1];

/**
 * Node.js version (can be left as string in protocol v5)
 * @member {Array.Number}
 * @private
 */
var nodeVersion = process.versions.node.split('.').map(function(v) {
    return parseInt(v, 10);
});

/**
 * Handler for `kernel_info_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function kernel_info_request(request) {
    request.respond(
        this.shellSocket,
        "kernel_info_reply", {
            "language": "javascript",
            "language_version": nodeVersion,
            "protocol_version": protocolVersion,
        });
}

/**
 * Handler for `execute_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function execute_request(request) {
    function beforeRun(session) {
        session.executionCount++;
        request.respond(
            this.iopubSocket,
            "pyin", {
                execution_count: session.executionCount,
                code: request.content.code,
            }
        );
    }

    function afterRun(session) {
        if (session.result.stdout) {
            request.respond(
                this.iopubSocket,
                "stream", {
                    name: "stdout",
                    data: session.result.stdout,
                }
            );
        }

        if (session.result.stderr) {
            request.respond(
                this.iopubSocket,
                "stream", {
                    name: "stderr",
                    data: session.result.stderr,
                }
            );
        }
    }

    function onSuccess(session) {
        request.respond(
            this.shellSocket,
            "execute_reply", {
                status: "ok",
                execution_count: session.executionCount,
                payload: [], // TODO(NR) payload not implemented,
                user_variables: {}, // TODO(NR) user_variables not implemented,
                user_expressions: {}, // TODO(NR) user_expressions not implemented,
            }
        );

        request.respond(
            this.iopubSocket,
            "pyout", {
                execution_count: session.executionCount,
                data: session.result.mime,
                metadata: {},
            }
        );
    }

    function onError(session) {
        request.respond(
            this.shellSocket,
            "execute_reply", {
                status: "error",
                execution_count: session.executionCount,
                ename: session.result.error.ename,
                evalue: session.result.error.evalue,
                traceback: session.result.error.traceback,
            });

        request.respond(
            this.iopubSocket,
            "pyerr", {
                execution_count: session.executionCount,
                ename: session.result.error.ename,
                evalue: session.result.error.evalue,
                traceback: session.result.error.traceback,
            });
    }

    var task = {
        action: "run",
        code: request.content.code,
        beforeRun: beforeRun.bind(this),
        afterRun: afterRun.bind(this),
        onSuccess: onSuccess.bind(this),
        onError: onError.bind(this)
    };
    this.sm.run(task);
}

/**
 * RegExp for whitespace
 * @member {RegExp}
 * @private
 */
var whitespaceRE = /\s/;

/**
 * RegExp for a simple identifier in Javascript
 * @member {RegExp}
 * @private
 */
var simpleIdentifierRE = /[_$a-zA-Z][_$a-zA-Z0-9]*$/;

/**
 * RegExp for a complex identifier in Javascript
 * @member {RegExp}
 * @private
 */
var complexIdentifierRE = /[_$a-zA-Z][_$a-zA-Z0-9]*(?:[_$a-zA-Z][_$a-zA-Z0-9]*|\.[_$a-zA-Z][_$a-zA-Z0-9]*|\[".*"\]|\['.*'\])*$/;

/**
 * Parse a Javascript expression
 *
 * @param {String} line       Javascript code
 * @param {Number} cursorPos  Cursor position within `line`
 *
 * @returns          expression             Parsed expression
 * @returns {String} expression.matchedText Matched expression, e.g. `foo["bar`
 * @returns {String} expression.scope       Scope of the property being matched,
 *                                          e.g. `foo`
 * @returns {String} expression.leftOp      Left-hand-side selector operator,
 *                                          e.g. `["`
 * @returns {String} expression.selector    Stem of the property being matched,
 *                                          e.g. `bar`
 * @returns {String} expression.rightOp     Right-hand-side selector operator,
 *                                          e.g. `"]`
 *
 * @todo Parse expressions with parenthesis
 * @private
 */
function parseExpression(line, cursorPos) {
    var expression = line.slice(0, cursorPos);
    if (!expression ||
        whitespaceRE.test(expression[expression.length - 1])) {
        return {
            matchedText: "",
            scope: "",
            leftOp: "",
            selector: "",
            rightOp: "",
        };
    }

    var selector;
    var re = simpleIdentifierRE.exec(expression);
    if (re === null) {
        selector = "";
    } else {
        selector = re[0];
        expression = expression.slice(0, re.index);
    }

    var leftOp;
    var rightOp;
    if (expression[expression.length - 1] === '.') {
        leftOp = ".";
        rightOp = "";
        expression = expression.slice(0, expression.length - 1);
    } else if (
        (expression[expression.length - 2] === '[') &&
        (expression[expression.length - 1] === '"')
    ) {
        leftOp = "[\"";
        rightOp = "\"]";
        expression = expression.slice(0, expression.length - 2);
    } else if (
        (expression[expression.length - 2] === '[') &&
        (expression[expression.length - 1] === '\'')
    ) {
        leftOp = "['";
        rightOp = "']";
        expression = expression.slice(0, expression.length - 2);
    } else {
        return {
            matchedText: line.slice(expression.length, cursorPos),
            scope: "",
            leftOp: "",
            selector: selector,
            rightOp: "",
        };
    }

    var scope;
    re = complexIdentifierRE.exec(expression);
    if (re) {
        scope = re[0];
        return {
            matchedText: line.slice(re.index, cursorPos),
            scope: scope,
            leftOp: leftOp,
            selector: selector,
            rightOp: rightOp,
        };
    } else if (!leftOp) {
        scope = "";
        return {
            matchedText: line.slice(expression.length, cursorPos),
            scope: scope,
            leftOp: leftOp,
            selector: selector,
            rightOp: rightOp,
        };
    }

    // Not implemented
    return null;
}

/**
 * Handler for `complete_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function complete_request(request) {
    function onSuccess(matches) {
        // filter matches
        if (expression.selector) {
            matches = matches.filter(function(elt) {
                return elt.lastIndexOf(expression.selector, 0) === 0;
            });
        }

        // append expression.rightOp to each match
        var left = expression.scope + expression.leftOp;
        var right = expression.rightOp;
        if (left || right) {
            matches = matches.map(function(elt) {
                return left + elt + right;
            });
        }

        // send response
        var content = {
            matches: matches,
            matched_text: expression.matchedText,
            status: "ok",
        };
        request.respond(this.shellSocket, "complete_reply", content);
    }

    function onError() {
        var content = {
            matches: [],
            matched_text: request.content.text,
            status: "ok",
        };
        request.respond(this.shellSocket, "complete_reply", content);
    }

    // parse expression
    var expression = parseExpression(
        request.content.line,
        request.content.cursor_pos
    );
    if (DEBUG) console.log("KERNEL: COMPLETE: expression", expression);

    if (expression === null) {
        onError.apply(this);
        return;
    }

    // define a task to build the list of matches
    var task;
    if (expression.scope === "") {
        task = {
            action: "getAllPropertyNames",
            code: "global",
            onSuccess: (function(session) {
                // list of reserved words (ecma-262)
                var matches = [
                    // keywords
                    "break", "case", "catch", "continue", "debugger", "default",
                    "delete", "do", "else", "finally", "for", "function", "if",
                    "in", "instanceof", "new", "return", "switch", "this",
                    "throw", "try", "typeof", "var", "void", "while", "with",
                    // future reserved words
                    "class", "const", "enum", "export", "extends", "import",
                    "super",
                    // future reserved words in strict mode
                    "implements", "interface", "let", "package", "private",
                    "protected", "public", "static", "yield",
                    // null literal
                    "null",
                    // boolean literals
                    "true", "false"
                ];

                // append global properties
                matches = matches.concat(session.result.names);

                onSuccess.call(this, matches);
            }).bind(this),
            onError: onError.bind(this)
        };
    } else {
        // get instance properties
        task = {
            action: "getAllPropertyNames",
            code: expression.scope,
            onSuccess: (function(session) {
                onSuccess.call(this, session.result.names);
            }).bind(this),
            onError: onError.bind(this)
        };
    }
    this.sm.run(task);
    return;
}

/**
 * Handler for `object_info_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function object_info_request(request) {
    var name = request.content.oname;

    // Is a builtin object?
    var content = getBuiltinDoc(name);
    if (content !== null) {
        if (DEBUG) console.log("KERNEL: INSPECT: builtin object", name);
        respond.call(this, content);
        return;
    }

    // Is a builtin property?
    // To answer this question, parse expression and inspect scope
    var expression = parseExpression(name, name.length);
    if (DEBUG) console.log("KERNEL: INSPECT: PARSE:", expression);

    // If there is no scope, inspect object instead
    if (!expression.scope) {
        inspectObject.call(this);
        return;
    }

    // Build task to inspect the scope
    var task = {
        action: "inspect",
        code: expression.scope,
        onSuccess: (function(session) {
            for (var i in session.result.constructorList) {
                var constructorName = session.result.constructorList[i];
                var content = getBuiltinDoc(
                    constructorName +
                    ".prototype." +
                    expression.selector
                );
                if (content !== null) {
                    respond.call(this, content);
                    return;
                }
            }

            // No builtin found, inspect object instead
            inspectObject.call(this);
            return;
        }).bind(this),
        onError: inspectObject.bind(this),
    };
    this.sm.run(task);

    return;

    function inspectObject() {
        var task = {
            action: "inspect",
            code: name,
            onSuccess: (function(session) {
                var content = {
                    string_name: session.result.string,
                };

                if (session.result.constructorList.length >= 1) {
                    content.type_name = session.result.constructorList[0];
                } else {
                    content.type_name = session.result.type;
                }

                if (session.result.constructorList.length >= 2) {
                    content.base_name = session.result.constructorList[1];
                }

                if (session.result.length) {
                    content.length = session.result.length;
                }

                content.docstring = (
                    session.result.type + ": " + session.result.string
                );

                respond.call(this, content);
            }).bind(this),
            onError: (function(session) {
                var content = {
                    oname: name,
                    found: false,
                };
                request.respond(this.shellSocket, "object_info_reply", content);
            }).bind(this)
        };
        this.sm.run(task);

        return;
    }

    function getBuiltinName(name) {
        var builtinName = name;
        if (builtinName in doc) {
            return builtinName;
        }

        builtinName = name.replace(/^[a-zA-Z]+Error./, "Error.");
        if (builtinName in doc) {
            return builtinName;
        }

        builtinName = name.replace(/^[a-zA-Z]+Array./, "TypedArray.");
        if (builtinName in doc) {
            return builtinName;
        }

        return null;
    }

    function getBuiltinDoc(name) {
        var builtinName = getBuiltinName(name);
        if (builtinName === null) {
            return null;
        }

        var builtinDoc = doc[builtinName];

        var content = {
            namespace: "builtin",
            docstring: builtinDoc.description,
        };

        if (builtinDoc.usage) {
            content.definition = builtinDoc.usage;
        }

        return content;
    }

    function respond(content) {
        content.oname = name;
        content.found = true;
        content.ismagic = false;
        content.isalias = false;
        request.respond(this.shellSocket, "object_info_reply", content);
    }
}

/**
 * Handler for `shutdown_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function shutdown_request(request) {
    function respond(code, signal) {
        request.respond(
            this.controlSocket, "shutdown_reply", request.content
        );
    }

    if (request.content.restart) {
        this.restart(respond.bind(this));
    } else {
        this.destroy(respond.bind(this));
    }
}
<commit_msg>sm: Fix bug in `inspect` task<commit_after>#!/usr/bin/env node

/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

module.exports = {
    kernel_info_request: kernel_info_request,
    execute_request: execute_request,
    complete_request: complete_request,
    object_info_request: object_info_request,
    shutdown_request: shutdown_request,
};

var DEBUG = global.DEBUG || false;

var console = require("console");

var doc = require("./mdn.js"); // Documentation for Javascript builtins

/**
 * IPython protocol version
 * @member {Array.Number}
 * @private
 */
var protocolVersion = [4, 1];

/**
 * Node.js version (can be left as string in protocol v5)
 * @member {Array.Number}
 * @private
 */
var nodeVersion = process.versions.node.split('.').map(function(v) {
    return parseInt(v, 10);
});

/**
 * Handler for `kernel_info_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function kernel_info_request(request) {
    request.respond(
        this.shellSocket,
        "kernel_info_reply", {
            "language": "javascript",
            "language_version": nodeVersion,
            "protocol_version": protocolVersion,
        });
}

/**
 * Handler for `execute_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function execute_request(request) {
    function beforeRun(session) {
        session.executionCount++;
        request.respond(
            this.iopubSocket,
            "pyin", {
                execution_count: session.executionCount,
                code: request.content.code,
            }
        );
    }

    function afterRun(session) {
        if (session.result.stdout) {
            request.respond(
                this.iopubSocket,
                "stream", {
                    name: "stdout",
                    data: session.result.stdout,
                }
            );
        }

        if (session.result.stderr) {
            request.respond(
                this.iopubSocket,
                "stream", {
                    name: "stderr",
                    data: session.result.stderr,
                }
            );
        }
    }

    function onSuccess(session) {
        request.respond(
            this.shellSocket,
            "execute_reply", {
                status: "ok",
                execution_count: session.executionCount,
                payload: [], // TODO(NR) payload not implemented,
                user_variables: {}, // TODO(NR) user_variables not implemented,
                user_expressions: {}, // TODO(NR) user_expressions not implemented,
            }
        );

        request.respond(
            this.iopubSocket,
            "pyout", {
                execution_count: session.executionCount,
                data: session.result.mime,
                metadata: {},
            }
        );
    }

    function onError(session) {
        request.respond(
            this.shellSocket,
            "execute_reply", {
                status: "error",
                execution_count: session.executionCount,
                ename: session.result.error.ename,
                evalue: session.result.error.evalue,
                traceback: session.result.error.traceback,
            });

        request.respond(
            this.iopubSocket,
            "pyerr", {
                execution_count: session.executionCount,
                ename: session.result.error.ename,
                evalue: session.result.error.evalue,
                traceback: session.result.error.traceback,
            });
    }

    var task = {
        action: "run",
        code: request.content.code,
        beforeRun: beforeRun.bind(this),
        afterRun: afterRun.bind(this),
        onSuccess: onSuccess.bind(this),
        onError: onError.bind(this)
    };
    this.sm.run(task);
}

/**
 * RegExp for whitespace
 * @member {RegExp}
 * @private
 */
var whitespaceRE = /\s/;

/**
 * RegExp for a simple identifier in Javascript
 * @member {RegExp}
 * @private
 */
var simpleIdentifierRE = /[_$a-zA-Z][_$a-zA-Z0-9]*$/;

/**
 * RegExp for a complex identifier in Javascript
 * @member {RegExp}
 * @private
 */
var complexIdentifierRE = /[_$a-zA-Z][_$a-zA-Z0-9]*(?:[_$a-zA-Z][_$a-zA-Z0-9]*|\.[_$a-zA-Z][_$a-zA-Z0-9]*|\[".*"\]|\['.*'\])*$/;

/**
 * Parse a Javascript expression
 *
 * @param {String} line       Javascript code
 * @param {Number} cursorPos  Cursor position within `line`
 *
 * @returns          expression             Parsed expression
 * @returns {String} expression.matchedText Matched expression, e.g. `foo["bar`
 * @returns {String} expression.scope       Scope of the property being matched,
 *                                          e.g. `foo`
 * @returns {String} expression.leftOp      Left-hand-side selector operator,
 *                                          e.g. `["`
 * @returns {String} expression.selector    Stem of the property being matched,
 *                                          e.g. `bar`
 * @returns {String} expression.rightOp     Right-hand-side selector operator,
 *                                          e.g. `"]`
 *
 * @todo Parse expressions with parenthesis
 * @private
 */
function parseExpression(line, cursorPos) {
    var expression = line.slice(0, cursorPos);
    if (!expression ||
        whitespaceRE.test(expression[expression.length - 1])) {
        return {
            matchedText: "",
            scope: "",
            leftOp: "",
            selector: "",
            rightOp: "",
        };
    }

    var selector;
    var re = simpleIdentifierRE.exec(expression);
    if (re === null) {
        selector = "";
    } else {
        selector = re[0];
        expression = expression.slice(0, re.index);
    }

    var leftOp;
    var rightOp;
    if (expression[expression.length - 1] === '.') {
        leftOp = ".";
        rightOp = "";
        expression = expression.slice(0, expression.length - 1);
    } else if (
        (expression[expression.length - 2] === '[') &&
        (expression[expression.length - 1] === '"')
    ) {
        leftOp = "[\"";
        rightOp = "\"]";
        expression = expression.slice(0, expression.length - 2);
    } else if (
        (expression[expression.length - 2] === '[') &&
        (expression[expression.length - 1] === '\'')
    ) {
        leftOp = "['";
        rightOp = "']";
        expression = expression.slice(0, expression.length - 2);
    } else {
        return {
            matchedText: line.slice(expression.length, cursorPos),
            scope: "",
            leftOp: "",
            selector: selector,
            rightOp: "",
        };
    }

    var scope;
    re = complexIdentifierRE.exec(expression);
    if (re) {
        scope = re[0];
        return {
            matchedText: line.slice(re.index, cursorPos),
            scope: scope,
            leftOp: leftOp,
            selector: selector,
            rightOp: rightOp,
        };
    } else if (!leftOp) {
        scope = "";
        return {
            matchedText: line.slice(expression.length, cursorPos),
            scope: scope,
            leftOp: leftOp,
            selector: selector,
            rightOp: rightOp,
        };
    }

    // Not implemented
    return null;
}

/**
 * Handler for `complete_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function complete_request(request) {
    function onSuccess(matches) {
        // filter matches
        if (expression.selector) {
            matches = matches.filter(function(elt) {
                return elt.lastIndexOf(expression.selector, 0) === 0;
            });
        }

        // append expression.rightOp to each match
        var left = expression.scope + expression.leftOp;
        var right = expression.rightOp;
        if (left || right) {
            matches = matches.map(function(elt) {
                return left + elt + right;
            });
        }

        // send response
        var content = {
            matches: matches,
            matched_text: expression.matchedText,
            status: "ok",
        };
        request.respond(this.shellSocket, "complete_reply", content);
    }

    function onError() {
        var content = {
            matches: [],
            matched_text: request.content.text,
            status: "ok",
        };
        request.respond(this.shellSocket, "complete_reply", content);
    }

    // parse expression
    var expression = parseExpression(
        request.content.line,
        request.content.cursor_pos
    );
    if (DEBUG) console.log("KERNEL: COMPLETE: expression", expression);

    if (expression === null) {
        onError.apply(this);
        return;
    }

    // define a task to build the list of matches
    var task;
    if (expression.scope === "") {
        task = {
            action: "getAllPropertyNames",
            code: "global",
            onSuccess: (function(session) {
                // list of reserved words (ecma-262)
                var matches = [
                    // keywords
                    "break", "case", "catch", "continue", "debugger", "default",
                    "delete", "do", "else", "finally", "for", "function", "if",
                    "in", "instanceof", "new", "return", "switch", "this",
                    "throw", "try", "typeof", "var", "void", "while", "with",
                    // future reserved words
                    "class", "const", "enum", "export", "extends", "import",
                    "super",
                    // future reserved words in strict mode
                    "implements", "interface", "let", "package", "private",
                    "protected", "public", "static", "yield",
                    // null literal
                    "null",
                    // boolean literals
                    "true", "false"
                ];

                // append global properties
                matches = matches.concat(session.result.names);

                onSuccess.call(this, matches);
            }).bind(this),
            onError: onError.bind(this)
        };
    } else {
        // get instance properties
        task = {
            action: "getAllPropertyNames",
            code: expression.scope,
            onSuccess: (function(session) {
                onSuccess.call(this, session.result.names);
            }).bind(this),
            onError: onError.bind(this)
        };
    }
    this.sm.run(task);
    return;
}

/**
 * Handler for `object_info_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function object_info_request(request) {
    var name = request.content.oname;

    // Is a builtin object?
    var content = getBuiltinDoc(name);
    if (content !== null) {
        if (DEBUG) console.log("KERNEL: INSPECT: builtin object", name);
        respond.call(this, content);
        return;
    }

    // Is a builtin property?
    // To answer this question, parse expression and inspect scope
    var expression = parseExpression(name, name.length);
    if (DEBUG) console.log("KERNEL: INSPECT: PARSE:", expression);

    // If there is no scope, inspect object instead
    if (!expression.scope) {
        inspectObject.call(this);
        return;
    }

    // Build task to inspect the scope
    var task = {
        action: "inspect",
        code: expression.scope,
        onSuccess: (function(session) {
            for (var i in session.result.constructorList) {
                var constructorName = session.result.constructorList[i];
                var content = getBuiltinDoc(
                    constructorName +
                    ".prototype." +
                    expression.selector
                );
                if (content !== null) {
                    respond.call(this, content);
                    return;
                }
            }

            // No builtin found, inspect object instead
            inspectObject.call(this);
            return;
        }).bind(this),
        onError: inspectObject.bind(this),
    };
    this.sm.run(task);

    return;

    function inspectObject() {
        var task = {
            action: "inspect",
            code: name,
            onSuccess: (function(session) {
                var content = {
                    string_name: session.result.string,
                };

                if (session.result.constructorList) {
                    if (session.result.constructorList.length >= 1) {
                        content.type_name = session.result.constructorList[0];
                    } else {
                        content.type_name = session.result.type;
                    }

                    if (session.result.constructorList.length >= 2) {
                        content.base_name = session.result.constructorList[1];
                    }
                }

                if (session.result.length) {
                    content.length = session.result.length;
                }

                content.docstring = (
                    session.result.type + ": " + session.result.string
                );

                respond.call(this, content);
            }).bind(this),
            onError: (function(session) {
                var content = {
                    oname: name,
                    found: false,
                };
                request.respond(this.shellSocket, "object_info_reply", content);
            }).bind(this)
        };
        this.sm.run(task);

        return;
    }

    function getBuiltinName(name) {
        var builtinName = name;
        if (builtinName in doc) {
            return builtinName;
        }

        builtinName = name.replace(/^[a-zA-Z]+Error./, "Error.");
        if (builtinName in doc) {
            return builtinName;
        }

        builtinName = name.replace(/^[a-zA-Z]+Array./, "TypedArray.");
        if (builtinName in doc) {
            return builtinName;
        }

        return null;
    }

    function getBuiltinDoc(name) {
        var builtinName = getBuiltinName(name);
        if (builtinName === null) {
            return null;
        }

        var builtinDoc = doc[builtinName];

        var content = {
            namespace: "builtin",
            docstring: builtinDoc.description,
        };

        if (builtinDoc.usage) {
            content.definition = builtinDoc.usage;
        }

        return content;
    }

    function respond(content) {
        content.oname = name;
        content.found = true;
        content.ismagic = false;
        content.isalias = false;
        request.respond(this.shellSocket, "object_info_reply", content);
    }
}

/**
 * Handler for `shutdown_request` messages
 *
 * @param {module:jp~Message} request Request message
 * @this Kernel
 */
function shutdown_request(request) {
    function respond(code, signal) {
        request.respond(
            this.controlSocket, "shutdown_reply", request.content
        );
    }

    if (request.content.restart) {
        this.restart(respond.bind(this));
    } else {
        this.destroy(respond.bind(this));
    }
}
<|endoftext|>


<commit_before>var path = require('path'),
	fs = require('fs'),
	wrench = require('wrench'),
	DOMParser = require("xmldom").DOMParser,
	XMLSerializer = require("xmldom").XMLSerializer,
	_ = require('../lib/alloy/underscore')._,
	U = require('../utils'),
	alloyRoot = path.join(__dirname,'..');

function createPlugin(rootDir) {
	var plugins = path.join(rootDir,"plugins");
	U.ensureDir(plugins);

	var alloyPluginDir = path.join(plugins,"ti.alloy");
	U.ensureDir(alloyPluginDir);

	var alloyPlugin = path.join(alloyPluginDir,"plugin.py");
	var pi = path.join(alloyRoot,"plugin","plugin.py");

	U.copyFileSync(pi,alloyPlugin);
	logger.info('Deployed ti.alloy plugin to '+alloyPlugin);
}

function installPlugin(dir)
{
	createPlugin(dir);

	var tiapp = path.join(dir,'tiapp.xml');
	if (path.existsSync(tiapp))
	{
		var xml = fs.readFileSync(tiapp);
		var doc = new DOMParser().parseFromString(String(xml));
		var plugins = doc.documentElement.getElementsByTagName("plugins");
		var found = false;

		if (plugins.length > 0)
		{
			var items = plugins.item(0).getElementsByTagName('plugin');
			if (items.length > 0)
			{
				for (var c=0;c<items.length;c++)
				{
					var plugin = items.item(c);
					var name = U.XML.getNodeText(plugin);
					if (name == 'ti.alloy')
					{
						found = true;
						break;
					}
				}
			}
		}

		if (!found)
		{
			var node = doc.createElement('plugin');
			node.setAttribute('version','1.0');
			var text = doc.createTextNode('ti.alloy');
			node.appendChild(text);

			var pna = null;

			// install the plugin into tiapp.xml
			if (plugins.length == 0)
			{
				var pn = doc.createElement('plugins');
				doc.documentElement.appendChild(pn);
				doc.documentElement.appendChild(doc.createTextNode("\n"));
				pna = pn;
			}
			else
			{
				pna = plugins.item(0);
			}

			pna.appendChild(node);
			pna.appendChild(doc.createTextNode("\n"));

			var serializer = new XMLSerializer();
			var newxml = serializer.serializeToString(doc);

			fs.writeFileSync(tiapp,newxml);
			logger.info("Installed 'ti.alloy' plugin to "+tiapp);
		}
	}
}

function newproject(args, program) {
	var dirs = ['controllers','styles','views','models','migrations','config','assets','lib','vendor','template'],
		templateDir = path.join(alloyRoot,'template'),
		defaultDir = path.join(templateDir,'default'),
		INDEX_XML  = fs.readFileSync(path.join(defaultDir,'index.xml'),'utf8'),
		INDEX_JSON = fs.readFileSync(path.join(defaultDir,'index.json'),'utf8'),
		INDEX_C    = fs.readFileSync(path.join(defaultDir,'index.js'),'utf8'),
		defaultConfig = {},
		projectPath, appPath, tmpPath, alloyJmkTemplate, cfg;

	// validate args
	if (!_.isArray(args) || args.length === 0) {
		U.die("\"alloy new\" requires an [OUTPUT_DIR]");
	}

	// get app path, create if necessary
	projectPath = args[0];
	appPath = path.join(projectPath,'app');
	if (path.existsSync(appPath)) {
		if (!program.force) {
			U.die("Directory already exists at: " + appPath);
		} else {
			wrench.rmdirSyncRecursive(appPath);
		}
	}
	wrench.mkdirSyncRecursive(appPath, 0777);

	// create alloy app directories
	for (var c = 0; c < dirs.length; c++) {
		tmpPath = path.join(appPath, dirs[c]);
		if (!path.existsSync(tmpPath)) {
			fs.mkdirSync(tmpPath);
		}
	}

	// create default view, controller, style, and config.
	fs.writeFileSync(path.join(appPath,'views','index.xml'),INDEX_XML);
	fs.writeFileSync(path.join(appPath,'styles','index.json'),INDEX_JSON);
	fs.writeFileSync(path.join(appPath,'controllers','index.js'),INDEX_C);
	fs.writeFileSync(path.join(appPath,'alloy.json'),U.stringifyJSON(defaultConfig));

	// Copy templates
	U.copyFilesAndDirs(templateDir, path.join(appPath,'template'));

	// write the build file
	alloyJmkTemplate = fs.readFileSync(path.join(templateDir,'alloy.jmk'), 'utf8');
	fs.writeFileSync(path.join(appPath,'alloy.jmk'), alloyJmkTemplate);

	// write the project config file
	cfg = {global:{}, "env:development":{}, "env:test":{}, "env:production":{}, "os:ios":{}, "os:android":{}};
	fs.writeFileSync(path.join(appPath,"config","config.json"), U.stringifyJSON(cfg));

	// install the plugin
	installPlugin(projectPath);

	logger.info('Generated new project at: ' + appPath);
}

module.exports = newproject;<commit_msg>use wrench.mkdir instead of fs.mkdir<commit_after>var path = require('path'),
	fs = require('fs'),
	wrench = require('wrench'),
	DOMParser = require("xmldom").DOMParser,
	XMLSerializer = require("xmldom").XMLSerializer,
	_ = require('../lib/alloy/underscore')._,
	U = require('../utils'),
	alloyRoot = path.join(__dirname,'..');

function createPlugin(rootDir) {
	var plugins = path.join(rootDir,"plugins");
	U.ensureDir(plugins);

	var alloyPluginDir = path.join(plugins,"ti.alloy");
	U.ensureDir(alloyPluginDir);

	var alloyPlugin = path.join(alloyPluginDir,"plugin.py");
	var pi = path.join(alloyRoot,"plugin","plugin.py");

	U.copyFileSync(pi,alloyPlugin);
	logger.info('Deployed ti.alloy plugin to '+alloyPlugin);
}

function installPlugin(dir)
{
	createPlugin(dir);

	var tiapp = path.join(dir,'tiapp.xml');
	if (path.existsSync(tiapp))
	{
		var xml = fs.readFileSync(tiapp);
		var doc = new DOMParser().parseFromString(String(xml));
		var plugins = doc.documentElement.getElementsByTagName("plugins");
		var found = false;

		if (plugins.length > 0)
		{
			var items = plugins.item(0).getElementsByTagName('plugin');
			if (items.length > 0)
			{
				for (var c=0;c<items.length;c++)
				{
					var plugin = items.item(c);
					var name = U.XML.getNodeText(plugin);
					if (name == 'ti.alloy')
					{
						found = true;
						break;
					}
				}
			}
		}

		if (!found)
		{
			var node = doc.createElement('plugin');
			node.setAttribute('version','1.0');
			var text = doc.createTextNode('ti.alloy');
			node.appendChild(text);

			var pna = null;

			// install the plugin into tiapp.xml
			if (plugins.length == 0)
			{
				var pn = doc.createElement('plugins');
				doc.documentElement.appendChild(pn);
				doc.documentElement.appendChild(doc.createTextNode("\n"));
				pna = pn;
			}
			else
			{
				pna = plugins.item(0);
			}

			pna.appendChild(node);
			pna.appendChild(doc.createTextNode("\n"));

			var serializer = new XMLSerializer();
			var newxml = serializer.serializeToString(doc);

			fs.writeFileSync(tiapp,newxml);
			logger.info("Installed 'ti.alloy' plugin to "+tiapp);
		}
	}
}

function newproject(args, program) {
	var dirs = ['controllers','styles','views','models','migrations','config','assets','lib','vendor','template'],
		templateDir = path.join(alloyRoot,'template'),
		defaultDir = path.join(templateDir,'default'),
		INDEX_XML  = fs.readFileSync(path.join(defaultDir,'index.xml'),'utf8'),
		INDEX_JSON = fs.readFileSync(path.join(defaultDir,'index.json'),'utf8'),
		INDEX_C    = fs.readFileSync(path.join(defaultDir,'index.js'),'utf8'),
		defaultConfig = {},
		projectPath, appPath, tmpPath, alloyJmkTemplate, cfg;

	// validate args
	if (!_.isArray(args) || args.length === 0) {
		U.die("\"alloy new\" requires an [OUTPUT_DIR]");
	}

	// get app path, create if necessary
	projectPath = args[0];
	appPath = path.join(projectPath,'app');
	if (path.existsSync(appPath)) {
		if (!program.force) {
			U.die("Directory already exists at: " + appPath);
		} else {
			wrench.rmdirSyncRecursive(appPath);
		}
	}
	wrench.mkdirSyncRecursive(appPath, 0777);

	// create alloy app directories
	for (var c = 0; c < dirs.length; c++) {
		tmpPath = path.join(appPath, dirs[c]);
		if (!path.existsSync(tmpPath)) {
			wrench.mkdirSyncRecursive(tmpPath, 0777);
		}
	}

	// create default view, controller, style, and config.
	fs.writeFileSync(path.join(appPath,'views','index.xml'),INDEX_XML);
	fs.writeFileSync(path.join(appPath,'styles','index.json'),INDEX_JSON);
	fs.writeFileSync(path.join(appPath,'controllers','index.js'),INDEX_C);
	fs.writeFileSync(path.join(appPath,'alloy.json'),U.stringifyJSON(defaultConfig));

	// Copy templates
	U.copyFilesAndDirs(templateDir, path.join(appPath,'template'));

	// write the build file
	alloyJmkTemplate = fs.readFileSync(path.join(templateDir,'alloy.jmk'), 'utf8');
	fs.writeFileSync(path.join(appPath,'alloy.jmk'), alloyJmkTemplate);

	// write the project config file
	cfg = {global:{}, "env:development":{}, "env:test":{}, "env:production":{}, "os:ios":{}, "os:android":{}};
	fs.writeFileSync(path.join(appPath,"config","config.json"), U.stringifyJSON(cfg));

	// install the plugin
	installPlugin(projectPath);

	logger.info('Generated new project at: ' + appPath);
}

module.exports = newproject;<|endoftext|>