<commit_before>var Couch = require('./couch');

module.exports = function(connect) {
    var Store = connect.session.Store;

    function ConnectCouchDB(opts, fn)  {
        opts = opts || {};
        if (fn) {
            console.error('DEPRECATED usage of callback function in ConnectCouchDB\'s constructor');
        }

        Store.call(this, opts);

        this.db = null;
        this.reapInterval = opts.reapInterval || (10 * 60 * 1000);
        this.compactInterval = opts.compactInterval || (5 * 60 * 1000);

        // Even when _revs_limit is set to 1, old revisions take up space,
        // therefore, compact the database every once in awhile.
        if (this.reapInterval > 0)
            this._reap = setInterval(this.reap.bind(this), this.reapInterval);
        if (this.compactInterval > 0)
            this._compact = setInterval(this.compact.bind(this), this.compactInterval);

        if (!opts.name) {
            throw "You must define a database";
        }
        this.db = new Couch(opts);
    }

    function _check_error(err) {
        if (err !== null) {
            console.log("connect-couchdb error:");
            console.log(err);
            console.log(new Error().stack);
        }
    }

    function _uri_encode(id) {
        // We first decode it to escape any current URI encoding.
        return encodeURIComponent(decodeURIComponent(id));
    }

    ConnectCouchDB.prototype.__proto__ = Store.prototype;

    ConnectCouchDB.prototype.setupOptions = function (opts, fn) {
        this.db.putOpt('_revs_limit', opts.revs_limit, function(err) {
            _check_error(err);
            fn && fn(err);
        });
    }

    ConnectCouchDB.prototype.setupDesignDocs = function (fn) {
        this.db.putDesignDocs([__dirname + '/connect-session.json'], function(err) {
            _check_error(err);
            fn && fn(err);
        });
    }

    ConnectCouchDB.prototype.setupDatabase = function (fn) {
        this.db.get('', function(err, doc) {
            if (err && err.statusCode === 404) {
                this.db.dbPut(function(err) {
                    _check_error(err);
                    fn && fn(err);
                }.bind(this));
            }
            else {
                _check_error(err);
                fn && fn(err);
            }
        }.bind(this));
    };

    ConnectCouchDB.prototype.setup = function (opts, fn) {
        this.setupDatabase(function (err) {
            if (err) return fn && fn(err);
            this.setupDesignDocs(function (err) {
                if (err) return fn && fn(err);
                this.setupOptions(opts, function (err) {
                    fn && fn(err);
                });
            }.bind(this));
        }.bind(this));
    };

    ConnectCouchDB.prototype.get = function (sid, fn) {
        sid = _uri_encode(sid);
        var now = +new Date;
        this.db.get(sid, function (err, doc) {
            if (err) {
                if (err.error == "not_found") err.code = "ENOENT";
                return fn && fn(err);
            } else if (doc.expires && now >= doc.expires) {
                return fn && fn(null, null);
            } else {
                return fn && fn(null, doc.sess);
            }
        }.bind(this));
    };

    // Cache last session update for comparison below.
    var locks = {};

    ConnectCouchDB.prototype.set = function (sid, sess, fn) {
        sid = _uri_encode(sid);
        fn = fn || function () {};
        this.db.get(sid, function (err, doc) {
            var expires = typeof sess.cookie.maxAge === 'number'
                ? (+new Date()) + sess.cookie.maxAge
                : (+new Date()) + (24 * 60 * 60 * 1000);
            if (err) {
                doc = {
                    _id: sid,
                    expires: expires,
                    type: 'connect-session',
                    sess: sess
                };
                this.db.put(doc, fn);
            } else {
                // Temporarily remove properties for session comparison
                var _lastAccess = sess.lastAccess;
                var _originalMaxAge = sess.cookie.originalMaxAge;
                if (sess.cookie._expires) {
                    var _expires = sess.cookie._expires;
                    sess.cookie._expires = null;
                }
                if (doc.sess.cookie.expires) {
                    var _expiresDoc = doc.sess.cookie.expires;
                    doc.sess.cookie.expires = null;
                }
                sess.lastAccess = null;
                sess.cookie.originalMaxAge = null;
                doc.sess.lastAccess = null;
                doc.sess.cookie.originalMaxAge = null;
                // Compare new session to current session, save if different.
                if (JSON.stringify(doc.sess) !== JSON.stringify(sess)) {
                    sess.lastAccess = _lastAccess;
                    if (_expires) sess.cookie._expires = _expires;
                    sess.cookie.originalMaxAge = _originalMaxAge;
                    doc.expires = expires;
                    doc.sess = sess;
                    this.db.put(doc, fn);
                } else {
                    // Otherwise, only save if > 1s has elaspsed since last save.
                    if (!locks[sid] || expires > locks[sid] + 1000) {
                        locks[sid] = expires;
                        sess.lastAccess = _lastAccess;
                        if (_expires) sess.cookie._expires = _expires;
                        sess.cookie.originalMaxAge = _originalMaxAge;
                        doc.expires = expires;
                        doc.sess = sess;
                        this.db.put(doc, fn);
                    } else {
                        return fn();
                    }
                }
            }
        }.bind(this));
    };

    ConnectCouchDB.prototype.destroy = function (sid, fn) {
        sid = _uri_encode(sid);
        this.db.get(sid, function (err, doc) {
            if (err) return fn && fn(err);
            this.db.del(doc, fn);
        }.bind(this));
    };

    function destroy(docs, fn) {
        var self = this;
        function destroyDocAt(index) {
            if (index === docs.length) {
                return fn && fn();
            } else {
                self.destroy(docs[index].value._id, function() {
                    destroyDocAt(index+1);
                });
            }
        }
        destroyDocAt(0);
    }

    ConnectCouchDB.prototype.clear = function (fn) {
        this.db.view('_design/connect-sessions/_view/expires', {}, function (err, docs) {
            if (err) return fn && fn(err);
            destroy.call(this, docs.rows, fn);
        }.bind(this));
    };

    ConnectCouchDB.prototype.reap = function (fn) {
        var now = +new Date;
        var options = { endkey: '[' + now + ',{}]' };
        this.db.view('_design/connect-sessions/_view/expires', options, function (err, docs) {
            if (err) return fn && fn(err);
            destroy.call(this, docs.rows, fn);
        }.bind(this));
    };

    ConnectCouchDB.prototype.length = function (fn) {
        var now = +new Date;
        var options = { startkey: '[' + now + ',{}]' };
        this.db.view('_design/connect-sessions/_view/expires', options, function (err, docs) {
            if (err) {
                return fn && fn(err);
            } else {
                return fn && fn(err, docs.total_rows - docs.offset);
            }
        });
    };

    ConnectCouchDB.prototype.compact = function () {
        var obj = {endpoint: '_compact', doc: {}};
        this.db.post(obj, function(err, res) {});
    };

    ConnectCouchDB.prototype.clearInterval = function () {
        if (this._reap) clearInterval(this._reap);
        if (this._compact) clearInterval(this._compact);
    };

    return ConnectCouchDB;
}
<commit_msg>push grace up to 60s from 1s, so session will not be re-put if nothing has changed and it has been less than 1m since last put<commit_after>var Couch = require('./couch');

module.exports = function(connect) {
    var Store = connect.session.Store;

    function ConnectCouchDB(opts, fn)  {
        opts = opts || {};
        if (fn) {
            console.error('DEPRECATED usage of callback function in ConnectCouchDB\'s constructor');
        }

        Store.call(this, opts);

        this.db = null;
        this.reapInterval = opts.reapInterval || (10 * 60 * 1000);
        this.compactInterval = opts.compactInterval || (5 * 60 * 1000);

        // Even when _revs_limit is set to 1, old revisions take up space,
        // therefore, compact the database every once in awhile.
        if (this.reapInterval > 0)
            this._reap = setInterval(this.reap.bind(this), this.reapInterval);
        if (this.compactInterval > 0)
            this._compact = setInterval(this.compact.bind(this), this.compactInterval);

        if (!opts.name) {
            throw "You must define a database";
        }
        this.db = new Couch(opts);
    }

    function _check_error(err) {
        if (err !== null) {
            console.log("connect-couchdb error:");
            console.log(err);
            console.log(new Error().stack);
        }
    }

    function _uri_encode(id) {
        // We first decode it to escape any current URI encoding.
        return encodeURIComponent(decodeURIComponent(id));
    }

    ConnectCouchDB.prototype.__proto__ = Store.prototype;

    ConnectCouchDB.prototype.setupOptions = function (opts, fn) {
        this.db.putOpt('_revs_limit', opts.revs_limit, function(err) {
            _check_error(err);
            fn && fn(err);
        });
    }

    ConnectCouchDB.prototype.setupDesignDocs = function (fn) {
        this.db.putDesignDocs([__dirname + '/connect-session.json'], function(err) {
            _check_error(err);
            fn && fn(err);
        });
    }

    ConnectCouchDB.prototype.setupDatabase = function (fn) {
        this.db.get('', function(err, doc) {
            if (err && err.statusCode === 404) {
                this.db.dbPut(function(err) {
                    _check_error(err);
                    fn && fn(err);
                }.bind(this));
            }
            else {
                _check_error(err);
                fn && fn(err);
            }
        }.bind(this));
    };

    ConnectCouchDB.prototype.setup = function (opts, fn) {
        this.setupDatabase(function (err) {
            if (err) return fn && fn(err);
            this.setupDesignDocs(function (err) {
                if (err) return fn && fn(err);
                this.setupOptions(opts, function (err) {
                    fn && fn(err);
                });
            }.bind(this));
        }.bind(this));
    };

    ConnectCouchDB.prototype.get = function (sid, fn) {
        sid = _uri_encode(sid);
        var now = +new Date;
        this.db.get(sid, function (err, doc) {
            if (err) {
                if (err.error == "not_found") err.code = "ENOENT";
                return fn && fn(err);
            } else if (doc.expires && now >= doc.expires) {
                return fn && fn(null, null);
            } else {
                return fn && fn(null, doc.sess);
            }
        }.bind(this));
    };

    // Cache last session update for comparison below.
    var locks = {};

    ConnectCouchDB.prototype.set = function (sid, sess, fn) {
        sid = _uri_encode(sid);
        fn = fn || function () {};
        this.db.get(sid, function (err, doc) {
            var expires = typeof sess.cookie.maxAge === 'number'
                ? (+new Date()) + sess.cookie.maxAge
                : (+new Date()) + (24 * 60 * 60 * 1000);
            if (err) {
                doc = {
                    _id: sid,
                    expires: expires,
                    type: 'connect-session',
                    sess: sess
                };
                this.db.put(doc, fn);
            } else {
                // Temporarily remove properties for session comparison
                var _lastAccess = sess.lastAccess;
                var _originalMaxAge = sess.cookie.originalMaxAge;
                if (sess.cookie._expires) {
                    var _expires = sess.cookie._expires;
                    sess.cookie._expires = null;
                }
                if (doc.sess.cookie.expires) {
                    var _expiresDoc = doc.sess.cookie.expires;
                    doc.sess.cookie.expires = null;
                }
                sess.lastAccess = null;
                sess.cookie.originalMaxAge = null;
                doc.sess.lastAccess = null;
                doc.sess.cookie.originalMaxAge = null;
                // Compare new session to current session, save if different.
                if (JSON.stringify(doc.sess) !== JSON.stringify(sess)) {
                    sess.lastAccess = _lastAccess;
                    if (_expires) sess.cookie._expires = _expires;
                    sess.cookie.originalMaxAge = _originalMaxAge;
                    doc.expires = expires;
                    doc.sess = sess;
                    this.db.put(doc, fn);
                } else {
                    // Otherwise, only save if > 1s has elaspsed since last save.
                    if (!locks[sid] || expires > locks[sid] + 60000) {
                        locks[sid] = expires;
                        sess.lastAccess = _lastAccess;
                        if (_expires) sess.cookie._expires = _expires;
                        sess.cookie.originalMaxAge = _originalMaxAge;
                        doc.expires = expires;
                        doc.sess = sess;
                        this.db.put(doc, fn);
                    } else {
                        return fn();
                    }
                }
            }
        }.bind(this));
    };

    ConnectCouchDB.prototype.destroy = function (sid, fn) {
        sid = _uri_encode(sid);
        this.db.get(sid, function (err, doc) {
            if (err) return fn && fn(err);
            this.db.del(doc, fn);
        }.bind(this));
    };

    function destroy(docs, fn) {
        var self = this;
        function destroyDocAt(index) {
            if (index === docs.length) {
                return fn && fn();
            } else {
                self.destroy(docs[index].value._id, function() {
                    destroyDocAt(index+1);
                });
            }
        }
        destroyDocAt(0);
    }

    ConnectCouchDB.prototype.clear = function (fn) {
        this.db.view('_design/connect-sessions/_view/expires', {}, function (err, docs) {
            if (err) return fn && fn(err);
            destroy.call(this, docs.rows, fn);
        }.bind(this));
    };

    ConnectCouchDB.prototype.reap = function (fn) {
        var now = +new Date;
        var options = { endkey: '[' + now + ',{}]' };
        this.db.view('_design/connect-sessions/_view/expires', options, function (err, docs) {
            if (err) return fn && fn(err);
            destroy.call(this, docs.rows, fn);
        }.bind(this));
    };

    ConnectCouchDB.prototype.length = function (fn) {
        var now = +new Date;
        var options = { startkey: '[' + now + ',{}]' };
        this.db.view('_design/connect-sessions/_view/expires', options, function (err, docs) {
            if (err) {
                return fn && fn(err);
            } else {
                return fn && fn(err, docs.total_rows - docs.offset);
            }
        });
    };

    ConnectCouchDB.prototype.compact = function () {
        var obj = {endpoint: '_compact', doc: {}};
        this.db.post(obj, function(err, res) {});
    };

    ConnectCouchDB.prototype.clearInterval = function () {
        if (this._reap) clearInterval(this._reap);
        if (this._compact) clearInterval(this._compact);
    };

    return ConnectCouchDB;
}
<|endoftext|>


<commit_before>var __hasProp = {}.hasOwnProperty,
	__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
	FS = require('fs-plus'),
	Path = require('path'),
	SSH2 = require('ssh2'),
	Connector = require('../connector');

module.exports = (function () {
	__extends(ConnectorSFTP, Connector);

	function ConnectorSFTP () {
		ConnectorSFTP.__super__.constructor.apply(this, arguments);
		this.ssh2 = null;
		this.sftp = null;
		this.status = 'disconnected';
	}

	ConnectorSFTP.prototype.isConnected = function () {
		var self = this;

		return self.status != 'disconnected';
	};

	ConnectorSFTP.prototype.connect = function (info, completed) {
		var self = this;
		self.info = info;

		var debug = self.info.debug;
		self.info.debug = true;

		self.status = 'connecting';

		self.ssh2 = new SSH2();
		self.ssh2.on('banner', function (msg, lang) {
			self.emit('greeting', msg);
		});
		self.ssh2.on('ready', function () {
			self.ssh2.sftp(function (err, sftp) {
				if (err) {
					self.disconnect();
					return;
				}

				self.status = 'connected';

				self.sftp = sftp;
				self.sftp.on('end', function () {
					self.disconnect();
					self.emit('ended');
				});

				self.emit('connected');

				if (typeof completed === 'function')
					completed.apply(self, []);
			});

		});
		self.ssh2.on('end', function () {
			self.disconnect();
			self.emit('ended');
		});
		self.ssh2.on('close', function () {
			self.disconnect();
			self.emit('closed');
		});
		self.ssh2.on('error', function (err) {
			self.emit('error', err);
		});
		self.ssh2.on('debug', function (str) {
			if (typeof debug == 'function')
				debug.apply(null, [str]);
		});
		self.ssh2.connect(self.info);

		return self;
	}

	ConnectorSFTP.prototype.disconnect = function (completed) {
		var self = this;

		self.status = 'disconnected';

		if (self.sftp) {
			self.sftp.end();
			self.sftp = null;
		}

		if (self.ssh2) {
			self.ssh2.end();
			self.ssh2 = null;
		}

		if (typeof completed === 'function')
			completed.apply(null, []);

		return self;
	}

	ConnectorSFTP.prototype.abort = function (completed) {

		// TODO find a way to abort current operation

		if (typeof completed === 'function')
			completed.apply(null, []);

		return this;
	}

	ConnectorSFTP.prototype.list = function (path, recursive, completed) {
		var self = this;

		if (self.isConnected()) {
			if (recursive) {
				var digg = 0,
					list = [];

				var e = function () {
					if (typeof completed === 'function')
						completed.apply(null, [null, list]);
				}
				var l = function (p) {
					++digg;
					self.sftp.readdir(p, function (err, lis) {
						if (err)
							return e();

						lis.forEach(function (item) {
							item.filename = Path.join(p, item.filename).replace(/\\/g, '/');
							item.date = new Date();
							item.date.setTime(item.attrs.mtime * 1000);
							list.push({
								name: item.filename,
								type: item.attrs.isFile() ? 'f' : 'd',
								size: item.attrs.size,
								date: item.date
							});
							if (!item.attrs.isFile()) {
								l(item.filename);
							}
						});

						if (--digg == 0)
							e();
					});
				}
				l(path);
			}
			else {
				self.sftp.readdir(path, function (err, lis) {
					var list = [];

					if (!err) {
						lis.forEach(function (item) {
							item.filename = Path.join(path, item.filename).replace(/\\/g, '/');
							item.date = new Date();
							item.date.setTime(item.attrs.mtime * 1000);
							list.push({
								name: item.filename,
								type: item.attrs.isFile() ? 'f' : 'd',
								size: item.attrs.size,
								date: item.date
							});
						});
					}

					if (typeof completed === 'function')
						completed.apply(null, [err, list]);
				});
			}
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return this;
	}

	ConnectorSFTP.prototype.get = function (path, recursive, completed, progress) {
		var self = this,
			local = self.client.toLocal(path);

		if (self.isConnected()) {
			self.sftp.stat(path, function (err, stats) {
				if (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err]);
					return;
				}

				if (stats.isFile()) {
					// File
					FS.makeTreeSync(Path.dirname(local));
					self.sftp.fastGet(path, local, {
						step: function (read, chunk, size) {
							if (typeof progress === 'function')
								progress.apply(null, [read / size]);
						}
					}, function (err) {
						if (typeof completed === 'function')
							completed.apply(null, [err]);
						return;
					});
				}
				else {
					// Directory

					self.list(path, recursive, function (err, list) {
						list.unshift({name: path, type: 'd'});
						list.forEach(function (item) { item.depth = item.name.replace(/^\/+/, '').replace(/\/+$/).split('/').length; });
						list.sort(function (a, b) {
							if (a.depth == b.depth)
								return 0;
							return a.depth > b.depth ? 1 : -1;
						});

						var error = null,
							total = list.length,
							i = -1;
						var e = function () {
							if (typeof completed === 'function')
								completed.apply(null, [error, list]);
						}
						var n = function () {
							++i;
							if (typeof progress === 'function')
								progress.apply(null, [i / total]);

							var item = list.shift();
							if (item == null)
								return e();
							var local = self.client.toLocal(item.name);
							if (item.type == 'd' || item.type == 'l') {
								//mkdirp(local, function (err) {
								FS.makeTree(local, function (err) {
									if (err)
										error = err;
									return n();
								});
							} else {
								self.sftp.fastGet(item.name, local, {
									step: function (read, chunk, size) {
										if (typeof progress === 'function')
											progress.apply(null, [(i / total) + (read / size / total)]);
									}
								}, function (err) {
									if (err)
										error = err;
									return n();
								});
							}
						}
						n();
					});
				}
			});
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.put = function (path, completed, progress) {
		var self = this,
			remote = self.client.toRemote(path);

		if (self.isConnected()) {
			// File
			if (FS.isFileSync(path)) {
				var e = function (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err ? err : null, [{name: path, type: 'f'}]]);
				}
				self.sftp.fastPut(path, remote, {
					step: function (written, chunk, size) {
						if (typeof progress === 'function')
							progress.apply(null, [written / size]);
					}
				}, function (err) {
					if (err) {
						if (err.type == 'NO_SUCH_FILE') {
							self.mkdir(Path.dirname(remote).replace(/\\/g, '/'), true, function (err) {
								if (err)
									return e(err);

								self.sftp.fastPut(path, remote, {
									step: function (written, chunk, size) {
										if (typeof progress === 'function')
											progress.apply(null, [written / size]);
									}
								}, function (err) {
									return e(err)
								});
							});
							return;
						}
						return e(err);
					}

					return e();
				})

			}

			// Folder
			else {
				self.client._traverseTree(path, function (list) {
					self.mkdir(remote, true, function (err) {
						var error,
							i = -1,
							total = list.length;
						var e = function () {
							if (typeof completed === 'function')
								completed.apply(null, [error, list]);
						}
						var n = function () {
							if (++i >= list.length)
								return e();
							var item = list[i],
								remote = self.client.toRemote(item.name);
							if (item.type == 'd' || item.type == 'l') {
								self.sftp.mkdir(remote, {}, function (err) {
									if (err)
										error = err;
									return n();
								});
							} else {
								self.sftp.fastPut(item.name, remote, {
									step: function (written, chunk, size) {
										if (typeof progress === 'function')
											progress.apply(null, [(i / total) + (written / size / total)]);
									}
								}, function (err) {
									if (err)
										error = err;
									return n();
								});
							}
						}
						return n();
					});
				});
			}
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.mkdir = function (path, recursive, completed) {
		var self = this,
			remotes = path.replace(/^\/+/, '').replace(/\/+$/, '').split('/'),
			dirs = ['/' + remotes.slice(0, remotes.length).join('/')];

		if (self.isConnected()) {
			if (recursive) {
				for (var a = remotes.length - 1; a > 0; --a)
					dirs.unshift('/' + remotes.slice(0, a).join('/'));
			}

			var n = function () {
				var dir = dirs.shift(),
					last = dirs.length == 0;

				self.sftp.mkdir(dir, {}, function (err) {
					if (last) {
						if (typeof completed === 'function')
							completed.apply(null, [err ? err : null]);
					} else {
						return n();
					}
				});
			}
			n();
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.mkfile = function (path, completed) {
		var self = this,
			local = self.client.toLocal(path),
			empty = new Buffer('', 'utf8');

		if (self.isConnected()) {
			self.sftp.open(path, 'w', {}, function (err, handle) {
				if (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err]);
					return;
				}
				self.sftp.write(handle, empty, 0, 0, 0, function (err) {
					if (err) {
						if (typeof completed === 'function')
							completed.apply(null, [err]);
						return;
					}
					//mkdirp(Path.dirname(local), function (err1) {
					FS.makeTree(Path.dirname(local), function (err1) {
						FS.writeFile(local, empty, function (err2) {
							if (typeof completed === 'function')
								completed.apply(null, [err1 || err2]);
						});
					});
				})
			});
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.rename = function (source, dest, completed) {
		var self = this;

		if (self.isConnected()) {
			self.sftp.rename(source, dest, function (err) {
				if (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err]);
				} else {
					FS.rename(self.client.toLocal(source), self.client.toLocal(dest), function (err) {
						if (typeof completed === 'function')
							completed.apply(null, [err]);
					});
				}
			});
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.delete = function (path, completed) {
		var self = this;

		if (self.isConnected()) {
			self.sftp.stat(path, function (err, stats) {
				if (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err]);
					return;
				}

				if (stats.isFile()) {
					// File
					self.sftp.unlink(path, function (err) {
						if (typeof completed === 'function')
							completed.apply(null, [err, [{name: path, type: 'f'}]]);
					});
				}
				else {
					// Directory
					self.list(path, true, function (err, list) {
						list.forEach(function (item) { item.depth = item.name.replace(/^\/+/, '').replace(/\/+$/).split('/').length; });
						list.sort(function (a, b) {
							if (a.depth == b.depth)
								return 0;
							return a.depth > b.depth ? -1 : 1;
						});

						var done = 0;

						var e = function () {
							self.sftp.rmdir(path, function (err) {
								if (typeof completed === 'function')
									completed.apply(null, [err, list]);
							});
						}
						list.forEach(function (item) {
							++done;
							var fn = item.type == 'd' || item.type == 'l' ? 'rmdir' : 'unlink';
							self.sftp[fn](item.name, function (err) {
								if (--done == 0)
									return e();
							});
						});
						if (list.length == 0)
							e();
					});
				}
			});
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	return ConnectorSFTP;
})();
<commit_msg>attempt to fix #42<commit_after>var __hasProp = {}.hasOwnProperty,
	__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
	FS = require('fs-plus'),
	Path = require('path'),
	SSH2 = require('ssh2'),
	Connector = require('../connector');

module.exports = (function () {
	__extends(ConnectorSFTP, Connector);

	function ConnectorSFTP () {
		ConnectorSFTP.__super__.constructor.apply(this, arguments);
		this.ssh2 = null;
		this.sftp = null;
		this.status = 'disconnected';
	}

	ConnectorSFTP.prototype.isConnected = function () {
		var self = this;

		return self.status != 'disconnected' && self.sftp;
	};

	ConnectorSFTP.prototype.connect = function (info, completed) {
		var self = this;
		self.info = info;

		var debug = self.info.debug;
		self.info.debug = true;

		self.status = 'connecting';

		self.ssh2 = new SSH2();
		self.ssh2.on('banner', function (msg, lang) {
			self.emit('greeting', msg);
		});
		self.ssh2.on('ready', function () {
			self.ssh2.sftp(function (err, sftp) {
				if (err) {
					self.disconnect();
					return;
				}

				self.status = 'connected';

				self.sftp = sftp;
				self.sftp.on('end', function () {
					self.disconnect();
					self.emit('ended');
				});

				self.emit('connected');

				if (typeof completed === 'function')
					completed.apply(self, []);
			});

		});
		self.ssh2.on('end', function () {
			self.disconnect();
			self.emit('ended');
		});
		self.ssh2.on('close', function () {
			self.disconnect();
			self.emit('closed');
		});
		self.ssh2.on('error', function (err) {
			self.emit('error', err);
		});
		self.ssh2.on('debug', function (str) {
			if (typeof debug == 'function')
				debug.apply(null, [str]);
		});
		self.ssh2.connect(self.info);

		return self;
	}

	ConnectorSFTP.prototype.disconnect = function (completed) {
		var self = this;

		self.status = 'disconnected';

		if (self.sftp) {
			self.sftp.end();
			self.sftp = null;
		}

		if (self.ssh2) {
			self.ssh2.end();
			self.ssh2 = null;
		}

		if (typeof completed === 'function')
			completed.apply(null, []);

		return self;
	}

	ConnectorSFTP.prototype.abort = function (completed) {

		// TODO find a way to abort current operation

		if (typeof completed === 'function')
			completed.apply(null, []);

		return this;
	}

	ConnectorSFTP.prototype.list = function (path, recursive, completed) {
		var self = this;

		if (self.isConnected()) {
			if (recursive) {
				var digg = 0,
					list = [];

				var e = function () {
					if (typeof completed === 'function')
						completed.apply(null, [null, list]);
				}
				var l = function (p) {
					++digg;
					self.sftp.readdir(p, function (err, lis) {
						if (err)
							return e();

						lis.forEach(function (item) {
							item.filename = Path.join(p, item.filename).replace(/\\/g, '/');
							item.date = new Date();
							item.date.setTime(item.attrs.mtime * 1000);
							list.push({
								name: item.filename,
								type: item.attrs.isFile() ? 'f' : 'd',
								size: item.attrs.size,
								date: item.date
							});
							if (!item.attrs.isFile()) {
								l(item.filename);
							}
						});

						if (--digg == 0)
							e();
					});
				}
				l(path);
			}
			else {
				self.sftp.readdir(path, function (err, lis) {
					var list = [];

					if (!err) {
						lis.forEach(function (item) {
							item.filename = Path.join(path, item.filename).replace(/\\/g, '/');
							item.date = new Date();
							item.date.setTime(item.attrs.mtime * 1000);
							list.push({
								name: item.filename,
								type: item.attrs.isFile() ? 'f' : 'd',
								size: item.attrs.size,
								date: item.date
							});
						});
					}

					if (typeof completed === 'function')
						completed.apply(null, [err, list]);
				});
			}
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return this;
	}

	ConnectorSFTP.prototype.get = function (path, recursive, completed, progress) {
		var self = this,
			local = self.client.toLocal(path);

		if (self.isConnected()) {
			self.sftp.stat(path, function (err, stats) {
				if (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err]);
					return;
				}

				if (stats.isFile()) {
					// File
					FS.makeTreeSync(Path.dirname(local));
					self.sftp.fastGet(path, local, {
						step: function (read, chunk, size) {
							if (typeof progress === 'function')
								progress.apply(null, [read / size]);
						}
					}, function (err) {
						if (typeof completed === 'function')
							completed.apply(null, [err]);
						return;
					});
				}
				else {
					// Directory

					self.list(path, recursive, function (err, list) {
						list.unshift({name: path, type: 'd'});
						list.forEach(function (item) { item.depth = item.name.replace(/^\/+/, '').replace(/\/+$/).split('/').length; });
						list.sort(function (a, b) {
							if (a.depth == b.depth)
								return 0;
							return a.depth > b.depth ? 1 : -1;
						});

						var error = null,
							total = list.length,
							i = -1;
						var e = function () {
							if (typeof completed === 'function')
								completed.apply(null, [error, list]);
						}
						var n = function () {
							++i;
							if (typeof progress === 'function')
								progress.apply(null, [i / total]);

							var item = list.shift();
							if (item == null)
								return e();
							var local = self.client.toLocal(item.name);
							if (item.type == 'd' || item.type == 'l') {
								//mkdirp(local, function (err) {
								FS.makeTree(local, function (err) {
									if (err)
										error = err;
									return n();
								});
							} else {
								self.sftp.fastGet(item.name, local, {
									step: function (read, chunk, size) {
										if (typeof progress === 'function')
											progress.apply(null, [(i / total) + (read / size / total)]);
									}
								}, function (err) {
									if (err)
										error = err;
									return n();
								});
							}
						}
						n();
					});
				}
			});
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.put = function (path, completed, progress) {
		var self = this,
			remote = self.client.toRemote(path);

		if (self.isConnected()) {
			// File
			if (FS.isFileSync(path)) {
				var e = function (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err ? err : null, [{name: path, type: 'f'}]]);
				}
				self.sftp.fastPut(path, remote, {
					step: function (written, chunk, size) {
						if (typeof progress === 'function')
							progress.apply(null, [written / size]);
					}
				}, function (err) {
					if (err) {
						if (err.type == 'NO_SUCH_FILE') {
							self.mkdir(Path.dirname(remote).replace(/\\/g, '/'), true, function (err) {
								if (err)
									return e(err);

								self.sftp.fastPut(path, remote, {
									step: function (written, chunk, size) {
										if (typeof progress === 'function')
											progress.apply(null, [written / size]);
									}
								}, function (err) {
									return e(err)
								});
							});
							return;
						}
						return e(err);
					}

					return e();
				})

			}

			// Folder
			else {
				self.client._traverseTree(path, function (list) {
					self.mkdir(remote, true, function (err) {
						var error,
							i = -1,
							total = list.length;
						var e = function () {
							if (typeof completed === 'function')
								completed.apply(null, [error, list]);
						}
						var n = function () {
							if (++i >= list.length)
								return e();
							var item = list[i],
								remote = self.client.toRemote(item.name);
							if (item.type == 'd' || item.type == 'l') {
								self.sftp.mkdir(remote, {}, function (err) {
									if (err)
										error = err;
									return n();
								});
							} else {
								self.sftp.fastPut(item.name, remote, {
									step: function (written, chunk, size) {
										if (typeof progress === 'function')
											progress.apply(null, [(i / total) + (written / size / total)]);
									}
								}, function (err) {
									if (err)
										error = err;
									return n();
								});
							}
						}
						return n();
					});
				});
			}
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.mkdir = function (path, recursive, completed) {
		var self = this,
			remotes = path.replace(/^\/+/, '').replace(/\/+$/, '').split('/'),
			dirs = ['/' + remotes.slice(0, remotes.length).join('/')];

		if (self.isConnected()) {
			if (recursive) {
				for (var a = remotes.length - 1; a > 0; --a)
					dirs.unshift('/' + remotes.slice(0, a).join('/'));
			}

			var n = function () {
				var dir = dirs.shift(),
					last = dirs.length == 0;

				self.sftp.mkdir(dir, {}, function (err) {
					if (last) {
						if (typeof completed === 'function')
							completed.apply(null, [err ? err : null]);
					} else {
						return n();
					}
				});
			}
			n();
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.mkfile = function (path, completed) {
		var self = this,
			local = self.client.toLocal(path),
			empty = new Buffer('', 'utf8');

		if (self.isConnected()) {
			self.sftp.open(path, 'w', {}, function (err, handle) {
				if (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err]);
					return;
				}
				self.sftp.write(handle, empty, 0, 0, 0, function (err) {
					if (err) {
						if (typeof completed === 'function')
							completed.apply(null, [err]);
						return;
					}
					//mkdirp(Path.dirname(local), function (err1) {
					FS.makeTree(Path.dirname(local), function (err1) {
						FS.writeFile(local, empty, function (err2) {
							if (typeof completed === 'function')
								completed.apply(null, [err1 || err2]);
						});
					});
				})
			});
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.rename = function (source, dest, completed) {
		var self = this;

		if (self.isConnected()) {
			self.sftp.rename(source, dest, function (err) {
				if (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err]);
				} else {
					FS.rename(self.client.toLocal(source), self.client.toLocal(dest), function (err) {
						if (typeof completed === 'function')
							completed.apply(null, [err]);
					});
				}
			});
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	ConnectorSFTP.prototype.delete = function (path, completed) {
		var self = this;

		if (self.isConnected()) {
			self.sftp.stat(path, function (err, stats) {
				if (err) {
					if (typeof completed === 'function')
						completed.apply(null, [err]);
					return;
				}

				if (stats.isFile()) {
					// File
					self.sftp.unlink(path, function (err) {
						if (typeof completed === 'function')
							completed.apply(null, [err, [{name: path, type: 'f'}]]);
					});
				}
				else {
					// Directory
					self.list(path, true, function (err, list) {
						list.forEach(function (item) { item.depth = item.name.replace(/^\/+/, '').replace(/\/+$/).split('/').length; });
						list.sort(function (a, b) {
							if (a.depth == b.depth)
								return 0;
							return a.depth > b.depth ? -1 : 1;
						});

						var done = 0;

						var e = function () {
							self.sftp.rmdir(path, function (err) {
								if (typeof completed === 'function')
									completed.apply(null, [err, list]);
							});
						}
						list.forEach(function (item) {
							++done;
							var fn = item.type == 'd' || item.type == 'l' ? 'rmdir' : 'unlink';
							self.sftp[fn](item.name, function (err) {
								if (--done == 0)
									return e();
							});
						});
						if (list.length == 0)
							e();
					});
				}
			});
		}
		else {
			if (typeof completed === 'function')
				completed.apply(null, ['Not connected']);
		}

		return self;
	}

	return ConnectorSFTP;
})();
<|endoftext|>


<commit_before>var types = require('../domains/types')
var walk = require('acorn/util/walk');
var status = require('../domains/status');

// arguments are " oldStatus (, name, val)* "
function changedStatus(oldStatus) {
    var newStatus = new status.Status;
    for (var i = 1; i < arguments.length; i = i + 2)
        newStatus[arguments[i]] = arguments[i+1];

    for (var p in oldStatus) {
        if (newStatus[p] === undefined)
            newStatus[p] = oldStatus[p];
    }
    return newStatus;
}

function immedProp(node) {
    var prop = node.property;
    if (!node.computed) return prop.name;
    if (prop.type === 'Literal') return prop.value;
    return '<i>';
}
function unopResultType(op) {
    switch (op) {
        case '+': case '-': case '~':
            return types.PrimNumber;
        case '!':
            return types.PrimBoolean;
        case 'typeof':
            return types.PrimString;
        case 'void': case 'delete':
            return types.AValNull;
    }
}

function binopIsBoolean(op) {
    switch (op) {
        case '==': case '!=': case '===': case '!==':
        case '<': case '>': case '>=': case '<=':
        case 'in': case 'instanceof':
            return true;
    }
    return false;
}

// To prevent recursion,
// we remember the status used in addConstraints
var visitedStatus = [];
var constraints = [];
function clearConstraints() {
    visitedStatus.length = 0;
    constraints.length = 0;
}

function addConstraints(ast, initStatus, rtCX) {

    // Check whether we have processed 'initStatus' before
    for (var i=0; i < visitedStatus.length; i++) {
        if (initStatus.equals(visitedStatus[i])) {
             // If so, do nothing
             // signifying we didn't add constraints
             return false;
         }
    }
    // If the initStatus is new, push it.
    // We do not record ast since ast node depends on the status
    visitedStatus.push(initStatus);

    // constraint generating walker for expressions
    var constraintGenerator = walk.make({

        Identifier: function (node, curStatus, c) {
            return curStatus.sc.getAValOf(node.name);
        },

        ThisExpression: function (node, curStatus, c) {
            return curStatus.self;
        },

        Literal: function (node, curStatus, c) {
            var res = new types.AVal;
            if (node.regex) {
                // not implemented yet
                // throw new Error('regex literal is not implemented yet');
                return res;
            }
            switch (typeof node.value) {
            case 'number':
                constraints.push({TYPE: types.PrimNumber,
                                  INCL_SET: res});
                break;
            case 'string':
                constraints.push({TYPE: types.PrimString,
                                  INCL_SET: res});
                break;
            case 'boolean':
                constraints.push({TYPE: types.PrimBoolean,
                                  INCL_SET: res});
                break;
            case 'object':
                // I guess: Literal && object ==> node.value == null
                // null is ignored, so nothing to add
                break;
            case 'function':
                throw new Error('I guess function is impossible here.');
            }
            return res;
        },

        AssignmentExpression: function (node, curStatus, c) {
            if (node.left.type === 'MemberExpression') {
                // TODO
                // LHS is not a simple variable.
            } else {
                // LHS is a simple variable.
                var varName = node.left.name;
                var lhsAVal = curStatus.sc.getAValOf(varName);
                var rhsAVal = c(node.right, curStatus, undefined);
                constraints.push({FROM: rhsAVal,
                                  TO: lhsAVal});
                // corresponding AVal is the RHS
                return rhsAVal;
            }
        },

        VariableDeclaration: function (node, curStatus, c) {
            for (var i = 0; i < node.declarations.length; i++) {
                var decl = node.declarations[i];
                if (decl.init) {
                    var lhsAVal = curStatus.sc.getAValOf(decl.id.name);
                    var rhsAVal = c(decl.init, curStatus, undefined);
                    constraints.push({FROM: rhsAVal,
                                      TO: lhsAVal});
                }
            }
        },

        LogicalExpression: function (node, curStatus, c) {
            var res = new types.AVal;
            var left = c(node.left, curStatus, undefined);
            var right = c(node.right, curStatus, undefined);
            constraints.push({FROM: left, TO: res},
                             {FROM: right, TO: res});
            return res;
        },

        ConditionalExpression: function (node, curStatus, c) {
            var res = new types.AVal;
            c(node.test, curStatus, undefined);
            var cons = c(node.consequent, curStatus, undefined);
            var alt = c(node.alternate, curStatus, undefined);
            constraints.push({FROM: cons, TO: res},
                             {FROM: alt, TO: res});
            return res;
        },

        NewExpression: function (node, curStatus, c) {
            var ret = new types.AVal;
            var callee = c(node.callee, curStatus, undefined);
            var args = [];
            for (var i = 0; i < node.arguments.length; i++) {
                args.push(c(node.arguments[i], curStatus, undefined));
            }
            var newDelta = curStatus.delta.appendOne(node['@label']);
            constraints.push({CALLEE: callee,
                              ARGS: args,
                              RET: ret,
                              EXC: curStatus.exc,
                              DELTA: newDelta});
            return ret;
        },

        ArrayExpression: function (node, curStatus, c) {
            var ret = new types.AVal;
            var arrType = new types.ArrType(rtCX.protos.Array);
            constraints.push({TYPE: arrType, INCL_SET: ret});

            for (var i = 0; i < node.elements.length; i++) {
                var eltAVal = c(node.elements[i], undefined);

                constraints.push({OBJ: ret, PROP: i, AVAL: eltAVal});
                constraints.push({OBJ: ret, PROP: '<i>', AVAL: eltAVal});
            }
            return ret;
        },

        ObjectExpression: function (node, curStatus, c) {
            var ret = new types.AVal;
            var objType = new types.ObjType(rtCX.protos.Object);
            constraints.push({TYPE: objType, INCL_SET: ret});

            for (var i = 0; i < node.properties.length; i++) {
                var propPair = node.properties[i];
                var propKey = propPair.key;
                var name;
                var propExpr = propPair.value;

                var fldAVal = c(propExpr, curStatus, undefined);

                if (propKey.type === 'Identifier') {
                    name = propKey.name;
                } else if (typeof propKey.value === 'string'
                        || typeof propKey.value === 'number') {
                    name = propKey.value;
                }
                constraints.push({OBJ: ret, PROP: name, AVAL: fldAVal});
            }
            return ret;
        },

        FunctionExpression: function (node, curStatus, c) {
            if (!node.fnInstances) {
                node.fnInstances = [];
            }
            var fnInstance = null;
            node.fnInstances.forEach(function (fnType) {
                if (fnType.sc === curStatus.sc) {
                    fnInstance = fnType;
                }
            });
            if (!fnInstance) {
                fnInstance
                    = new types.FnType(rtCX.protos.Function,
                                       '[anonymous function]',
                                       node.body['@block'].getParamVarNames(),
                                       curStatus.sc);
                node.fnInstances.push(fnInstance);
                var prototypeObject =
                    new types.ObjType(rtCX.protos.Object,
                                      '?.prototype');
                var prototypeProp = fnInstance.getProp('prototype');
                constraints.push({TYPE: prototypeObject,
                                  INCL_SET: prototypeProp});
            }
            var ret = new types.AVal;
            constraints.push({TYPE: fnInstance,
                              INCL_SET: ret});
            return ret;
        },

        FunctionDeclaration: function (node, curStatus, c) {
            // Drop initial catch scopes
            var sc0 = curStatus.sc.removeInitialCatchBlocks();
            if (!node.fnInstances) {
                node.fnInstances = [];
            }
            var fnInstance = null;
            node.fnInstances.forEach(function (fnType) {
                if (fnType.sc === sc0) {
                    fnInstance = fnType;
                }
            });
            if (!fnInstance) {
                fnInstance
                    = new types.FnType(rtCX.protos.Function,
                                       node.id.name,
                                       node.body['@block'].getParamVarNames(),
                                       sc0);
                node.fnInstances.push(fnInstance);
                // for each fnInstance, assign one prototype object
                var prototypeObject =
                    new types.ObjType(rtCX.protos.Object,
                                      node.id.name + '.prototype');
                var prototypeProp = fnInstance.getProp('prototype');
                constraints.push({TYPE: prototypeObject,
                                  INCL_SET: prototypeProp});
            }
            var lhsAVal = sc0.getAValOf(node.id.name);

            constraints.push({TYPE: fnInstance,
                              INCL_SET: lhsAVal});
            // nothing to return
            return types.AValNull;
        },

        SequenceExpression: function (node, curStatus, c) {
            var lastIndex = node.expressions.length - 1;
            for (var i = 0; i < lastIndex; i++) {
                c(node.expressions[i], curStatus, undefined);
            }
            return c(node.expressions[lastIndex], curStatus, undefined);
        },

        UnaryExpression: function (node, curStatus, c) {
            c(node.argument, curStatus, undefined);
            var res = new types.AVal;
            constraints.push({TYPE: unopResultType(node.operator),
                              INCL_SET: res});
            return res;
        },

        UpdateExpression: function (node, curStatus, c) {
            c(node.argument, curStatus, undefined);
            var res = new types.AVal;
            constraints.push({TYPE: types.PrimNumber,
                              INCL_SET: res});
            // We ignore the effect of updating to number type
            return res;
        },

        BinaryExpression: function (node, curStatus, c) {
            var lOprd = c(node.left, curStatus, undefined);
            var rOprd = c(node.right, curStatus, undefined);
            var res = new types.AVal;

            if (node.operator == '+') {
                constraints.push({ADD_OPRD1: lOprd,
                                  ADD_OPRD2: rOprd,
                                  RESULT: res });
            } else {
                if (binopIsBoolean(node.operator)) {
                    constraints.push({TYPE: types.PrimBoolean,
                                      INCL_SET: res});
                } else {
                    constraints.push({TYPE: types.PrimNumber,
                                      INCL_SET: res});
                }
            }
            return res;
        },

        TryStatement: function (node, curStatus, c) {
            // construct scope chain for catch block
            var catchBlockSC =
                node.handler.body['@block']
                .getScopeInstance(curStatus.sc, curStatus.delta);
            // get the AVal for exception parameter
            var excAVal = catchBlockSC.getAValOf(node.handler.param.name);

            // for try block
            var tryStatus = changedStatus(curStatus, 'exc', excAVal);
            c(node.block, tryStatus, undefined);

            // for catch block
            var catchStatus = changedStatus(curStatus, 'sc', catchBlockSC);
            c(node.handler.body, catchStatus, undefined);

            // for finally block
            if (node.finalizer !== null)
                c(node.finalizer.body, curStatus, undefined);
        },

        ThrowStatement: function (node, curStatus, c) {
            var thr = c(node.argument, curStatus, undefined);
            constraints.push({FROM: thr,
                              TO: curStatus.exc});
        },

        CallExpression: function (node, curStatus, c) {
            var resAVal = new types.AVal;
            var argAVals = [];

            // get AVals for each arguments
            for (var i = 0; i < node.arguments.length; i++) {
                argAVals.push(
                    c(node.arguments[i], curStatus, undefined));
            }

            if (node.callee.type === 'MemberExpression') {

                // TODO: method call
            } else {
                // normal function call
                var calleeAVal = c(node.callee, curStatus, undefined);
                // append current call site to the context
                var newDelta = curStatus.delta.appendOne(node['@label']);
                // callee의 return을 call expression으로
                // callee의 exception을 호출 측의 exception에 전달해야
                constraints.push({
                    CALLEE: calleeAVal,
                    SELF: rtCX.globalObject,
                    PARAMS: argAVals,
                    RET: resAVal,
                    EXC: curStatus.exc,
                    DELTA: newDelta
                });
            }
            return resAVal;
        },

        MemberExpression: function (node, curStatus, c) {
            var ret = new types.AVal;
            var objAVal = c(node.object, curStatus, undefined);
            if (node.property.type !== 'Identifier') {
                // return from property is ignored
                c(node.property, curStatus, undefined);
            }
            var name = immedProp(node);

            constraints.push({OBJ: objAVal,
                              PROP: name,
                              READ_TO: ret});
            return ret;
        },

        ReturnStatement: function (node, curStatus, c) {
            if (!node.argument) return;
            var ret = c(node.argument, curStatus, undefined);
            constraints.push({FROM: ret,
                              TO: curStatus.ret});
        }
    });

    recursiveWithReturn(ast, initStatus, constraintGenerator);

    // We actually added constraints
    return true;
}

function recursiveWithReturn(node, state, visitor) {
    function c(node, st, override) {
        return visitor[override || node.type](node, st, c);
    }
    return c(node, state);
}

exports.constraints = constraints;
exports.addConstraints = addConstraints;
exports.clearConstraints = clearConstraints;<commit_msg>Add .prototype.constructor for function instances<commit_after>var types = require('../domains/types')
var walk = require('acorn/util/walk');
var status = require('../domains/status');

// arguments are " oldStatus (, name, val)* "
function changedStatus(oldStatus) {
    var newStatus = new status.Status;
    for (var i = 1; i < arguments.length; i = i + 2)
        newStatus[arguments[i]] = arguments[i+1];

    for (var p in oldStatus) {
        if (newStatus[p] === undefined)
            newStatus[p] = oldStatus[p];
    }
    return newStatus;
}

function immedProp(node) {
    var prop = node.property;
    if (!node.computed) return prop.name;
    if (prop.type === 'Literal') return prop.value;
    return '<i>';
}
function unopResultType(op) {
    switch (op) {
        case '+': case '-': case '~':
            return types.PrimNumber;
        case '!':
            return types.PrimBoolean;
        case 'typeof':
            return types.PrimString;
        case 'void': case 'delete':
            return types.AValNull;
    }
}

function binopIsBoolean(op) {
    switch (op) {
        case '==': case '!=': case '===': case '!==':
        case '<': case '>': case '>=': case '<=':
        case 'in': case 'instanceof':
            return true;
    }
    return false;
}

// To prevent recursion,
// we remember the status used in addConstraints
var visitedStatus = [];
var constraints = [];
function clearConstraints() {
    visitedStatus.length = 0;
    constraints.length = 0;
}

function addConstraints(ast, initStatus, rtCX) {

    // Check whether we have processed 'initStatus' before
    for (var i=0; i < visitedStatus.length; i++) {
        if (initStatus.equals(visitedStatus[i])) {
             // If so, do nothing
             // signifying we didn't add constraints
             return false;
         }
    }
    // If the initStatus is new, push it.
    // We do not record ast since ast node depends on the status
    visitedStatus.push(initStatus);

    // constraint generating walker for expressions
    var constraintGenerator = walk.make({

        Identifier: function (node, curStatus, c) {
            return curStatus.sc.getAValOf(node.name);
        },

        ThisExpression: function (node, curStatus, c) {
            return curStatus.self;
        },

        Literal: function (node, curStatus, c) {
            var res = new types.AVal;
            if (node.regex) {
                // not implemented yet
                // throw new Error('regex literal is not implemented yet');
                return res;
            }
            switch (typeof node.value) {
            case 'number':
                constraints.push({TYPE: types.PrimNumber,
                                  INCL_SET: res});
                break;
            case 'string':
                constraints.push({TYPE: types.PrimString,
                                  INCL_SET: res});
                break;
            case 'boolean':
                constraints.push({TYPE: types.PrimBoolean,
                                  INCL_SET: res});
                break;
            case 'object':
                // I guess: Literal && object ==> node.value == null
                // null is ignored, so nothing to add
                break;
            case 'function':
                throw new Error('I guess function is impossible here.');
            }
            return res;
        },

        AssignmentExpression: function (node, curStatus, c) {
            if (node.left.type === 'MemberExpression') {
                // TODO
                // LHS is not a simple variable.
            } else {
                // LHS is a simple variable.
                var varName = node.left.name;
                var lhsAVal = curStatus.sc.getAValOf(varName);
                var rhsAVal = c(node.right, curStatus, undefined);
                constraints.push({FROM: rhsAVal,
                                  TO: lhsAVal});
                // corresponding AVal is the RHS
                return rhsAVal;
            }
        },

        VariableDeclaration: function (node, curStatus, c) {
            for (var i = 0; i < node.declarations.length; i++) {
                var decl = node.declarations[i];
                if (decl.init) {
                    var lhsAVal = curStatus.sc.getAValOf(decl.id.name);
                    var rhsAVal = c(decl.init, curStatus, undefined);
                    constraints.push({FROM: rhsAVal,
                                      TO: lhsAVal});
                }
            }
        },

        LogicalExpression: function (node, curStatus, c) {
            var res = new types.AVal;
            var left = c(node.left, curStatus, undefined);
            var right = c(node.right, curStatus, undefined);
            constraints.push({FROM: left, TO: res},
                             {FROM: right, TO: res});
            return res;
        },

        ConditionalExpression: function (node, curStatus, c) {
            var res = new types.AVal;
            c(node.test, curStatus, undefined);
            var cons = c(node.consequent, curStatus, undefined);
            var alt = c(node.alternate, curStatus, undefined);
            constraints.push({FROM: cons, TO: res},
                             {FROM: alt, TO: res});
            return res;
        },

        NewExpression: function (node, curStatus, c) {
            var ret = new types.AVal;
            var callee = c(node.callee, curStatus, undefined);
            var args = [];
            for (var i = 0; i < node.arguments.length; i++) {
                args.push(c(node.arguments[i], curStatus, undefined));
            }
            var newDelta = curStatus.delta.appendOne(node['@label']);
            constraints.push({CALLEE: callee,
                              ARGS: args,
                              RET: ret,
                              EXC: curStatus.exc,
                              DELTA: newDelta});
            return ret;
        },

        ArrayExpression: function (node, curStatus, c) {
            var ret = new types.AVal;
            var arrType = new types.ArrType(rtCX.protos.Array);
            constraints.push({TYPE: arrType, INCL_SET: ret});

            for (var i = 0; i < node.elements.length; i++) {
                var eltAVal = c(node.elements[i], undefined);

                constraints.push({OBJ: ret, PROP: i, AVAL: eltAVal});
                constraints.push({OBJ: ret, PROP: '<i>', AVAL: eltAVal});
            }
            return ret;
        },

        ObjectExpression: function (node, curStatus, c) {
            var ret = new types.AVal;
            var objType = new types.ObjType(rtCX.protos.Object);
            constraints.push({TYPE: objType, INCL_SET: ret});

            for (var i = 0; i < node.properties.length; i++) {
                var propPair = node.properties[i];
                var propKey = propPair.key;
                var name;
                var propExpr = propPair.value;

                var fldAVal = c(propExpr, curStatus, undefined);

                if (propKey.type === 'Identifier') {
                    name = propKey.name;
                } else if (typeof propKey.value === 'string'
                        || typeof propKey.value === 'number') {
                    name = propKey.value;
                }
                constraints.push({OBJ: ret, PROP: name, AVAL: fldAVal});
            }
            return ret;
        },

        FunctionExpression: function (node, curStatus, c) {
            if (!node.fnInstances) {
                node.fnInstances = [];
            }
            var fnInstance = null;
            node.fnInstances.forEach(function (fnType) {
                if (fnType.sc === curStatus.sc) {
                    fnInstance = fnType;
                }
            });
            if (!fnInstance) {
                fnInstance
                    = new types.FnType(rtCX.protos.Function,
                                       '[anonymous function]',
                                       node.body['@block'].getParamVarNames(),
                                       curStatus.sc);
                node.fnInstances.push(fnInstance);
                var prototypeObject =
                    new types.ObjType(rtCX.protos.Object,
                                      '?.prototype');
                // For .prototype
                var prototypeProp = fnInstance.getProp('prototype');
                constraints.push({TYPE: prototypeObject,
                                  INCL_SET: prototypeProp});
                // For .prototype.constructor
                var constructorProp = prototypeObject.getProp('constructor');
                constraints.push({TYPE: fnInstance,
                                  INCL_SET: constructorProp});
            }
            var ret = new types.AVal;
            constraints.push({TYPE: fnInstance,
                              INCL_SET: ret});
            return ret;
        },

        FunctionDeclaration: function (node, curStatus, c) {
            // Drop initial catch scopes
            var sc0 = curStatus.sc.removeInitialCatchBlocks();
            if (!node.fnInstances) {
                node.fnInstances = [];
            }
            var fnInstance = null;
            node.fnInstances.forEach(function (fnType) {
                if (fnType.sc === sc0) {
                    fnInstance = fnType;
                }
            });
            if (!fnInstance) {
                fnInstance
                    = new types.FnType(rtCX.protos.Function,
                                       node.id.name,
                                       node.body['@block'].getParamVarNames(),
                                       sc0);
                node.fnInstances.push(fnInstance);
                // for each fnInstance, assign one prototype object
                var prototypeObject =
                    new types.ObjType(rtCX.protos.Object,
                                      node.id.name + '.prototype');
                // For .prototype
                var prototypeProp = fnInstance.getProp('prototype');
                constraints.push({TYPE: prototypeObject,
                                  INCL_SET: prototypeProp});
                // For .prototype.constructor
                var constructorProp = prototypeObject.getProp('constructor');
                constraints.push({TYPE: fnInstance,
                                  INCL_SET: constructorProp});
            }
            var lhsAVal = sc0.getAValOf(node.id.name);

            constraints.push({TYPE: fnInstance,
                              INCL_SET: lhsAVal});
            // nothing to return
            return types.AValNull;
        },

        SequenceExpression: function (node, curStatus, c) {
            var lastIndex = node.expressions.length - 1;
            for (var i = 0; i < lastIndex; i++) {
                c(node.expressions[i], curStatus, undefined);
            }
            return c(node.expressions[lastIndex], curStatus, undefined);
        },

        UnaryExpression: function (node, curStatus, c) {
            c(node.argument, curStatus, undefined);
            var res = new types.AVal;
            constraints.push({TYPE: unopResultType(node.operator),
                              INCL_SET: res});
            return res;
        },

        UpdateExpression: function (node, curStatus, c) {
            c(node.argument, curStatus, undefined);
            var res = new types.AVal;
            constraints.push({TYPE: types.PrimNumber,
                              INCL_SET: res});
            // We ignore the effect of updating to number type
            return res;
        },

        BinaryExpression: function (node, curStatus, c) {
            var lOprd = c(node.left, curStatus, undefined);
            var rOprd = c(node.right, curStatus, undefined);
            var res = new types.AVal;

            if (node.operator == '+') {
                constraints.push({ADD_OPRD1: lOprd,
                                  ADD_OPRD2: rOprd,
                                  RESULT: res });
            } else {
                if (binopIsBoolean(node.operator)) {
                    constraints.push({TYPE: types.PrimBoolean,
                                      INCL_SET: res});
                } else {
                    constraints.push({TYPE: types.PrimNumber,
                                      INCL_SET: res});
                }
            }
            return res;
        },

        TryStatement: function (node, curStatus, c) {
            // construct scope chain for catch block
            var catchBlockSC =
                node.handler.body['@block']
                .getScopeInstance(curStatus.sc, curStatus.delta);
            // get the AVal for exception parameter
            var excAVal = catchBlockSC.getAValOf(node.handler.param.name);

            // for try block
            var tryStatus = changedStatus(curStatus, 'exc', excAVal);
            c(node.block, tryStatus, undefined);

            // for catch block
            var catchStatus = changedStatus(curStatus, 'sc', catchBlockSC);
            c(node.handler.body, catchStatus, undefined);

            // for finally block
            if (node.finalizer !== null)
                c(node.finalizer.body, curStatus, undefined);
        },

        ThrowStatement: function (node, curStatus, c) {
            var thr = c(node.argument, curStatus, undefined);
            constraints.push({FROM: thr,
                              TO: curStatus.exc});
        },

        CallExpression: function (node, curStatus, c) {
            var resAVal = new types.AVal;
            var argAVals = [];

            // get AVals for each arguments
            for (var i = 0; i < node.arguments.length; i++) {
                argAVals.push(
                    c(node.arguments[i], curStatus, undefined));
            }

            if (node.callee.type === 'MemberExpression') {

                // TODO: method call
            } else {
                // normal function call
                var calleeAVal = c(node.callee, curStatus, undefined);
                // append current call site to the context
                var newDelta = curStatus.delta.appendOne(node['@label']);
                // callee의 return을 call expression으로
                // callee의 exception을 호출 측의 exception에 전달해야
                constraints.push({
                    CALLEE: calleeAVal,
                    SELF: rtCX.globalObject,
                    PARAMS: argAVals,
                    RET: resAVal,
                    EXC: curStatus.exc,
                    DELTA: newDelta
                });
            }
            return resAVal;
        },

        MemberExpression: function (node, curStatus, c) {
            var ret = new types.AVal;
            var objAVal = c(node.object, curStatus, undefined);
            if (node.property.type !== 'Identifier') {
                // return from property is ignored
                c(node.property, curStatus, undefined);
            }
            var name = immedProp(node);

            constraints.push({OBJ: objAVal,
                              PROP: name,
                              READ_TO: ret});
            return ret;
        },

        ReturnStatement: function (node, curStatus, c) {
            if (!node.argument) return;
            var ret = c(node.argument, curStatus, undefined);
            constraints.push({FROM: ret,
                              TO: curStatus.ret});
        }
    });

    recursiveWithReturn(ast, initStatus, constraintGenerator);

    // We actually added constraints
    return true;
}

function recursiveWithReturn(node, state, visitor) {
    function c(node, st, override) {
        return visitor[override || node.type](node, st, c);
    }
    return c(node, state);
}

exports.constraints = constraints;
exports.addConstraints = addConstraints;
exports.clearConstraints = clearConstraints;<|endoftext|>


<commit_before>var types = require('../domains/types')
var walk = require('acorn/util/walk');

// status:
// { self  : AVal,
//   ret   : AVal,
//   exc   : AVal,
//   delta : Context,
//   sc    : ScopeChain }

// arguments are " oldStatus (, name, val)* "
function changedStatus(oldStatus) {
    var newStatus = Object.create(null);
    for (var i = 1; i < arguments.length; i = i + 2)
        newStatus[arguments[i]] = arguments[i+1];

    for (var p in oldStatus) {
        if (newStatus[p] === undefined)
            newStatus[p] = oldStatus[p];
    }
    return newStatus;
}

var constraints;

function getConstraints(ast, gScope, cx) {
    // temporal set for generated constraints.
    constraints = [];

    // constraint generating walker for expressions
    var constraintGenerator = walk.make({

        Identifier: function (node, curStatus, c) {
            return curStatus.sc.getAValOf(node.name);
        },

        ThisExpression: function (node, curStatus, c) {
            return curStatus.self;
        },

        Literal: function (node, curStatus, c) {
            var res = new types.AVal;
            if (node.regex) {
                // not implemented yet
                // throw new Error('regex literal is not implemented yet');
                return res;
            }
            switch (typeof node.value) {
            case 'number':
                res.addType(types.PrimNumber);
                break;
            case 'string':
                res.addType(types.PrimString);
                break;
            case 'boolean':
                res.addType(types.PrimBoolean);
                break;
            case 'object':
                // I guess: Literal && object ==> node.value == null
                // null is ignored, so nothing to add
                break;
            case 'function':
                throw new Error('I guess function is impossible here.');
            }
            return res;
        },

        AssignmentExpression: function (node, curStatus, c) {
            if (node.left.type === 'MemberExpression') {
                // TODO
                // LHS is not a simple variable.
            } else {
                // LHS is a simple variable.
                var varName = node.left.name;
                var lhsAVal = curStatus.sc.getAValOf(varName);
                var rhsAVal = c(node.right, curStatus, undefined);
                constraints.push({FROM: rhsAVal,
                                  TO: lhsAVal});
                // corresponding AVal is the RHS
                return rhsAVal;
            }
        },
        VariableDeclaration: function (node, curStatus, c) {
            for (var i = 0; i < node.declarations.length; i++) {
                var decl = node.declarations[i];
                if (decl.init) {
                    var lhsAVal = curStatus.sc.getAValOf(decl.id.name);
                    var rhsAVal = c(decl.init, curStatus, undefined);
                    constraints.push({FROM: rhsAVal,
                                      TO: lhsAVal});
                }
            }
        },

        LogicalExpression: function (node, curStatus, c) {
            var res = new types.AVal;
            var left = c(node.left, curStatus, undefined);
            var right = c(node.right, curStatus, undefined);
            constraints.push({FROM: left, TO: res},
                             {FROM: right, TO: res});
            return res;
        },

        ConditionalExpression: function (node, curStatus, c) {
            var res = new types.AVal;
            c(node.test, curStatus, undefined);
            var cons = c(node.consequent, curStatus, undefined);
            var alt = c(node.alternate, curStatus, undefined);
            constraints.push({FROM: cons, TO: res},
                             {FROM: alt, TO: res});
            return res;
        },

        FunctionDeclaration: function (node, curStatus, c) {
            var sc0 = curStatus.sc.removeInitialCatchBlocks();
            if (!node.fnInstances) {
                node.fnInstances = [];
            }
            var fnInstance = null;
            node.fnInstances.forEach(function (fnType) {
                if (fnType.sc === sc0) {
                    fnInstance = fnType;
                }
            });
            if (!fnInstance) {
                fnInstance
                    = new types.FnType(node.id.name,
                                       node.body['@block'].getParamVarNames(),
                                       sc0);
                node.fnInstances.push(fnInstance);
            }
            var lhsAVal = sc0.getAValOf(node.id.name);
            constraints.push({TYPE: fnInstance,
                              INCL_SET: lhsAVal});
            // nothing to return
            return types.AValNull;
        },

        CallExpression: function (node, curStatus, c) {
            var resAVal = new types.AVal;
            var argAVals = [];

            // get AVals for each arguments
            for (var i = 0; i < node.arguments.length; i++) {
                argAVals.push(
                    c(node.arguments[i], curStatus, undefined));
            }

            if (node.callee.type === 'MemberExpression') {

                // TODO: method call
            } else {
                // normal function call
                var calleeAVal = c(node.callee, curStatus, undefined);
                // append current call site to the context
                var newDelta = curStatus.delta.appendOne(node['@label']);
                // callee의 return을 call expression으로
                // callee의 exception을 호출 측의 exception에 전달해야
                constraints.push({
                    CALLEE: calleeAVal,
                    SELF: cx.globalObject,
                    PARAMS: argAVals,
                    RET: resAVal,
                    EXC: curStatus.exc,
                    DELTA: newDelta
                });
            }
            return resAVal;
        }
    });

    recursiveWithReturn(ast, gScope, constraintGenerator);

    return constraints;
}

function recursiveWithReturn(node, state, visitor) {
    function c(node, st, override) {
        return visitor[override || node.type](node, st, c);
    }
    return c(node, state);
}

exports.getConstraints = getConstraints;
<commit_msg>Constraint generation for TryStatement<commit_after>var types = require('../domains/types')
var walk = require('acorn/util/walk');

// status:
// { self  : AVal,
//   ret   : AVal,
//   exc   : AVal,
//   delta : Context,
//   sc    : ScopeChain }

// arguments are " oldStatus (, name, val)* "
function changedStatus(oldStatus) {
    var newStatus = Object.create(null);
    for (var i = 1; i < arguments.length; i = i + 2)
        newStatus[arguments[i]] = arguments[i+1];

    for (var p in oldStatus) {
        if (newStatus[p] === undefined)
            newStatus[p] = oldStatus[p];
    }
    return newStatus;
}

var constraints;

function getConstraints(ast, gScope, cx) {
    // temporal set for generated constraints.
    constraints = [];

    // constraint generating walker for expressions
    var constraintGenerator = walk.make({

        Identifier: function (node, curStatus, c) {
            return curStatus.sc.getAValOf(node.name);
        },

        ThisExpression: function (node, curStatus, c) {
            return curStatus.self;
        },

        Literal: function (node, curStatus, c) {
            var res = new types.AVal;
            if (node.regex) {
                // not implemented yet
                // throw new Error('regex literal is not implemented yet');
                return res;
            }
            switch (typeof node.value) {
            case 'number':
                res.addType(types.PrimNumber);
                break;
            case 'string':
                res.addType(types.PrimString);
                break;
            case 'boolean':
                res.addType(types.PrimBoolean);
                break;
            case 'object':
                // I guess: Literal && object ==> node.value == null
                // null is ignored, so nothing to add
                break;
            case 'function':
                throw new Error('I guess function is impossible here.');
            }
            return res;
        },

        AssignmentExpression: function (node, curStatus, c) {
            if (node.left.type === 'MemberExpression') {
                // TODO
                // LHS is not a simple variable.
            } else {
                // LHS is a simple variable.
                var varName = node.left.name;
                var lhsAVal = curStatus.sc.getAValOf(varName);
                var rhsAVal = c(node.right, curStatus, undefined);
                constraints.push({FROM: rhsAVal,
                                  TO: lhsAVal});
                // corresponding AVal is the RHS
                return rhsAVal;
            }
        },

        VariableDeclaration: function (node, curStatus, c) {
            for (var i = 0; i < node.declarations.length; i++) {
                var decl = node.declarations[i];
                if (decl.init) {
                    var lhsAVal = curStatus.sc.getAValOf(decl.id.name);
                    var rhsAVal = c(decl.init, curStatus, undefined);
                    constraints.push({FROM: rhsAVal,
                                      TO: lhsAVal});
                }
            }
        },

        LogicalExpression: function (node, curStatus, c) {
            var res = new types.AVal;
            var left = c(node.left, curStatus, undefined);
            var right = c(node.right, curStatus, undefined);
            constraints.push({FROM: left, TO: res},
                             {FROM: right, TO: res});
            return res;
        },

        ConditionalExpression: function (node, curStatus, c) {
            var res = new types.AVal;
            c(node.test, curStatus, undefined);
            var cons = c(node.consequent, curStatus, undefined);
            var alt = c(node.alternate, curStatus, undefined);
            constraints.push({FROM: cons, TO: res},
                             {FROM: alt, TO: res});
            return res;
        },

        FunctionDeclaration: function (node, curStatus, c) {
            var sc0 = curStatus.sc.removeInitialCatchBlocks();
            if (!node.fnInstances) {
                node.fnInstances = [];
            }
            var fnInstance = null;
            node.fnInstances.forEach(function (fnType) {
                if (fnType.sc === sc0) {
                    fnInstance = fnType;
                }
            });
            if (!fnInstance) {
                fnInstance
                    = new types.FnType(node.id.name,
                                       node.body['@block'].getParamVarNames(),
                                       sc0);
                node.fnInstances.push(fnInstance);
            }
            var lhsAVal = sc0.getAValOf(node.id.name);
            constraints.push({TYPE: fnInstance,
                              INCL_SET: lhsAVal});
            // nothing to return
            return types.AValNull;
        },

        TryStatement: function (node, curStatus, c) {
            // for try block
            c(node.block, curStatus, undefined);

            // for catch block
            var catchBlockSC =
                node.handler.body['@block']
                .getScopeInstance(curStatus.sc, curStatus.delta);
            var catchStatus = changedStatus(curStatus, 'sc', catchBlockSC);
            c(node.handler.body, catchStatus, undefined);

            // for finally block
            if (node.finalizer !== null)
                c(node.finalizer.body, curStatus, undefined);
        },

        CallExpression: function (node, curStatus, c) {
            var resAVal = new types.AVal;
            var argAVals = [];

            // get AVals for each arguments
            for (var i = 0; i < node.arguments.length; i++) {
                argAVals.push(
                    c(node.arguments[i], curStatus, undefined));
            }

            if (node.callee.type === 'MemberExpression') {

                // TODO: method call
            } else {
                // normal function call
                var calleeAVal = c(node.callee, curStatus, undefined);
                // append current call site to the context
                var newDelta = curStatus.delta.appendOne(node['@label']);
                // callee의 return을 call expression으로
                // callee의 exception을 호출 측의 exception에 전달해야
                constraints.push({
                    CALLEE: calleeAVal,
                    SELF: cx.globalObject,
                    PARAMS: argAVals,
                    RET: resAVal,
                    EXC: curStatus.exc,
                    DELTA: newDelta
                });
            }
            return resAVal;
        }
    });

    recursiveWithReturn(ast, gScope, constraintGenerator);

    return constraints;
}

function recursiveWithReturn(node, state, visitor) {
    function c(node, st, override) {
        return visitor[override || node.type](node, st, c);
    }
    return c(node, state);
}

exports.getConstraints = getConstraints;
<|endoftext|>


<commit_before>require(['jquery'], function($) {
	var Customize = {
		onClick: function(e) {
			console.log("Clicked Wizard", "event", e, "this", this);
			this.updateWizard();
		},
		updateWizard: function() {
			console.log("Updating Wizard...");
			this.remove('wizard_add_students');
			this.replaceText("wizard_add_tas", /TA(s?)/g, "TF$1");
		},
		remove: function(id) {
			$("#"+id).remove();
		},
		replaceText: function(id, pattern, replacement) {
			var $itemEl = $("#"+id);
			var $boxEl = this.getMessageBoxEl();
			var that = this;

			// Replace text on the checklist
			that._replaceText($itemEl, pattern, replacement);

			// When the checklist item is clicked, replace text in the message box
			$itemEl.on("click", function(e) {
				that._replaceText($boxEl, pattern, replacement);
			});
		},
		_replaceText: function($el, pattern, replacement) {
			// Replace all descendant text node values
			$el.find("*").contents().filter(function () {
				return this.nodeType === 3;
			}).each(function() {
				console.log("replacing", $el, this);
				this.nodeValue = this.nodeValue.replace(pattern, replacement);
			});
		},
		getMessageBoxEl: function() {
			return $(".CourseWizard__modalOverlay .ic-wizard-box__message");
		}
	};

	Customize.onClick = $.proxy(Customize.onClick, Customize);

	console.log("setting up jquery test...", Customize);

	$(".wizard_popup_link").on("click", Customize.onClick);
});
<commit_msg>Updated jquery test.<commit_after>require(['jquery'], function($) {
	var Customize = {
		onClick: function(e) {
			console.log("Clicked Wizard", "event", e, "this", this);
			this.updateWizard();
		},
		updateWizard: function() {
			console.log("Updating Wizard...");
			this.remove('wizard_add_students');
			this.replaceText("wizard_add_tas", /TA(s?)/g, "TF$1");
		},
		remove: function(id) {
			$("#"+id).remove();
		},
		replaceText: function(id, pattern, replacement) {
			var $itemEl = $("#"+id);
			var $boxEl = this.getMessageBoxEl();
			var that = this;
			console.log("replaceText", id, pattern, replacement);

			// Replace text on the checklist
			that._replaceText($itemEl, pattern, replacement);

			// When the checklist item is clicked, replace text in the message box
			$itemEl.on("click", function(e) {
				console.log("clicked", e, $boxEl);
				that._replaceText($boxEl, pattern, replacement);
			});
		},
		_replaceText: function($el, pattern, replacement) {
			// Replace all descendant text node values
			$el.find("*").contents().filter(function () {
				console.log("filtering", this.nodeType);
				return this.nodeType === 3;
			}).each(function() {
				console.log("replacing", $el, this);
				this.nodeValue = this.nodeValue.replace(pattern, replacement);
			});
		},
		getMessageBoxEl: function() {
			return $(".CourseWizard__modalOverlay .ic-wizard-box__message");
		}
	};

	Customize.onClick = $.proxy(Customize.onClick, Customize);

	console.log("setting up jquery test...", Customize);

	$(".wizard_popup_link").on("click", Customize.onClick);
});
<|endoftext|>


<commit_before>var express = require('express')
var server = express();

var extraPublicOwner;
var extraPublicOwnerContent;
if (process.argv.length > 3) {
  console.info("loading extra public return value for path /public/" + process.argv[2] + " from file: " + process.argv[3]);
  extraPublicOwner = process.argv[2];
  extraPublicOwnerContent = require(process.argv[3]);
}

server.get('/', function(req, res){
  res.send('ready');
});

const mockPublicResponse = require('./test/mockPublicResponse.json');
const mockPublicModifiedResponse = require('./test/mockPublicModifiedResponse.json');
server.get('/v2/public', function(req, res){
  if (req.query.modified){
    res.send(mockPublicModifiedResponse)
  }else{
    res.send(mockPublicResponse)
  }
});

const mockTimoPublicItemsResponse = require('./test/mockTimoPublicItemsResponse.json');
const mockTimoPublicItemsModifiedResponse = require('./test/mockTimoPublicItemsModifiedResponse.json');
server.get('/v2/public/timo', function(req, res){
  if (req.query.modified){
    res.send(mockTimoPublicItemsModifiedResponse);
  }else{
    res.send(mockTimoPublicItemsResponse);
  }
});

const mockLauriPublicItemsResponse = require('./test/mockLauriPublicItemsResponse.json');
const mockEmptyResponse = require('./test/mockEmptyResponse.json');
server.get('/v2/public/lauri', function(req, res){
  if (req.query.modified){
    res.send(mockEmptyResponse);
  }else{
    res.send(mockLauriPublicItemsResponse);
  }
});

const mockJPPublicItemsResponse = require('./test/mockJPPublicItemsResponse.json');
const mockJPPublicItemsModifiedResponse = require('./test/mockJPPublicItemsModifiedResponse.json');
server.get('/v2/public/jp', function(req, res){
  if (req.query.modified){
    res.send(mockJPPublicItemsModifiedResponse);
  }else{
    res.send(mockJPPublicItemsResponse);
  }
});

const mockTCPublicItemsResponse = require('./test/mockTCPublicItemsResponse.json');
server.get('/v2/public/tc', function(req, res){
  if (req.query.modified){
    res.send(mockEmptyResponse);
  }else{
    res.send(mockTCPublicItemsResponse);
  }
});

const mockInfoResponse = require('./test/mockInfoResponse.json');
const mockInfoLatestResponse = require('./test/mockInfoLatestResponse.json');
const mockInfoLatestHistoryResponse = require('./test/mockInfoLatestHistoryResponse.json');
server.get('/v2/info', function(req, res){
  if (req.query.latest === "true" && req.query.history !== "true"){
    res.send(mockInfoLatestResponse);
  }else if (req.query.latest === "true" && req.query.history === "true"){
    res.send(mockInfoLatestHistoryResponse);
  }else {
    res.send(mockInfoResponse);
  }
});

if (extraPublicOwner && extraPublicOwnerContent){
  server.get('/v2/public/' + extraPublicOwner, function(req, res){
    if (req.query.modified){
      res.send(mockEmptyResponse);
    }else{
      res.send(extraPublicOwnerContent);
    }
  });
}

server.get('/v2/short/:id', function(req, res){
  if (req.params.id === "1"){
    res.send({
      handle: "timo",
    });
  } else if (req.params.id === "2"){
    res.send({
      handle: "lauri",
    });
  } else if (req.params.id === "3"){
    res.send({
      handle: "jp",
    });
  } else if (req.params.id === "4"){
    res.send({
      handle: "tc",
    });
  } else if (req.params.id === "421"){
    res.send({
      handle: "timo",
      path: "productivity",
    });
  } else if (req.params.id === "521"){
    res.send({
      handle: "timo",
      path: "timo-test-note",
    });
  } else if (req.params.id === "930"){
    res.send({
      handle: "tc",
      path: "list-of-servers",
    });
  } else if (req.params.id === "939"){
    res.send({
      handle: "tc",
      path: "authored-note",
    });
  } else if (req.params.id === "938"){
    res.send({
      handle: "tc",
      path: "authored-note-2",
    });
  } else if (req.params.id === "521"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note",
    });
  } else if (req.params.id === "522"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-2",
    });
  } else if (req.params.id === "523"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-3",
    });
  } else if (req.params.id === "524"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-4",
    });
  } else if (req.params.id === "525"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-5",
    });
  } else if (req.params.id === "526"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-6",
    });
  // Skip other lauri notes
  } else if (req.params.id === "1021"){
    res.send({
      handle: "jp",
      path: "jp-testing",
    });
  }
});

const mockPreviewResponse = require('./test/mockTimoPreviewItemResponse.json');
server.get('/v2/owners/55449eb6-2fb3-41d5-b806-b4e3be5692cc/data/c876628e-1d67-411a-84f9-5dfedbed8872/preview/1', function(req, res){
  res.send(mockPreviewResponse);
});

server.listen(3004, function () {
  console.log('Minimal express server is running at port 3004')
});
<commit_msg>Added possibility to test modified value<commit_after>var express = require('express')
var server = express();

var extraPublicOwner;
var extraPublicOwnerContent;
var extraPublicOwnerModifiedContent;
if (process.argv.length > 3) {
  console.info("loading extra public return value for path /public/" + process.argv[2] + " from file: " + process.argv[3]);
  extraPublicOwner = process.argv[2];
  extraPublicOwnerContent = require(process.argv[3]);
  if (process.argv.length > 4){
    console.info("loading extra public modified return value for path /public/" + process.argv[2] + "?modified=x from file: " + process.argv[4]);
    extraPublicOwnerModifiedContent = require(process.argv[4]);
  }
}

server.get('/', function(req, res){
  res.send('ready');
});

const mockPublicResponse = require('./test/mockPublicResponse.json');
const mockPublicModifiedResponse = require('./test/mockPublicModifiedResponse.json');
server.get('/v2/public', function(req, res){
  if (req.query.modified){
    res.send(mockPublicModifiedResponse)
  }else{
    res.send(mockPublicResponse)
  }
});

const mockTimoPublicItemsResponse = require('./test/mockTimoPublicItemsResponse.json');
const mockTimoPublicItemsModifiedResponse = require('./test/mockTimoPublicItemsModifiedResponse.json');
server.get('/v2/public/timo', function(req, res){
  if (req.query.modified){
    res.send(mockTimoPublicItemsModifiedResponse);
  }else{
    res.send(mockTimoPublicItemsResponse);
  }
});

const mockLauriPublicItemsResponse = require('./test/mockLauriPublicItemsResponse.json');
const mockEmptyResponse = require('./test/mockEmptyResponse.json');
server.get('/v2/public/lauri', function(req, res){
  if (req.query.modified){
    res.send(mockEmptyResponse);
  }else{
    res.send(mockLauriPublicItemsResponse);
  }
});

const mockJPPublicItemsResponse = require('./test/mockJPPublicItemsResponse.json');
const mockJPPublicItemsModifiedResponse = require('./test/mockJPPublicItemsModifiedResponse.json');
server.get('/v2/public/jp', function(req, res){
  if (req.query.modified){
    res.send(mockJPPublicItemsModifiedResponse);
  }else{
    res.send(mockJPPublicItemsResponse);
  }
});

const mockTCPublicItemsResponse = require('./test/mockTCPublicItemsResponse.json');
server.get('/v2/public/tc', function(req, res){
  if (req.query.modified){
    res.send(mockEmptyResponse);
  }else{
    res.send(mockTCPublicItemsResponse);
  }
});

const mockInfoResponse = require('./test/mockInfoResponse.json');
const mockInfoLatestResponse = require('./test/mockInfoLatestResponse.json');
const mockInfoLatestHistoryResponse = require('./test/mockInfoLatestHistoryResponse.json');
server.get('/v2/info', function(req, res){
  if (req.query.latest === "true" && req.query.history !== "true"){
    res.send(mockInfoLatestResponse);
  }else if (req.query.latest === "true" && req.query.history === "true"){
    res.send(mockInfoLatestHistoryResponse);
  }else {
    res.send(mockInfoResponse);
  }
});

if (extraPublicOwner && extraPublicOwnerContent){
  server.get('/v2/public/' + extraPublicOwner, function(req, res){
    if (req.query.modified){
      if (extraPublicOwnerModifiedContent){
        res.send(extraPublicOwnerModifiedContent);
      } else {
        res.send(mockEmptyResponse);
      }
    }else{
      res.send(extraPublicOwnerContent);
    }
  });
}

server.get('/v2/short/:id', function(req, res){
  if (req.params.id === "1"){
    res.send({
      handle: "timo",
    });
  } else if (req.params.id === "2"){
    res.send({
      handle: "lauri",
    });
  } else if (req.params.id === "3"){
    res.send({
      handle: "jp",
    });
  } else if (req.params.id === "4"){
    res.send({
      handle: "tc",
    });
  } else if (req.params.id === "421"){
    res.send({
      handle: "timo",
      path: "productivity",
    });
  } else if (req.params.id === "521"){
    res.send({
      handle: "timo",
      path: "timo-test-note",
    });
  } else if (req.params.id === "930"){
    res.send({
      handle: "tc",
      path: "list-of-servers",
    });
  } else if (req.params.id === "939"){
    res.send({
      handle: "tc",
      path: "authored-note",
    });
  } else if (req.params.id === "938"){
    res.send({
      handle: "tc",
      path: "authored-note-2",
    });
  } else if (req.params.id === "521"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note",
    });
  } else if (req.params.id === "522"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-2",
    });
  } else if (req.params.id === "523"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-3",
    });
  } else if (req.params.id === "524"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-4",
    });
  } else if (req.params.id === "525"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-5",
    });
  } else if (req.params.id === "526"){
    res.send({
      handle: "lauri",
      path: "lauri-test-note-6",
    });
  // Skip other lauri notes
  } else if (req.params.id === "1021"){
    res.send({
      handle: "jp",
      path: "jp-testing",
    });
  }
});

const mockPreviewResponse = require('./test/mockTimoPreviewItemResponse.json');
server.get('/v2/owners/55449eb6-2fb3-41d5-b806-b4e3be5692cc/data/c876628e-1d67-411a-84f9-5dfedbed8872/preview/1', function(req, res){
  res.send(mockPreviewResponse);
});

server.listen(3004, function () {
  console.log('Minimal express server is running at port 3004')
});
<|endoftext|>