<commit_before>function DomainViewer(guid, store, op_url, upload_url) {
	this.guid = guid;
	this.store = store;
	this.op_url = op_url;
	this.upload_url = upload_url;

	this.domainsGrid;
	this.tabPanel;
}

DomainViewer.prototype.createTabPanel = function() {
    var This = this;
    this.tabPanel = new Ext.TabPanel({
        region: 'center',
        margins: '5 5 5 0',
        enableTabScroll: true,
        activeTab: 0,
        plain: true,
        resizeTabs: true,
        items: [{
            layout: 'fit',
            title: 'Domain Manager',
            autoLoad: {
                url: this.op_url + '/intro'
            }
        }]
        });
    this.tabPanel.on('tabchange', function(tp, tab) {
    	var rec = This.domainsGrid.getStore().find('name', tab.title);
    	if(rec < 0)
            This.domainsGrid.getSelectionModel().deselectAll(true);
    	else
    		This.domainsGrid.getSelectionModel().select(rec, true);
    });
};

DomainViewer.prototype.createLeftPanel = function() {
	var This = this;
	var domainStore = Ext.create('Ext.data.Store', {
        proxy: {
            type: 'memory',
            reader: {
                type: 'json'
            },
            sorters: ['name']
        },
        fields: ['name', 'directory', 'url', 'isLegacy'],
        autoLoad: true,
        data: this.store.list,
        sorters: ['name']
	});

    this.domainsGrid = new Ext.grid.Panel({
        region: 'west',
        width: '25%',
        split: true,
        /*margins: '5 0 5 5',
        cmargins: '5 5 5 5',*/
        margins: 5,
        store: domainStore,
        bodyCls:'multi-line-grid',
        dockedItems: [{
        	xtype: 'toolbar',
        	dock: 'top',
        	items: [
        	{
        	    text: 'Add',
        	    iconCls: 'addIcon',
        	    menu:[
        	    {
        	    	itemId: 'createbutton',
        	    	text: 'Add Domain',
        	    	iconCls: 'addIcon'
        	    },
        	    {
        	    	itemId: 'importbutton',
        	    	text: 'Import Domain',
        	    	iconCls: 'importIcon'
        	    }]
        	}, {
        		itemId: 'selectbutton',
        		text: 'Use',
        		iconCls: 'default_domain'
        	}, {
        		itemId: 'delbutton',
        		text: 'Delete',
        		iconCls: 'delIcon'
        	}, {
        		itemId: 'downloadbutton',
        		text: 'Download',
        		iconCls: 'downloadIcon'
        	}]
        }],
        columns: [{
        	dataIndex: 'name',
        	header: 'Domain',
        	flex: 1,
        	renderer: Ext.bind(This.formatDomainName, This)
        }]
    });
};

DomainViewer.prototype.formatDomainName = function(name, item, rec) {
	var cls = this.store.selected == name ? 'default_domain' : 'domain';
	var isLegacy = rec.get('isLegacy');
	return "
"+name+"
";
};

DomainViewer.prototype.getDomainDetails = function(store) {
	var detailpanel = {
		xtype: 'propertygrid',
		border: false,
		source: store
	};
	return detailpanel;
};

DomainViewer.prototype.openDomain = function(name) {
	var This = this;
	var cls = this.store.selected == name ? 'default_domain' : 'domain';
	var items = this.tabPanel.items.items;
	for(var i=0; i<items.length; i++) {
		if(items[i].title == name) {
			this.tabPanel.setActiveTab(items[i]);
			return items[i];
		}
	}

	var tab = new Ext.Panel({
		layout : 'fit',
		closable : true,
		iconCls : cls,
		border: false,
		title : name,
		items : []
	});
    this.tabPanel.add(tab);
    Ext.apply(tab, {
        loader: {
            loadMask: true,
            url: This.op_url+"/getDomainDetails?domain="+name,
            renderer: function(loader, response, req) {
                var store = Ext.decode(response.responseText);
                if (store) {
                    tab.removeAll();
                    tab.add(This.getDomainDetails(store));
                }
            }
        }
    });
    this.tabPanel.setActiveTab(tab);
    tab.getLoader().load();

    return tab;
};

DomainViewer.prototype.createDomain = function(domname) {
	var This = this;
    var domname = getRDFID(domname);
    var rec = This.domainsGrid.getStore().find('name', domname);
    if (rec != -1) {
    	console.log(rec);
        showError(domname + ' already exists ! Choose a different name.');
        return;
    }
    var url = This.op_url + '/createDomain';
    Ext.get(This.domainsGrid.getId()).mask("Creating Domain..");
    Ext.Ajax.request({
        url: url,
        params: {
        	domain: domname
        },
        success: function(response) {
        	 Ext.get(This.domainsGrid.getId()).unmask();
        	 try {
        		 var ret = Ext.decode(response.responseText);
        		 if(!ret.error && ret.name) {
        			 This.domainsGrid.getStore().add(ret);
        		 }
        		 else {
        			 _console(ret);
        			 showError(ret.error);
        		 }
        	 }
        	 catch (e) {
        		 _console(e.message);
        	 }
        },
        failure: function(response) {
        	Ext.get(This.tabPanel.getId()).unmask();
        	_console(response.responseText);
        }

    });
};

DomainViewer.prototype.importDomain = function(domname, loc) {
	var This = this;
    var domname = getRDFID(domname);
    var rec = This.domainsGrid.getStore().find('name', domname);
    if (rec != -1) {
    	console.log(rec);
        showError(domname + ' already exists ! Choose a different name.');
        return;
    }
    var url = This.op_url + '/importDomain';
    Ext.get(This.domainsGrid.getId()).mask("Importing Domain..");
    Ext.Ajax.request({
        url: url,
        params: {
        	domain: domname,
            location: loc,
        },
        success: function(response) {
        	 Ext.get(This.domainsGrid.getId()).unmask();
        	 try {
        		 var ret = Ext.decode(response.responseText);
        		 if(!ret.error && ret.name) {
        			 This.domainsGrid.getStore().add(ret);
        		 }
        		 else {
        			 _console(ret);
        			 showError(ret.error);
        		 }
        	 }
        	 catch (e) {
        		 _console(e.message);
        	 }
        },
        failure: function(response) {
        	Ext.get(This.tabPanel.getId()).unmask();
        	_console(response.responseText);
        }

    });
};

DomainViewer.prototype.selectDomain = function(domname) {
	var This = this;
    var url = This.op_url + '/selectDomain';
    Ext.get(This.domainsGrid.getId()).mask("Selecting Domain..");
    Ext.Ajax.request({
        url: url,
        params: {
        	domain: domname
        },
        success: function(response) {
        	 Ext.get(This.domainsGrid.getId()).unmask();
        	 try {
        		 if(response.responseText == "OK") {
        			 This.store.selected = domname;
            		 This.domainsGrid.reconfigure();
        		 }
        		 else {
        			 _console(response.responseText);
        		 }
        	 }
        	 catch (e) {
        		 _console(e.message);
        	 }
        },
        failure: function(response) {
        	Ext.get(This.tabPanel.getId()).unmask();
        	_console(response.responseText);
        }

    });
};

DomainViewer.prototype.deleteDomain = function(domname) {
	var This = this;
    var url = This.op_url + '/deleteDomain';
    Ext.get(This.domainsGrid.getId()).mask("Deleting Domain..");
    Ext.Ajax.request({
        url: url,
        params: {
        	domain: domname
        },
        success: function(response) {
        	 Ext.get(This.domainsGrid.getId()).unmask();
        	 try {
        		 if(response.responseText == "OK") {
        			 var rec = This.domainsGrid.getStore().find('name', domname);
        			 This.domainsGrid.getStore().removeAt(rec);
        		 }
        		 else {
        			 _console(response.responseText);
        		 }
        	 }
        	 catch (e) {
        		 _console(e.message);
        	 }
        },
        failure: function(response) {
        	Ext.get(This.tabPanel.getId()).unmask();
        	_console(response.responseText);
        }

    });
};

DomainViewer.prototype.showImportDomainDialog = function() {
	var This = this;
    var win = new Ext.Window({
        xtype: 'panel',
        layout: {
            type: 'accordion'
        },
        width: 500,
        items: [
        {
            xtype: 'panel',
            title: 'Enter Domain Location',
            border: false,
            layout: {
                type: 'hbox',
                defaultMargins: 10
            },
            items: [{
                xtype: 'textfield',
                flex: 1,
                emptyText: 'Enter domain location or Upload from below'
            }, {
                xtype: 'button',
                text: 'Submit',
                iconCls: 'addIcon',
                handler: function() {
                	var panel = this.up('panel');
                	var loc = this.prev().value;
                	if(!loc) {
                		showError('Please enter the domain location or Upload from below');
                		return;
                	}
                	win.close();
                	var domname = loc.replace(/^.+\//,"");
                	domname = domname.replace(/^.+\\/,"");
                	This.importDomain(domname, loc);
                }
            }]
        }, {
            	xtype: 'xuploadpanel',
            	collapsed: true,
            	title: 'Upload Domain',
            	collapsible: true,
            	border: false,
            	multipart_params: {type: 'domain'},
                url: This.upload_url,
                addButtonCls: 'addIcon',
                deleteButtonCls: 'delIcon',
                uploadButtonCls: 'uploadIcon',
                cancelButtonCls: 'delIcon',
                listeners : {
                	"uploadcomplete" : function(item, files) {
                		// Just check the first file (only one file upload allowed here)
                		var file = files[0];
                		var loc = file.location;
                		win.close();
                		var domname = loc.replace(/^.+\//,"");
                		domname = domname.replace(/^.+\\/,"");
                		This.importDomain(domname, loc);
                	}
                }
        }]
    });
    win.show();
};

DomainViewer.prototype.createListeners = function() {
	var This = this;
	this.domainsGrid.on("select", function(item, rec) {
		if(This.tabPanel)
			This.openDomain(rec.get('name'));
	});
	this.domainsGrid.down('#createbutton').on("click", function() {
	    Ext.Msg.prompt("Create Domain", "Enter name for new domain", function(btn, text) {
	        if (btn == 'ok' && text) {
	        	This.createDomain(text);
	        }
	    });
	});
	this.domainsGrid.down('#importbutton').on("click", function() {
		This.showImportDomainDialog();
	});
	this.domainsGrid.down('#downloadbutton').on("click", function() {
		var sels = This.domainsGrid.getSelectionModel().getSelection();
		if(!sels.length) {
			showError("Click on a domain first");
			return;
		}
		var domain = sels[0];
		window.open(This.op_url+"/downloadDomain?domain="+escape(domain.get("name")));
	});
	this.domainsGrid.down('#selectbutton').on("click", function() {
		var sels = This.domainsGrid.getSelectionModel().getSelection();
		if(!sels.length) {
			showError("Click on a domain first");
			return;
		}
		var domain = sels[0];
		This.selectDomain(domain.get("name"));
	});
	this.domainsGrid.down('#delbutton').on("click", function() {
		// Delete domain
		var sels = This.domainsGrid.getSelectionModel().getSelection();
		if(!sels.length) {
			showError("Click on a domain first");
			return;
		}
		var domain = sels[0];
	    Ext.MessageBox.confirm("Confirm Delete", "Are you sure you want to Delete " + domain.get('name'), function(b) {
	        if (b == "yes") {
	        	This.deleteDomain(domain.get("name"));
	        }
	    });
	});
};

DomainViewer.prototype.createMainPanel = function() {
    this.mainPanel = new Ext.Viewport({
        layout: {
            type: 'border'
        },
        items: [this.domainsGrid, this.tabPanel]
    });
    this.mainPanel.add(getPortalHeader(CONTEXT_ROOT));
};

DomainViewer.prototype.initialize_bak = function() {
    this.createLeftPanel();
    this.createTabPanel();
    this.createMainPanel();
    this.createListeners();
};

DomainViewer.prototype.initialize = function() {
    this.createLeftPanel();
    Ext.apply(this.domainsGrid, {
    	region: 'center'
    });
    this.mainPanel = new Ext.Viewport({
        layout: 'border',
        items: [getPortalHeader(CONTEXT_ROOT), this.domainsGrid]
    });
    this.createListeners();
};<commit_msg>Bugfix<commit_after>function DomainViewer(guid, store, op_url, upload_url) {
	this.guid = guid;
	this.store = store;
	this.op_url = op_url;
	this.upload_url = upload_url;

	this.domainsGrid;
	this.tabPanel;
}

DomainViewer.prototype.createTabPanel = function() {
    var This = this;
    this.tabPanel = new Ext.TabPanel({
        region: 'center',
        margins: '5 5 5 0',
        enableTabScroll: true,
        activeTab: 0,
        plain: true,
        resizeTabs: true,
        items: [{
            layout: 'fit',
            title: 'Domain Manager',
            autoLoad: {
                url: this.op_url + '/intro'
            }
        }]
        });
    this.tabPanel.on('tabchange', function(tp, tab) {
    	var rec = This.domainsGrid.getStore().find('name', tab.title);
    	if(rec < 0)
            This.domainsGrid.getSelectionModel().deselectAll(true);
    	else
    		This.domainsGrid.getSelectionModel().select(rec, true);
    });
};

DomainViewer.prototype.createLeftPanel = function() {
	var This = this;
	var domainStore = Ext.create('Ext.data.Store', {
        proxy: {
            type: 'memory',
            reader: {
                type: 'json'
            },
            sorters: ['name']
        },
        fields: ['name', 'directory', 'url', 'isLegacy'],
        autoLoad: true,
        data: this.store.list,
        sorters: ['name']
	});

    this.domainsGrid = new Ext.grid.Panel({
        region: 'west',
        width: '25%',
        split: true,
        /*margins: '5 0 5 5',
        cmargins: '5 5 5 5',*/
        margins: 5,
        store: domainStore,
        bodyCls:'multi-line-grid',
        dockedItems: [{
        	xtype: 'toolbar',
        	dock: 'top',
        	items: [
        	{
        	    text: 'Add',
        	    iconCls: 'addIcon',
        	    menu:[
        	    {
        	    	itemId: 'createbutton',
        	    	text: 'Add Domain',
        	    	iconCls: 'addIcon'
        	    },
        	    {
        	    	itemId: 'importbutton',
        	    	text: 'Import Domain',
        	    	iconCls: 'importIcon'
        	    }]
        	}, {
        		itemId: 'selectbutton',
        		text: 'Use',
        		iconCls: 'default_domain'
        	}, {
        		itemId: 'delbutton',
        		text: 'Delete',
        		iconCls: 'delIcon'
        	}, {
        		itemId: 'downloadbutton',
        		text: 'Download',
        		iconCls: 'downloadIcon'
        	}]
        }],
        columns: [{
        	dataIndex: 'name',
        	header: 'Domain',
        	flex: 1,
        	renderer: Ext.bind(This.formatDomainName, This)
        }]
    });
};

DomainViewer.prototype.formatDomainName = function(name, item, rec) {
	var cls = this.store.selected == name ? 'default_domain' : 'domain';
	var isLegacy = rec.get('isLegacy');
	return "
"+name+"
";
};

DomainViewer.prototype.getDomainDetails = function(store) {
	var detailpanel = {
		xtype: 'propertygrid',
		border: false,
		source: store
	};
	return detailpanel;
};

DomainViewer.prototype.openDomain = function(name) {
	var This = this;
	var cls = this.store.selected == name ? 'default_domain' : 'domain';
	var items = this.tabPanel.items.items;
	for(var i=0; i<items.length; i++) {
		if(items[i].title == name) {
			this.tabPanel.setActiveTab(items[i]);
			return items[i];
		}
	}

	var tab = new Ext.Panel({
		layout : 'fit',
		closable : true,
		iconCls : cls,
		border: false,
		title : name,
		items : []
	});
    this.tabPanel.add(tab);
    Ext.apply(tab, {
        loader: {
            loadMask: true,
            url: This.op_url+"/getDomainDetails?domain="+name,
            renderer: function(loader, response, req) {
                var store = Ext.decode(response.responseText);
                if (store) {
                    tab.removeAll();
                    tab.add(This.getDomainDetails(store));
                }
            }
        }
    });
    this.tabPanel.setActiveTab(tab);
    tab.getLoader().load();

    return tab;
};

DomainViewer.prototype.createDomain = function(domname) {
	var This = this;
    var domname = getRDFID(domname);
    var rec = This.domainsGrid.getStore().find('name', domname);
    if (rec != -1) {
    	console.log(rec);
        showError(domname + ' already exists ! Choose a different name.');
        return;
    }
    var url = This.op_url + '/createDomain';
    Ext.get(This.domainsGrid.getId()).mask("Creating Domain..");
    Ext.Ajax.request({
        url: url,
        params: {
        	domain: domname
        },
        success: function(response) {
        	 Ext.get(This.domainsGrid.getId()).unmask();
        	 try {
        		 var ret = Ext.decode(response.responseText);
        		 if(!ret.error && ret.name) {
        			 This.domainsGrid.getStore().add(ret);
        		 }
        		 else {
        			 _console(ret);
        			 showError(ret.error);
        		 }
        	 }
        	 catch (e) {
        		 _console(e.message);
        	 }
        },
        failure: function(response) {
        	Ext.get(This.domainsGrid.getId()).unmask();
        	_console(response.responseText);
        }

    });
};

DomainViewer.prototype.importDomain = function(domname, loc) {
	var This = this;
    var domname = getRDFID(domname);
    var rec = This.domainsGrid.getStore().find('name', domname);
    if (rec != -1) {
    	console.log(rec);
        showError(domname + ' already exists ! Choose a different name.');
        return;
    }
    var url = This.op_url + '/importDomain';
    Ext.get(This.domainsGrid.getId()).mask("Importing Domain..");
    Ext.Ajax.request({
        url: url,
        params: {
        	domain: domname,
            location: loc,
        },
        success: function(response) {
        	 Ext.get(This.domainsGrid.getId()).unmask();
        	 try {
        		 var ret = Ext.decode(response.responseText);
        		 if(!ret.error && ret.name) {
        			 This.domainsGrid.getStore().add(ret);
        		 }
        		 else {
        			 _console(ret);
        			 showError(ret.error);
        		 }
        	 }
        	 catch (e) {
        		 _console(e.message);
        	 }
        },
        failure: function(response) {
        	Ext.get(This.domainsGrid.getId()).unmask();
        	_console(response.responseText);
        }

    });
};

DomainViewer.prototype.selectDomain = function(domname) {
	var This = this;
    var url = This.op_url + '/selectDomain';
    Ext.get(This.domainsGrid.getId()).mask("Selecting Domain..");
    Ext.Ajax.request({
        url: url,
        params: {
        	domain: domname
        },
        success: function(response) {
        	 Ext.get(This.domainsGrid.getId()).unmask();
        	 try {
        		 if(response.responseText == "OK") {
        			 This.store.selected = domname;
            		 This.domainsGrid.reconfigure();
        		 }
        		 else {
        			 _console(response.responseText);
        		 }
        	 }
        	 catch (e) {
        		 _console(e.message);
        	 }
        },
        failure: function(response) {
        	Ext.get(This.domainsGrid.getId()).unmask();
        	_console(response.responseText);
        }

    });
};

DomainViewer.prototype.deleteDomain = function(domname) {
	var This = this;
    var url = This.op_url + '/deleteDomain';
    Ext.get(This.domainsGrid.getId()).mask("Deleting Domain..");
    Ext.Ajax.request({
        url: url,
        params: {
        	domain: domname
        },
        success: function(response) {
        	 Ext.get(This.domainsGrid.getId()).unmask();
        	 try {
        		 if(response.responseText == "OK") {
        			 var rec = This.domainsGrid.getStore().find('name', domname);
        			 This.domainsGrid.getStore().removeAt(rec);
        		 }
        		 else {
        			 _console(response.responseText);
        		 }
        	 }
        	 catch (e) {
        		 _console(e.message);
        	 }
        },
        failure: function(response) {
        	Ext.get(This.domainsGrid.getId()).unmask();
        	_console(response.responseText);
        }

    });
};

DomainViewer.prototype.showImportDomainDialog = function() {
	var This = this;
    var win = new Ext.Window({
        xtype: 'panel',
        layout: {
            type: 'accordion'
        },
        width: 500,
        items: [
        {
            xtype: 'panel',
            title: 'Enter Domain Location',
            border: false,
            layout: {
                type: 'hbox',
                defaultMargins: 10
            },
            items: [{
                xtype: 'textfield',
                flex: 1,
                emptyText: 'Enter domain location or Upload from below'
            }, {
                xtype: 'button',
                text: 'Submit',
                iconCls: 'addIcon',
                handler: function() {
                	var panel = this.up('panel');
                	var loc = this.prev().value;
                	if(!loc) {
                		showError('Please enter the domain location or Upload from below');
                		return;
                	}
                	win.close();
                	var domname = loc.replace(/^.+\//,"");
                	domname = domname.replace(/^.+\\/,"");
                	This.importDomain(domname, loc);
                }
            }]
        }, {
            	xtype: 'xuploadpanel',
            	collapsed: true,
            	title: 'Upload Domain',
            	collapsible: true,
            	border: false,
            	multipart_params: {type: 'domain'},
                url: This.upload_url,
                addButtonCls: 'addIcon',
                deleteButtonCls: 'delIcon',
                uploadButtonCls: 'uploadIcon',
                cancelButtonCls: 'delIcon',
                listeners : {
                	"uploadcomplete" : function(item, files) {
                		// Just check the first file (only one file upload allowed here)
                		var file = files[0];
                		var loc = file.location;
                		win.close();
                		var domname = loc.replace(/^.+\//,"");
                		domname = domname.replace(/^.+\\/,"");
                		This.importDomain(domname, loc);
                	}
                }
        }]
    });
    win.show();
};

DomainViewer.prototype.createListeners = function() {
	var This = this;
	this.domainsGrid.on("select", function(item, rec) {
		if(This.tabPanel)
			This.openDomain(rec.get('name'));
	});
	this.domainsGrid.down('#createbutton').on("click", function() {
	    Ext.Msg.prompt("Create Domain", "Enter name for new domain", function(btn, text) {
	        if (btn == 'ok' && text) {
	        	This.createDomain(text);
	        }
	    });
	});
	this.domainsGrid.down('#importbutton').on("click", function() {
		This.showImportDomainDialog();
	});
	this.domainsGrid.down('#downloadbutton').on("click", function() {
		var sels = This.domainsGrid.getSelectionModel().getSelection();
		if(!sels.length) {
			showError("Click on a domain first");
			return;
		}
		var domain = sels[0];
		window.open(This.op_url+"/downloadDomain?domain="+escape(domain.get("name")));
	});
	this.domainsGrid.down('#selectbutton').on("click", function() {
		var sels = This.domainsGrid.getSelectionModel().getSelection();
		if(!sels.length) {
			showError("Click on a domain first");
			return;
		}
		var domain = sels[0];
		This.selectDomain(domain.get("name"));
	});
	this.domainsGrid.down('#delbutton').on("click", function() {
		// Delete domain
		var sels = This.domainsGrid.getSelectionModel().getSelection();
		if(!sels.length) {
			showError("Click on a domain first");
			return;
		}
		var domain = sels[0];
	    Ext.MessageBox.confirm("Confirm Delete", "Are you sure you want to Delete " + domain.get('name'), function(b) {
	        if (b == "yes") {
	        	This.deleteDomain(domain.get("name"));
	        }
	    });
	});
};

DomainViewer.prototype.createMainPanel = function() {
    this.mainPanel = new Ext.Viewport({
        layout: {
            type: 'border'
        },
        items: [this.domainsGrid, this.tabPanel]
    });
    this.mainPanel.add(getPortalHeader(CONTEXT_ROOT));
};

DomainViewer.prototype.initialize_bak = function() {
    this.createLeftPanel();
    this.createTabPanel();
    this.createMainPanel();
    this.createListeners();
};

DomainViewer.prototype.initialize = function() {
    this.createLeftPanel();
    Ext.apply(this.domainsGrid, {
    	region: 'center'
    });
    this.mainPanel = new Ext.Viewport({
        layout: 'border',
        items: [getPortalHeader(CONTEXT_ROOT), this.domainsGrid]
    });
    this.createListeners();
};<|endoftext|>


<commit_before>/**
 * Global variables
 */

// Database sheet
var databaseSheet = SpreadsheetApp.openById('1jbj7zbpqvkKXF-QjLq0S3Lw21f_fBsAweKbiwE9j5Ig').getSheets()[0];
var recordRange = "E3:I24";
var attendRange = "K2:O2";
var contentClearRange = "E3:H24";
var dataValiClearRange = "F3:G24";
var inputDateCell = "C2";
var attendanceInputRange = "C2:C6";
var okToSubmit = "C15";
var rowStart = 3;
var rowEnd = 24;

// Attandence sheet
var attandenceSheet = SpreadsheetApp.openById('1dpdU3CgIamunwLS_Opv5zrNCVypE6AEAn80iZXbRMic').getSheets()[0];

//Punch Card sheet
var punchCardSheet = SpreadsheetApp.openById('1d3jLeX_FcNEEq_bQvcK7LRKQnq01-8hKHH5JSR_HH5k');

// Drop List sheet @param string[][]
var dropList = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("List").getDataRange().getValues();

// Hash map for main drop list
var dropListHash = {
    labels: {},
    init: function() {
        for (var i = 1; i < dropList.length; i++) {
            dropListHash.labels[dropList[i][0]] = i;
        }
    }
};
dropListHash.init();

var punchUserHash = {
    labels: {},
    init: function() {
        var userList = punchCardSheet.getSheetByName("Member").getDataRange().getValues();
        // Find out col number
        for (var i = 0; i < userList.length; i++) {
            punchUserHash.labels[userList[i][0]] = i;
        }
    }
};
punchUserHash.init();

/* Flag cells, condination is (row, col)
* OK to submit flag: H8 (8,8)
* Record is Ok flag: A4-A25 (4,1)-(25,1)

/**
 * Get Current Google User
 */

function getCurrentUser() {
    return Session.getActiveUser().getEmail();
}

/**
 * Check if it is ready to submit
 */
function submit() {
    var sheet = SpreadsheetApp.getActiveSheet();
    var ui = SpreadsheetApp.getUi();
    var date = sourceSheet.getRange(inputDateCell).getValue();
    var weekDay = {
        1: '月',
        2: '火',
        3: '水',
        4: '木',
        5: '金',
        6: '土',
        0: '日'
    };
    var row = dayOfYear(date); //convert date into row number
    var status = sheet.getRange(okToSubmit).getValue();
    if (status == 'NG') {
        ui.alert('記入内容を修正してください。');
    } else if (isPunched(getCurrentUser(), row)) {
        ui.alert('この日はすでに記録があります。日付をチェックしてください。');
    } else if (ui.alert(String(date.getMonth() + 1) + "月" + String(date.getDate()) + "日" + "(" + weekDay[date.getDay()] + ")分の記入内容を登録しますか？",
            ui.ButtonSet.YES_NO) == ui.Button.YES) {
        // Add records in punch card
        punchCard(getCurrentUser(), row);
        insertNewRecords(sheet);
    }
}

/**
 * Insert new records to database sheet
 */
function insertNewRecords(sourceSheet) {
    // Append items to database sheet
    var content = sourceSheet.getRange(recordRange).getValues();
    var userName = getCurrentUser();
    var inputDate = sourceSheet.getRange(inputDateCell).getValue();
    for (var i = 0; i < content.length; i++) {
        if (content[i][content[i].length - 1] == "OK") { // Record is Ok to submit flag:
            content[i][content[i].length - 2] = timeStringToFloat(content[i][content[i].length - 2]);
            databaseSheet.appendRow([userName, inputDate].concat(content[i].slice(0, content[i].length - 1)));
        }
    }
    // Append items to attendance sheet
    var attendData = sourceSheet.getRange(attendRange).getValues();
    attendData[0][attendData[0].length - 1] = timeStringToFloat(attendData[0][attendData[0].length - 1]);
    attandenceSheet.appendRow([userName, inputDate].concat(attendData[0]));
    SpreadsheetApp.getUi().alert('登録完了');
}

/**
 * Check duplication insertion, if not, punch card and send ok to proceed submit
 */
function isPunched(user, row) {
    var offset = 2;
    // Find out col number
    var col = punchUserHash.labels[user] + 3;
    var punchSheet = punchCardSheet.getSheetByName("Punch");
    // Punch
    if (punchSheet.getRange(row + offset, col).getValue) {
        return false;
    } else {
        return true;
    }
}

function punchCard(user, row) {
    var offset = 2;
    // Find out col number
    var col = punchUserHash.labels[user] + 3;
    var punchSheet = punchCardSheet.getSheetByName("Punch");
    // Punch
    punchSheet.getRange(row + offset, col).setValue(1);
    punchSheet.getRange(row + offset, col).setBackground("#68ed9b");
    punchSheet.getRange(row + offset, col).setFontColor("#68ed9b");
}

// Day of year
function dayOfYear(date) {
    if (!(date instanceof Date)) {
        return 0;
    }
    var start = new Date(date.getFullYear(), 0, 0);
    var diff = date - start;
    var oneDay = 86400000; // (1000*60*60*24)
    return Math.floor(diff / oneDay);
}

/**
 * Clear Current Table
 */
function clearContents() {
    var sheet = SpreadsheetApp.getActiveSheet();
    sheet.getRange(contentClearRange).clear({
        contentsOnly: true
    });
    sheet.getRange(dataValiClearRange).clear({
        validationsOnly: true
    });
    // Fill in default date, work time
    sheet.getRange(attendanceInputRange).setValues([
        [new Date()],
        ["9:00"],
        ["17:45"],
        ["1:00"],
        ["No"]
    ]);
}

/**
 * Generate sub drop list
 */

function onEdit(e) {
    var row = e.range.getRow();
    var col = e.range.getColumn();
    if (e.range.getHeight() > 1) {
        return void(0);
    }
    // When main class is selected
    var cell = e.range.getSheet().getRange(row, col + 1);
    if (row <= rowEnd && row >= rowStart && col == 5) { // set sub class items
        var rule = SpreadsheetApp.newDataValidation().requireValueInList(dropList[dropListHash.labels[e.value]].slice(1), true).build();
        cell.setDataValidation(rule);
        // Set phase items
        cell = e.source.getActiveSheet().getRange(row, col + 2);
        if (dropListHash.labels[e.value] != 1) {
            cell.clearDataValidations();
            cell.setValue("-");
        } else {
            var rule = SpreadsheetApp.newDataValidation().requireValueInList(dropList[dropList.length - 1].slice(1), true).build();
            cell.setDataValidation(rule);
        }
    }
}

// Covert 2:30 time to 2.5 format
function timeStringToFloat(time) {
    var hours = time.getHours();
    var minutes = time.getMinutes();
    return hours + minutes / 60;
}

/**
 * UI related
 */

function onOpen(e) {
    SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
        .createMenu('Custom Menu')
        .addItem('Show sidebar', 'showSidebar')
        .addToUi();
    showSidebar();
}

function showSidebar() {
    var html = HtmlService.createTemplateFromFile('Sidebar').evaluate()
        .setSandboxMode(HtmlService.SandboxMode.IFRAME);

    SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
        .showSidebar(html);
}

function include(filename) {
    return HtmlService.createHtmlOutputFromFile(filename)
        .setSandboxMode(HtmlService.SandboxMode.IFRAME)
        .getContent();
}

function nipp() {
    var sheet = SpreadsheetApp.getActiveSheet();
    insertNewRecords(sheet);
}
<commit_msg>handle when e.value === undefined<commit_after>/**
 * Global variables
 */

// Database sheet
var databaseSheet = SpreadsheetApp.openById('1jbj7zbpqvkKXF-QjLq0S3Lw21f_fBsAweKbiwE9j5Ig').getSheets()[0];
var recordRange = "E3:I24";
var attendRange = "K2:O2";
var contentClearRange = "E3:H24";
var dataValiClearRange = "F3:G24";
var inputDateCell = "C2";
var attendanceInputRange = "C2:C6";
var okToSubmit = "C15";
var rowStart = 3;
var rowEnd = 24;

// Attandence sheet
var attandenceSheet = SpreadsheetApp.openById('1dpdU3CgIamunwLS_Opv5zrNCVypE6AEAn80iZXbRMic').getSheets()[0];

//Punch Card sheet
var punchCardSheet = SpreadsheetApp.openById('1d3jLeX_FcNEEq_bQvcK7LRKQnq01-8hKHH5JSR_HH5k');

// Drop List sheet @param string[][]
var dropList = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("List").getDataRange().getValues();

// Hash map for main drop list
var dropListHash = {
    labels: {},
    init: function() {
        for (var i = 1; i < dropList.length; i++) {
            dropListHash.labels[dropList[i][0]] = i;
        }
    }
};
dropListHash.init();

var punchUserHash = {
    labels: {},
    init: function() {
        var userList = punchCardSheet.getSheetByName("Member").getDataRange().getValues();
        // Find out col number
        for (var i = 0; i < userList.length; i++) {
            punchUserHash.labels[userList[i][0]] = i;
        }
    }
};
punchUserHash.init();

/* Flag cells, condination is (row, col)
* OK to submit flag: H8 (8,8)
* Record is Ok flag: A4-A25 (4,1)-(25,1)

/**
 * Get Current Google User
 */

function getCurrentUser() {
    return Session.getActiveUser().getEmail();
}

/**
 * Check if it is ready to submit
 */
function submit() {
    var sheet = SpreadsheetApp.getActiveSheet();
    var ui = SpreadsheetApp.getUi();
    var date = sourceSheet.getRange(inputDateCell).getValue();
    var weekDay = {
        1: '月',
        2: '火',
        3: '水',
        4: '木',
        5: '金',
        6: '土',
        0: '日'
    };
    var row = dayOfYear(date); //convert date into row number
    var status = sheet.getRange(okToSubmit).getValue();
    if (status == 'NG') {
        ui.alert('記入内容を修正してください。');
    } else if (isPunched(getCurrentUser(), row)) {
        ui.alert('この日はすでに記録があります。日付をチェックしてください。');
    } else if (ui.alert(String(date.getMonth() + 1) + "月" + String(date.getDate()) + "日" + "(" + weekDay[date.getDay()] + ")分の記入内容を登録しますか？",
            ui.ButtonSet.YES_NO) == ui.Button.YES) {
        // Add records in punch card
        punchCard(getCurrentUser(), row);
        insertNewRecords(sheet);
    }
}

/**
 * Insert new records to database sheet
 */
function insertNewRecords(sourceSheet) {
    // Append items to database sheet
    var content = sourceSheet.getRange(recordRange).getValues();
    var userName = getCurrentUser();
    var inputDate = sourceSheet.getRange(inputDateCell).getValue();
    for (var i = 0; i < content.length; i++) {
        if (content[i][content[i].length - 1] == "OK") { // Record is Ok to submit flag:
            content[i][content[i].length - 2] = timeStringToFloat(content[i][content[i].length - 2]);
            databaseSheet.appendRow([userName, inputDate].concat(content[i].slice(0, content[i].length - 1)));
        }
    }
    // Append items to attendance sheet
    var attendData = sourceSheet.getRange(attendRange).getValues();
    attendData[0][attendData[0].length - 1] = timeStringToFloat(attendData[0][attendData[0].length - 1]);
    attandenceSheet.appendRow([userName, inputDate].concat(attendData[0]));
    SpreadsheetApp.getUi().alert('登録完了');
}

/**
 * Check duplication insertion, if not, punch card and send ok to proceed submit
 */
function isPunched(user, row) {
    var offset = 2;
    // Find out col number
    var col = punchUserHash.labels[user] + 3;
    var punchSheet = punchCardSheet.getSheetByName("Punch");
    // Punch
    if (punchSheet.getRange(row + offset, col).getValue) {
        return false;
    } else {
        return true;
    }
}

function punchCard(user, row) {
    var offset = 2;
    // Find out col number
    var col = punchUserHash.labels[user] + 3;
    var punchSheet = punchCardSheet.getSheetByName("Punch");
    // Punch
    punchSheet.getRange(row + offset, col).setValue(1);
    punchSheet.getRange(row + offset, col).setBackground("#68ed9b");
    punchSheet.getRange(row + offset, col).setFontColor("#68ed9b");
}

// Day of year
function dayOfYear(date) {
    if (!(date instanceof Date)) {
        return 0;
    }
    var start = new Date(date.getFullYear(), 0, 0);
    var diff = date - start;
    var oneDay = 86400000; // (1000*60*60*24)
    return Math.floor(diff / oneDay);
}

/**
 * Clear Current Table
 */
function clearContents() {
    var sheet = SpreadsheetApp.getActiveSheet();
    sheet.getRange(contentClearRange).clear({
        contentsOnly: true
    });
    sheet.getRange(dataValiClearRange).clear({
        validationsOnly: true
    });
    // Fill in default date, work time
    sheet.getRange(attendanceInputRange).setValues([
        [new Date()],
        ["9:00"],
        ["17:45"],
        ["1:00"],
        ["No"]
    ]);
}

/**
 * Generate sub drop list
 */

function onEdit(e) {
    var row = e.range.getRow();
    var col = e.range.getColumn();
    if (e.range.getHeight() > 1) {
        return void(0);
    }
    if (e.value === undefined) {
        e.range.getSheet().getRange(row, col + 1, 1, 2).clear();
    } else {
        // When main class is selected
        var cell = e.range.getSheet().getRange(row, col + 1);
        if (row <= rowEnd && row >= rowStart && col == 5) { // set sub class items
            var rule = SpreadsheetApp.newDataValidation().requireValueInList(dropList[dropListHash.labels[e.value]].slice(1), true).build();
            cell.setDataValidation(rule);
            // Set phase items
            cell = e.source.getActiveSheet().getRange(row, col + 2);
            if (dropListHash.labels[e.value] != 1) {
                cell.clearDataValidations();
                cell.setValue("-");
            } else {
                var rule = SpreadsheetApp.newDataValidation().requireValueInList(dropList[dropList.length - 1].slice(1), true).build();
                cell.setDataValidation(rule);
            }
        }
    }
}

// Covert 2:30 time to 2.5 format
function timeStringToFloat(time) {
    var hours = time.getHours();
    var minutes = time.getMinutes();
    return hours + minutes / 60;
}

/**
 * UI related
 */

function onOpen(e) {
    SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
        .createMenu('Custom Menu')
        .addItem('Show sidebar', 'showSidebar')
        .addToUi();
    showSidebar();
}

function showSidebar() {
    var html = HtmlService.createTemplateFromFile('Sidebar').evaluate()
        .setSandboxMode(HtmlService.SandboxMode.IFRAME);

    SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
        .showSidebar(html);
}

function include(filename) {
    return HtmlService.createHtmlOutputFromFile(filename)
        .setSandboxMode(HtmlService.SandboxMode.IFRAME)
        .getContent();
}

function nipp() {
    var sheet = SpreadsheetApp.getActiveSheet();
    insertNewRecords(sheet);
}
<|endoftext|>


<commit_before>'use strict'

function _getSubscriptionContainer() {
  return document.getElementById('subscription-manager-container')
}

Promise.all([
  __collections,
  __subscriptions
]).then(valueArr => {
  var collections = valueArr[0]
  var subscriptions = valueArr[1]

  template.render('manager-section',{ quantity: Object.keys(collections).length }).then(html => {
    var elem = document.createElement('div')
    _getSubscriptionContainer().parentNode.insertBefore(elem,_getSubscriptionContainer())
    elem.outerHTML = html

    return document.getElementById('collections-manager-container')
  }).then(node => {
    var form = document.getElementById('add-collection-form')

    node.addEventListener('click',e => {
      if(e.target.hasAttribute('data-collection-id')) {
        var removedId = e.target.dataset.collectionId
        if(window.confirm('Remove collection '+ collections[removedId].name+'?')) {
          _removeCollection(e.target.dataset.collectionId)
        }
      }
    })

    form.querySelector('input[name="add-collection"]').addEventListener('input',e => {
      e.target.classList.toggle('invalid',!e.target.checkValidity() && e.target.value !== '')
    })

    form.querySelector('input[name="add-collection"]').addEventListener('invalid',e => {
      e.target.classList.add('invalid')
    })

    form.addEventListener('submit',e => {
      _addCollection(form.querySelector('input[name="add-collection"]').value)

      e.preventDefault()
      e.stopPropagation()
      return false
    })

    return document.querySelector('#collection-manager-list tbody')
  }).then(node => {
    return Object.keys(collections).map(id => {
      return template.render('manager-section-item',{
        id: id,
        name: collections[id].name
      }).then(html => {
        var elem = document.createElement('tr')
        node.appendChild(elem)
        elem.outerHTML = html
        return document.getElementById(id+'-manager-collection')
      })
    })
  })

  var buttons

  Promise.all(
    [template.render('manager-subscription-popup-item', { id: '', label: '' })].concat(
    Object.keys(collections).map(k => {
      return template.render('manager-subscription-popup-item', { id: k, label: collections[k].name })
    }))
  ).then(valueArr => {
    buttons = valueArr.join('')
    return Promise.all(Object.keys(collections).map(k => {
      return template.render('manager-subscription-button', { id: k, label: collections[k].name, buttons: buttons }).then(html => {
        return { [collections[k].name]: html }
      })
    }))
  })
  .then(valueArr => {
    return template.render('manager-subscription-button', { id: '', label: '', buttons: buttons }).then(html => {
      valueArr.push({'(none)':html})
      return valueArr.reduce((all,curr) => Object.assign(all,curr),{})
    })
  })
  .then(htmls => {
    let query = '#subscription-manager-list tr.subscription-item td:first-of-type .subscription-title-wrap'
    Array.prototype.slice.call(document.querySelectorAll(query))
    .map(node => {
      var collection = subscriptions[node.closest('tr').dataset.channelExternalId].collection
      var elem = document.createElement('div')
      node.appendChild(elem)

      if(collection === null) {
        elem.outerHTML = htmls['(none)']
      } else {
        elem.outerHTML = htmls[collection.name]
      }
    })
  })
})
<commit_msg>Setting collection attribute for manage button.<commit_after>'use strict'

function _getSubscriptionContainer() {
  return document.getElementById('subscription-manager-container')
}

Promise.all([
  __collections,
  __subscriptions
]).then(valueArr => {
  var collections = valueArr[0]
  var subscriptions = valueArr[1]

  template.render('manager-section',{ quantity: Object.keys(collections).length }).then(html => {
    var elem = document.createElement('div')
    _getSubscriptionContainer().parentNode.insertBefore(elem,_getSubscriptionContainer())
    elem.outerHTML = html

    return document.getElementById('collections-manager-container')
  }).then(node => {
    var form = document.getElementById('add-collection-form')

    node.addEventListener('click',e => {
      if(e.target.hasAttribute('data-collection-id')) {
        var removedId = e.target.dataset.collectionId
        if(window.confirm('Remove collection '+ collections[removedId].name+'?')) {
          _removeCollection(e.target.dataset.collectionId)
        }
      }
    })

    form.querySelector('input[name="add-collection"]').addEventListener('input',e => {
      e.target.classList.toggle('invalid',!e.target.checkValidity() && e.target.value !== '')
    })

    form.querySelector('input[name="add-collection"]').addEventListener('invalid',e => {
      e.target.classList.add('invalid')
    })

    form.addEventListener('submit',e => {
      _addCollection(form.querySelector('input[name="add-collection"]').value)

      e.preventDefault()
      e.stopPropagation()
      return false
    })

    return document.querySelector('#collection-manager-list tbody')
  }).then(node => {
    return Object.keys(collections).map(id => {
      return template.render('manager-section-item',{
        id: id,
        name: collections[id].name
      }).then(html => {
        var elem = document.createElement('tr')
        node.appendChild(elem)
        elem.outerHTML = html
        return document.getElementById(id+'-manager-collection')
      })
    })
  })

  var buttons

  Promise.all(
    [template.render('manager-subscription-popup-item', { id: '', label: '' })].concat(
    Object.keys(collections).map(k => {
      return template.render('manager-subscription-popup-item', { id: k, label: collections[k].name })
    }))
  ).then(valueArr => {
    buttons = valueArr.join('')
    return Promise.all(Object.keys(collections).map(k => {
      return template.render('manager-subscription-button', { id: k, label: collections[k].name, buttons: buttons }).then(html => {
        return { [collections[k].name]: html }
      })
    }))
  })
  .then(valueArr => {
    return template.render('manager-subscription-button', { id: '', label: '', buttons: buttons }).then(html => {
      valueArr.push({'(none)':html})
      return valueArr.reduce((all,curr) => Object.assign(all,curr),{})
    })
  })
  .then(htmls => {
    let query = '#subscription-manager-list tr.subscription-item td:first-of-type .subscription-title-wrap'
    Array.prototype.slice.call(document.querySelectorAll(query))
    .map(node => {
      var channelId = node.closest('tr').dataset.channelExternalId
      var collection = subscriptions[channelId].collection
      var elem = document.createElement('div')
      node.appendChild(elem)

      if(collection === null) {
        elem.outerHTML = htmls['(none)']
      } else {
        elem.outerHTML = htmls[collection.name]
      }

      node.querySelector('.add-to-collection ul').setAttribute('data-subscription-id',channelId)
    })
  })
})<|endoftext|>


<commit_before>const mongoose = require('mongoose')
const Version = require('./common/Version.js')

const schema = new mongoose.Schema({
  id: {
    type: String,
    required: true
  },
  feedURL: {
    required: true,
    type: String
  },
  scheduleName: {
    required: true,
    type: String
  },
  date: {
    type: Date,
    default: Date.now
  },
  properties: {
    type: Map,
    of: String
  }
})

schema.add(Version)

schema.index({
  feedURL: 1,
  scheduleName: 1
})

exports.schema = schema
/** @type {import('mongoose').Model} */
exports.Model = null
<commit_msg>Adjust Article index to only be scheduleName<commit_after>const mongoose = require('mongoose')
const Version = require('./common/Version.js')

const schema = new mongoose.Schema({
  id: {
    type: String,
    required: true
  },
  feedURL: {
    required: true,
    type: String
  },
  scheduleName: {
    required: true,
    type: String
  },
  date: {
    type: Date,
    default: Date.now
  },
  properties: {
    type: Map,
    of: String
  }
})

schema.add(Version)

schema.index({
  scheduleName: 1
})

exports.schema = schema
/** @type {import('mongoose').Model} */
exports.Model = null
<|endoftext|>


<commit_before>require('isomorphic-fetch')

module.exports = class Fetcher {
  get(url, opts) {
    const options = opts || {}
    options.method = 'GET'
    return this.makeRequest(url, options)
  }

  makeRequest(url, opts) {
    const options = opts || {}
    if (typeof options.headers === 'undefined') {
      options.headers = {}
    }
    return new Promise((resolve, reject) => {
      fetch(url, options).then((response) => {
        this.handleJsonResponse(response, url, resolve, reject)
      })
    })
  }

  handleJsonResponse(response, url, resolve, reject) {
    response.json().then((json) => {
      if (response.ok) {
        resolve(json)
      } else {
        const jsonError = json
        jsonError.url = url
        jsonError.status = this.getStatus(response)
        reject(jsonError)
      }
    }).catch((error) => {
      console.error('failed to parse json response', error)
      reject({ error })
    })
  }

  getStatus(response) {
    return `${response.status} ${response.statusText}`
  }
}
<commit_msg>Reject promise when fetch has error<commit_after>require('isomorphic-fetch')

module.exports = class Fetcher {
  get(url, opts) {
    const options = opts || {}
    options.method = 'GET'
    return this.makeRequest(url, options)
  }

  makeRequest(url, opts) {
    const options = opts || {}
    if (typeof options.headers === 'undefined') {
      options.headers = {}
    }
    return new Promise((resolve, reject) => {
      fetch(url, options).then((response) => {
        this.handleJsonResponse(response, url, resolve, reject)
      }).catch(reject)
    })
  }

  handleJsonResponse(response, url, resolve, reject) {
    response.json().then((json) => {
      if (response.ok) {
        resolve(json)
      } else {
        const jsonError = json
        jsonError.url = url
        jsonError.status = this.getStatus(response)
        reject(jsonError)
      }
    }).catch((error) => {
      console.error('failed to parse JSON response', error)
      reject({ error })
    })
  }

  getStatus(response) {
    return `${response.status} ${response.statusText}`
  }
}
<|endoftext|>


<commit_before>// @flow
import type { Placement } from '../enums';
import type { ModifierArguments, Modifier, Padding } from '../types';
import getOppositePlacement from '../utils/getOppositePlacement';
import getVariationPlacement from '../utils/getVariationPlacement';
import getOppositeVariationPlacement from '../utils/getOppositeVariationPlacement';
import getBasePlacement from '../utils/getBasePlacement';
import mergePaddingObject from '../utils/mergePaddingObject';
import expandToHashMap from '../utils/expandToHashMap';
import { basePlacements, right, bottom, left, top, start } from '../enums';

type Options = {
  fallbackPlacements: Array<Placement>,
  padding: Padding,
  checkVariation: boolean,
};

export function flip({ state, options, name }: ModifierArguments<Options>) {
  const placement = state.placement;
  const defaultFallbackPlacements = [
    getOppositePlacement(state.options.placement),
  ];
  const {
    fallbackPlacements = defaultFallbackPlacements,
    padding = 0,
    checkVariation = true,
  } = options;

  const overflow = state.modifiersData['detectOverflow:flip'].overflowOffsets;
  const flipIndex = state.modifiersData[name].index;
  const paddingObject = mergePaddingObject(
    typeof padding !== 'number'
      ? padding
      : expandToHashMap(padding, basePlacements)
  );

  const placementOrder = [state.options.placement, ...fallbackPlacements];

  let flippedPlacement = placementOrder[flipIndex];

  if (!flippedPlacement && placement !== state.options.placement) {
    state.placement = state.options.placement;
    state.reset = true;
    return state;
  }

  if (!flippedPlacement && placement === state.options.placement) {
    return state;
  }

  const basePlacement = getBasePlacement(flippedPlacement);
  const fitsOnMainAxis =
    overflow[basePlacement] + paddingObject[basePlacement] <= 0;

  // Check alt axis to see if we can switch variation placements
  const variation = getVariationPlacement(flippedPlacement);
  if (variation && checkVariation && fitsOnMainAxis) {
    const isVertical = [top, bottom].includes(basePlacement);
    const isStartVariation = variation === start;
    const len = isVertical ? 'width' : 'height';

    const sideA = isStartVariation
      ? isVertical
        ? right
        : bottom
      : isVertical
      ? left
      : top;
    const sideB = isStartVariation
      ? isVertical
        ? left
        : top
      : isVertical
      ? right
      : bottom;

    const fitsA = overflow[sideA] + paddingObject[sideA] <= 0;
    const fitsB = overflow[sideB] + paddingObject[sideB] <= 0;

    const oppositeVariation = getOppositeVariationPlacement(flippedPlacement);
    const storedPlacement = state.modifiersData[`${name}#persistent`];

    const condition =
      state.measures.popper[len] < state.measures.reference[len]
        ? fitsA && !fitsB
        : !fitsA && fitsB;

    if (condition) {
      state.modifiersData[`${name}#persistent`] = oppositeVariation;
      flippedPlacement = oppositeVariation;
    } else if (storedPlacement) {
      state.modifiersData[`${name}#persistent`] = flippedPlacement;
      flippedPlacement = storedPlacement;
    }
  }

  if (!fitsOnMainAxis) {
    state.modifiersData[name].index += 1;
    state.reset = true;
    return state;
  } else if (fitsOnMainAxis && state.placement !== flippedPlacement) {
    state.placement = flippedPlacement;
    state.reset = true;
    return state;
  }

  return state;
}

export default ({
  name: 'flip',
  enabled: true,
  phase: 'main',
  fn: flip,
  requires: ['detectOverflow:flip'],
  optionallyRequires: ['offset'],
  data: { index: 0 },
}: Modifier<Options>);
<commit_msg>fix: variation flipping<commit_after>// @flow
import type { Placement } from '../enums';
import type { ModifierArguments, Modifier, Padding } from '../types';
import getOppositePlacement from '../utils/getOppositePlacement';
import getOppositeVariationPlacement from '../utils/getOppositeVariationPlacement';
import getVariationPlacement from '../utils/getVariationPlacement';
import getBasePlacement from '../utils/getBasePlacement';
import mergePaddingObject from '../utils/mergePaddingObject';
import expandToHashMap from '../utils/expandToHashMap';
import { basePlacements, right, bottom, left, top, start, end } from '../enums';

type Options = {
  fallbackPlacements: Array<Placement>,
  padding: Padding,
  checkVariation: boolean,
};

export function flip({ state, options, name }: ModifierArguments<Options>) {
  const {
    fallbackPlacements: preFallbackPlacements = [
      getOppositePlacement(state.options.placement),
    ],
    padding = 0,
    checkVariation = true,
  } = options;

  // If all placements don't fit, we want to use the originally specified one
  // in options as the last
  const fallbackPlacements = preFallbackPlacements.concat(
    state.options.placement
  );

  const overflow = state.modifiersData['detectOverflow:flip'].overflowOffsets;
  const flipIndex = state.modifiersData[name].index;
  const paddingObject = mergePaddingObject(
    typeof padding !== 'number'
      ? padding
      : expandToHashMap(padding, basePlacements)
  );

  const placementOrder = [state.options.placement, ...fallbackPlacements];

  let flippedPlacement = placementOrder[flipIndex];

  if (!flippedPlacement) {
    return state;
  }

  const basePlacement = getBasePlacement(flippedPlacement);
  const fitsOnMainAxis =
    overflow[basePlacement] + paddingObject[basePlacement] <= 0;

  // Check alt axis to see if we can switch variation placements
  const variation = getVariationPlacement(flippedPlacement);
  if (variation && checkVariation && fitsOnMainAxis) {
    const isVertical = [top, bottom].includes(basePlacement);
    const isStartVariation = variation === start;
    const len = isVertical ? 'width' : 'height';

    const sideA = isStartVariation
      ? isVertical
        ? right
        : bottom
      : isVertical
      ? left
      : top;
    const sideB = isStartVariation
      ? isVertical
        ? left
        : top
      : isVertical
      ? right
      : bottom;

    const fitsA = overflow[sideA] + paddingObject[sideA] <= 0;
    const fitsB = overflow[sideB] + paddingObject[sideB] <= 0;

    const oppositeVariation = variation === start ? end : start;
    // const oppositePlacement = getOppositeVariationPlacement(flippedPlacement);
    const storedVariation = state.modifiersData[`${name}#persistent`];

    const condition =
      state.measures.popper[len] < state.measures.reference[len]
        ? fitsA && !fitsB
        : !fitsA && fitsB;

    if (condition) {
      state.modifiersData[`${name}#persistent`] = oppositeVariation;
      flippedPlacement = `${basePlacement}-${oppositeVariation}`;
    } else if (storedVariation) {
      state.modifiersData[`${name}#persistent`] = variation;
      flippedPlacement = `${basePlacement}-${storedVariation}`;
    }
  }

  if (!fitsOnMainAxis) {
    state.modifiersData[name].index += 1;
    state.reset = true;
    return state;
  } else if (fitsOnMainAxis && state.placement !== flippedPlacement) {
    state.placement = flippedPlacement;
    state.reset = true;
    return state;
  }

  return state;
}

export default ({
  name: 'flip',
  enabled: true,
  phase: 'main',
  fn: flip,
  requires: ['detectOverflow:flip'],
  optionallyRequires: ['offset'],
  data: { index: 0 },
}: Modifier<Options>);
<|endoftext|>


<commit_before>let instanceofSymbol = Symbol('instanceofSymbol')

const TreeNodeMixin = base => {
    class TreeNode extends base {

        constructor(options) {
            super(options)
            this._parent = null // default to no parent.
            this._children = [];
        }

        /**
         * this._parent is protected (node's can access other node._parent).
         * The user should use the addChild methods, which automatically handles
         * setting a parent.
         *
         * @readonly
         */
        get parent() {
            return this._parent
        }

        /**
         * @readonly
         */
        get children() {
            // return a new array, so that the user modifying it doesn't affect
            // this node's actual children.
            return [...this._children]
        }

        /**
         * Add a child node to this TreeNode.
         *
         * @param {TreeNode} childNode The child node to add.
         */
        addChild (childNode) {

            if (! (childNode instanceof TreeNode))
                throw new Error('TreeNode.addChild expects the childNode argument to be a TreeNode instance.')

            // Do nothing if the child TreeNode is already added to this TreeNode.
            //
            // After adding a TreeNode to a parent using this imperative API, the
            // MotorHTMLNode ends up calling addChild on this TreeNode's parent a second time
            // in the element's attachedCallback, but the code stops at this line (which is
            // good).
            //
            // TODO TODO: prevent the second call altogether.
            if (childNode._parent === this) return

            if (childNode._parent)
                childNode._parent.removeChild(childNode)

            // Add parent
            childNode._parent = this;

            // Add to children array
            this._children.push(childNode);

            return this
        }

        /**
         * Add all the child nodes in the given array to this node.
         *
         * @param {Array.TreeNode} nodes The nodes to add.
         */
        addChildren(nodes) {
            nodes.forEach(node => this.addChild(node))
            return this
        }

        /**
         * Remove a child node from this node. Silently fails if the node doesn't
         * exist, etc.
         *
         * XXX Should this be silent? Or should we throw?
         *
         * @param {TreeNode} childNode The node to remove.
         */
        removeChild(childNode) {
            if (! (childNode instanceof TreeNode))
                throw new Error(`
                    TreeNode.removeChild expects the childNode argument to be an
                    instance of TreeNode. There should only be TreeNodes in the
                    tree.
                `)

            let thisHasChild = this._children.indexOf(childNode) >= 0
            if (! thisHasChild) return this

            childNode._parent = null
            this._children.splice(this._children.indexOf(childNode), 1);

            return this
        }

        /**
         * Remove all the child nodes in the given array from this node.
         *
         * @param {Array.TreeNode} nodes The nodes to remove.
         */
        removeChildren(nodes) {
            nodes.forEach(node => this.removeChild(node))
            return this
        }

        /**
         * @readonly
         * @return {number} How many children this TreeNode has.
         */
        get childCount() {
            return this._children.length
        }
    }

    Object.defineProperty(TreeNode, Symbol.hasInstance, {
        value: function(obj) {
            if (this !== TreeNode) return Object.getPrototypeOf(TreeNode)[Symbol.hasInstance].call(this, obj)

            let currentProto = obj

            while(currentProto) {
                let desc = Object.getOwnPropertyDescriptor(currentProto, "constructor")

                if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol))
                    return true

                currentProto = Object.getPrototypeOf(currentProto)
            }

            return false
        }
    })

    TreeNode[instanceofSymbol] = true

    return TreeNode
}

const TreeNode = TreeNodeMixin(class{})
TreeNode.mixin = TreeNodeMixin

export {TreeNode as default}
<commit_msg>removeAllChildren convenience method for TreeNode.<commit_after>let instanceofSymbol = Symbol('instanceofSymbol')

const TreeNodeMixin = base => {
    class TreeNode extends base {

        constructor(options) {
            super(options)
            this._parent = null // default to no parent.
            this._children = [];
        }

        /**
         * this._parent is protected (node's can access other node._parent).
         * The user should use the addChild methods, which automatically handles
         * setting a parent.
         *
         * @readonly
         */
        get parent() {
            return this._parent
        }

        /**
         * @readonly
         */
        get children() {
            // return a new array, so that the user modifying it doesn't affect
            // this node's actual children.
            return [...this._children]
        }

        /**
         * Add a child node to this TreeNode.
         *
         * @param {TreeNode} childNode The child node to add.
         */
        addChild (childNode) {

            if (! (childNode instanceof TreeNode))
                throw new Error('TreeNode.addChild expects the childNode argument to be a TreeNode instance.')

            // Do nothing if the child TreeNode is already added to this TreeNode.
            //
            // After adding a TreeNode to a parent using this imperative API, the
            // MotorHTMLNode ends up calling addChild on this TreeNode's parent a second time
            // in the element's attachedCallback, but the code stops at this line (which is
            // good).
            //
            // TODO TODO: prevent the second call altogether.
            if (childNode._parent === this) return

            if (childNode._parent)
                childNode._parent.removeChild(childNode)

            // Add parent
            childNode._parent = this;

            // Add to children array
            this._children.push(childNode);

            return this
        }

        /**
         * Add all the child nodes in the given array to this node.
         *
         * @param {Array.TreeNode} nodes The nodes to add.
         */
        addChildren(nodes) {
            nodes.forEach(node => this.addChild(node))
            return this
        }

        /**
         * Remove a child node from this node. Silently fails if the node doesn't
         * exist, etc.
         *
         * XXX Should this be silent? Or should we throw?
         *
         * @param {TreeNode} childNode The node to remove.
         */
        removeChild(childNode) {
            if (! (childNode instanceof TreeNode))
                throw new Error(`
                    TreeNode.removeChild expects the childNode argument to be an
                    instance of TreeNode. There should only be TreeNodes in the
                    tree.
                `)

            let thisHasChild = this._children.indexOf(childNode) >= 0
            if (! thisHasChild) return this

            childNode._parent = null
            this._children.splice(this._children.indexOf(childNode), 1);

            return this
        }

        /**
         * Remove all the child nodes in the given array from this node.
         *
         * @param {Array.TreeNode} nodes The nodes to remove.
         */
        removeChildren(nodes) {
            nodes.forEach(node => this.removeChild(node))
            return this
        }

        /**
         * Shortcut to remove all children.
         */
        removeAllChildren() {
            this.removeChildren(this._children)
            return this
        }

        /**
         * @readonly
         * @return {number} How many children this TreeNode has.
         */
        get childCount() {
            return this._children.length
        }
    }

    Object.defineProperty(TreeNode, Symbol.hasInstance, {
        value: function(obj) {
            if (this !== TreeNode) return Object.getPrototypeOf(TreeNode)[Symbol.hasInstance].call(this, obj)

            let currentProto = obj

            while(currentProto) {
                let desc = Object.getOwnPropertyDescriptor(currentProto, "constructor")

                if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol))
                    return true

                currentProto = Object.getPrototypeOf(currentProto)
            }

            return false
        }
    })

    TreeNode[instanceofSymbol] = true

    return TreeNode
}

const TreeNode = TreeNodeMixin(class{})
TreeNode.mixin = TreeNodeMixin

export {TreeNode as default}
<|endoftext|>


<commit_before>/*global define */

(function () {
    "use strict";

    console.log('DestWndSetupCtrl setup');
    var areWeInitialized = false;
    define([
        'angular'
    ], function (angular) {
        console.log('DestWndSetupCtrl define');

        var selfdict = {},
            scopeDict = {};
        selfdict.isInitialized = areWeInitialized = false;

        function DestWndSetupCtrl($scope, $modal, $rootScope) {
            console.log("in DestWndSetupCtrl");
            selfdict.scope = $scope;
            selfdict.isInitialized = areWeInitialized = false;
            scopeDict.rootScope = $rootScope;

            // selfdict.callbackFunction = null;
            $scope.showDestDialog = false;
            $scope.choiceCallback = null;
            $scope.destSelections = ["Same Window", "New Tab", "New Pop-up Window"];
            $scope.data = {
                dstSel : $scope.destSelections[0].slice(0),
                prevDstSel : $scope.destSelections[0].slice(0),
                whichDismiss : "Cancel",
                dlg2show : "SelectWndDlg",
                title : 'do we have a title?',
                icon : null,
                snippet : 'nothing in snippet',
                showDetail : '+'
            };

            $scope.preserveState = function () {
                console.log("preserveState");
                // $scope.data.whichDismiss = 'Cancel';
                $scope.data.prevDstSel = $scope.data.dstSel.slice(0);
                console.log("preserve " + $scope.data.prevDstSel + " from " + $scope.data.dstSel);
            };

            $scope.restoreState = function () {
                console.log("restoreState");
                // $scope.data.whichDismiss = 'Accept';
                console.log("restore " + $scope.data.dstSel + " from " + $scope.data.prevDstSel);
                $scope.data.dstSel = $scope.data.prevDstSel.slice(0);
            };

            $scope.showDialog = function (choiceCallback, args) {
                $scope.choiceCallback = choiceCallback;

                $scope.data.title = args.title;
                $scope.data.icon = args.icon;
                $scope.data.snippet = args.snippet;
                $scope.showDestDialog = true;
            };

            $scope.onAcceptDestination = function () {
                console.log("onAcceptDestination " + $scope.data.dstSel);
                var callback = $scope.choiceCallback;
                if ($scope.choiceCallback) {
                    $scope.choiceCallback = null;
                    callback($scope.data.dstSel);
                } else {
                    scopeDict.rootScope.$broadcast('DestinationSelectorEvent', { destWnd: $scope.data.dstSel });
                }
            };

            $scope.hitEnter = function (evt) {
                if (angular.equals(evt.keyCode, 13) && !(angular.equals($scope.name, null) || angular.equals($scope.name, ''))) {
                    $scope.save();
                }
            }; // end hitEnter

            $scope.safeApply = function (fn) {
                var phase = this.$root.$$phase;
                if (phase === '$apply' || phase === '$digest') {
                    if (fn && (typeof fn === 'function')) {
                        fn();
                    }
                } else {
                    this.$apply(fn);
                }
            };
        }

        DestWndSetupCtrl.prototype.isInitialized = function () {
            return areWeInitialized;
        };

        DestWndSetupCtrl.prototype.showDialog = function (choiceCallback) {

            /*global $scope */
            $scope.$scope.choiceCallback = choiceCallback;
            $scope.showDialog($scope.choiceCallback);
        };

        function init(App) {
            console.log('DestWndSetupCtrl init');

            selfdict.isInitialized = areWeInitialized = true;
            App.controller('DestWndSetupCtrl',  ['$scope', '$modal', '$rootScope', DestWndSetupCtrl]);

            App.directive("modalShowDest", function () {
                console.log("setting up directive modalShowDest");
                return {
                    restrict: "A",   /* why doesn't "E" work? */
                     /*
                    It will check the cache for inlined templates and if one is not found
                    it will make an xhr to the url to fetch the template.
                    The url with "/" prefix is relative to the domain, without the "/" prefix
                    it will be relative to the main ("index.html") page or
                    base url (if you use location in the html5 mode).
                    */

                    templateUrl : '/templates/DestSelectDlgGen',   // .jade will be appended
                    // template : '<ng-include src="getContentUrl()"/>',
                    replace : true,
                    transclude: true,
                    scope: {
                        modalVisible: "=",
                        modalMdata: "="
                    },

                    controller: function ($scope) {
                        console.log("setting up directive modalShowDest controller");
                        $scope.getContentUrl = function () {
                            console.log("find file modalShowDest html");
                            return '/templates/DestSelectDlgGen.html';
                        };

                        $scope.status = {
                            'detailsOpen' : false,
                            'destChoicesOpen' : false
                        };
                        /*
                        $scope.onShowDetailsClick = function () {
                            $scope.status.detailsOpen = !$scope.status.detailsOpen;
                            if ($scope.status.detailsOpen && $scope.status.destChoicesOpen) {
                                $scope.status.destChoicesOpen = false;
                            }
                            // $scope.status.destChoicesOpen = !$scope.status.destChoicesOpen;
                        }

                        $scope.onDestChoiceClick = function () {
                            $scope.status.destChoicesOpen = !$scope.status.destChoicesOpen;
                            if ($scope.status.destChoicesOpen && $scope.status.detailsOpen) {
                                $scope.status.detailsOpen = false;
                            }
                            // $scope.status.detailsOpen = !$scope.status.detailsOpen;
                        }
                        */
                    },

                    link: function (scope, element, attrs) {
                        var localScope = scope;
                        //Hide or show the modal
                        scope.showModal = function (visible, elem) {
                            if (!elem) {
                                elem = element;
                            }
                            scope.status.detailsOpen = scope.status.destChoicesOpen = false;
                            scope.$parent.safeApply(function () {
                                scope.status.detailsOpen = scope.status.destChoicesOpen = false;
                            });
                            if (visible) {
                                $(elem).modal311("show");
                            } else {
                                $(elem).modal311("hide");
                            }
                        };

                        scope.$on('ShowWindowSelectorModalEvent', function (event, args) {
                            var title = args.title,
                                icon = args.thumbnail,
                                snippet = args.snippet;
                            localScope.$parent.data.title = title;
                            localScope.$parent.data.icon = icon;
                            localScope.$parent.data.snippet = snippet;
                            localScope.$parent.safeApply(function () {
                                localScope.status.detailsOpen = localScope.status.destChoicesOpen = false;
                            });
                            // localScope.status.detailsOpen = localScope.status.destChoicesOpen = false;
                            console.log('local scope.$on received title : ' + title);

                            localScope.$parent.safeApply(function () {
                                localScope.$parent.showDestDialog = true;
                            });
                        });

                        //Check to see if the modal-visible attribute exists
                        if (!attrs.modalVisible) {
                            //The attribute isn't defined, show the modal by default
                            scope.showModal(true);
                        } else {
                            //Watch for changes to the modal-visible attribute
                            scope.$watch("modalVisible", function (newValue, oldValue) {
                                scope.showModal(newValue);
                                console.log("watch modalVisible  : ");
                                console.debug(scope.$parent.data);
                                // scope.$parent.preserveState();
                            });
                            //Watch for changes to the modal-mdata attribute
                            scope.$watch("modalMdata", function (newValue, oldValue) {
                                if (!angular.isUndefinedOrNull(newValue)) {
                                    localScope.$parent.data = newValue;
                                }
                                console.log("watch modalMdata scope.$parent data  : ");
                                console.debug(localScope.$parent.data);
                            });
                            //Watch for changes to the modal-mdata attribute
                            scope.$watch("data.dstSel", function (newValue, oldValue) {
                                if (!angular.isUndefinedOrNull(newValue)) {
                                    localScope.$parent.data.dstSel = newValue;
                                }
                                console.log("watch modalMdata scope.$parent data  : ");
                                console.debug(localScope.$parent.data);
                            });

                            scope.$watch('scope.$parent.showDestDialog', function (newValue, oldValue) {
                                console.log("scope.$watch newValue : " + newValue);
                                console.log("scope.$watch 'scope.$parent.showDestDialog' : " + scope.$parent.showDestDialog);
                                scope.showModal(newValue);
                                //attrs.modalVisible = false;
                            });

                        }
                        //Update the visible value when the dialog is closed through UI actions (Ok, cancel, etc.)
                        $(element).on('hidden.bs.modal', function () {
                            scope.modalVisible = localScope.$parent.showDestDialog = localScope.showDestDialog =false;
                            scope.status.detailsOpen = scope.status.destChoicesOpen = false;
                            scope.$parent.data.title = '';
                            scope.$parent.data.icon = null;
                            scope.$parent.data.snippet = '';
                            console.log("hide event called");
                            if (!scope.$$phase && !scope.$root.$$phase) {
                                scope.$apply();
                            }
                            scope.$apply();
                            console.log("hidden modalMdata : ");
                            console.debug(scope.$parent.data);
                            console.log("whichDismiss : " + scope.$parent.data.whichDismiss);
                            if (scope.$parent.data.whichDismiss === "Accept") {
                                scope.$parent.onAcceptDestination();
                            }
                        });
                    }

                };
            });

            return DestWndSetupCtrl;
        }

        return {
            start: init,
            showDialog : DestWndSetupCtrl.prototype.showDialog,
            isInitialized : DestWndSetupCtrl.prototype.isInitialized
        };

    });
}());
<commit_msg>Attempts to make safeApply avoid some exception due to gmsearch<commit_after>/*global define */

(function () {
    "use strict";

    console.log('DestWndSetupCtrl setup');
    var areWeInitialized = false;
    define([
        'angular'
    ], function (angular) {
        console.log('DestWndSetupCtrl define');

        var selfdict = {},
            scopeDict = {};
        selfdict.isInitialized = areWeInitialized = false;

        function DestWndSetupCtrl($scope, $modal, $rootScope) {
            console.log("in DestWndSetupCtrl");
            selfdict.scope = $scope;
            selfdict.isInitialized = areWeInitialized = false;
            scopeDict.rootScope = $rootScope;

            // selfdict.callbackFunction = null;
            $scope.showDestDialog = false;
            $scope.choiceCallback = null;
            $scope.destSelections = ["Same Window", "New Tab", "New Pop-up Window"];
            $scope.data = {
                dstSel : $scope.destSelections[0].slice(0),
                prevDstSel : $scope.destSelections[0].slice(0),
                whichDismiss : "Cancel",
                dlg2show : "SelectWndDlg",
                title : 'do we have a title?',
                icon : null,
                snippet : 'nothing in snippet',
                showDetail : '+'
            };

            $scope.preserveState = function () {
                console.log("preserveState");
                // $scope.data.whichDismiss = 'Cancel';
                $scope.data.prevDstSel = $scope.data.dstSel.slice(0);
                console.log("preserve " + $scope.data.prevDstSel + " from " + $scope.data.dstSel);
            };

            $scope.restoreState = function () {
                console.log("restoreState");
                // $scope.data.whichDismiss = 'Accept';
                console.log("restore " + $scope.data.dstSel + " from " + $scope.data.prevDstSel);
                $scope.data.dstSel = $scope.data.prevDstSel.slice(0);
            };

            $scope.showDialog = function (choiceCallback, args) {
                $scope.choiceCallback = choiceCallback;

                $scope.data.title = args.title;
                $scope.data.icon = args.icon;
                $scope.data.snippet = args.snippet;
                $scope.showDestDialog = true;
            };

            $scope.onAcceptDestination = function () {
                console.log("onAcceptDestination " + $scope.data.dstSel);
                var callback = $scope.choiceCallback;
                if ($scope.choiceCallback) {
                    $scope.choiceCallback = null;
                    callback($scope.data.dstSel);
                } else {
                    scopeDict.rootScope.$broadcast('DestinationSelectorEvent', { destWnd: $scope.data.dstSel });
                }
            };

            $scope.hitEnter = function (evt) {
                if (angular.equals(evt.keyCode, 13) && !(angular.equals($scope.name, null) || angular.equals($scope.name, ''))) {
                    $scope.save();
                }
            }; // end hitEnter

            $scope.safeApply = function (fn) {
                var phase = 'nophase';
                if(this.$root){
                    phase = this.$root.$$phase;
                }

                if (phase === '$apply' || phase === '$digest') {
                    if (fn && (typeof fn === 'function')) {
                        fn();
                    }
                } else {
                    this.$apply(fn);
                }
            };
        }

        DestWndSetupCtrl.prototype.isInitialized = function () {
            return areWeInitialized;
        };

        DestWndSetupCtrl.prototype.showDialog = function (choiceCallback) {

            /*global $scope */
            $scope.$scope.choiceCallback = choiceCallback;
            $scope.showDialog($scope.choiceCallback);
        };

        function init(App) {
            console.log('DestWndSetupCtrl init');

            selfdict.isInitialized = areWeInitialized = true;
            App.controller('DestWndSetupCtrl',  ['$scope', '$modal', '$rootScope', DestWndSetupCtrl]);

            App.directive("modalShowDest", function () {
                console.log("setting up directive modalShowDest");
                return {
                    restrict: "A",   /* why doesn't "E" work? */
                     /*
                    It will check the cache for inlined templates and if one is not found
                    it will make an xhr to the url to fetch the template.
                    The url with "/" prefix is relative to the domain, without the "/" prefix
                    it will be relative to the main ("index.html") page or
                    base url (if you use location in the html5 mode).
                    */

                    templateUrl : '/templates/DestSelectDlgGen',   // .jade will be appended
                    // template : '<ng-include src="getContentUrl()"/>',
                    replace : true,
                    transclude: true,
                    scope: {
                        modalVisible: "=",
                        modalMdata: "="
                    },

                    controller: function ($scope) {
                        console.log("setting up directive modalShowDest controller");
                        $scope.getContentUrl = function () {
                            console.log("find file modalShowDest html");
                            return '/templates/DestSelectDlgGen.html';
                        };

                        $scope.status = {
                            'detailsOpen' : false,
                            'destChoicesOpen' : false
                        };
                        /*
                        $scope.onShowDetailsClick = function () {
                            $scope.status.detailsOpen = !$scope.status.detailsOpen;
                            if ($scope.status.detailsOpen && $scope.status.destChoicesOpen) {
                                $scope.status.destChoicesOpen = false;
                            }
                            // $scope.status.destChoicesOpen = !$scope.status.destChoicesOpen;
                        }

                        $scope.onDestChoiceClick = function () {
                            $scope.status.destChoicesOpen = !$scope.status.destChoicesOpen;
                            if ($scope.status.destChoicesOpen && $scope.status.detailsOpen) {
                                $scope.status.detailsOpen = false;
                            }
                            // $scope.status.detailsOpen = !$scope.status.detailsOpen;
                        }
                        */
                    },

                    link: function (scope, element, attrs) {
                        var localScope = scope;
                        scope.$parent.showDestDialog = false;
                        //Hide or show the modal
                        scope.showModal = function (visible, elem) {
                            if (!elem) {
                                elem = element;
                            }
                            scope.status.detailsOpen = scope.status.destChoicesOpen = false;
                            // scope.$parent.safeApply(function () {
                            //     scope.status.detailsOpen = scope.status.destChoicesOpen = false;
                            // });
                            if (visible) {
                                $(elem).modal311("show");
                            } else {
                                $(elem).modal311("hide");
                            }
                        };

                        scope.$on('ShowWindowSelectorModalEvent', function (event, args) {
                            var title = args.title,
                                icon = args.thumbnail,
                                snippet = args.snippet;
                            localScope.$parent.data.title = title;
                            localScope.$parent.data.icon = icon;
                            localScope.$parent.data.snippet = snippet;
                            localScope.$parent.safeApply(function () {
                                localScope.status.detailsOpen = localScope.status.destChoicesOpen = false;
                            });
                            // localScope.status.detailsOpen = localScope.status.destChoicesOpen = false;
                            console.log('local scope.$on received title : ' + title);

                            localScope.$parent.safeApply(function () {
                                localScope.$parent.showDestDialog = true;
                            });
                        });

                        //Check to see if the modal-visible attribute exists
                        if (!attrs.modalVisible) {
                            //The attribute isn't defined, show the modal by default
                            scope.showModal(true);
                        } else {
                            //Watch for changes to the modal-visible attribute
                            scope.$watch("modalVisible", function (newValue, oldValue) {
                                scope.showModal(newValue);
                                console.log("watch modalVisible  : ");
                                console.debug(scope.$parent.data);
                                // scope.$parent.preserveState();
                            });
                            //Watch for changes to the modal-mdata attribute
                            scope.$watch("modalMdata", function (newValue, oldValue) {
                                if (!angular.isUndefinedOrNull(newValue)) {
                                    localScope.$parent.data = newValue;
                                }
                                console.log("watch modalMdata scope.$parent data  : ");
                                console.debug(localScope.$parent.data);
                            });
                            //Watch for changes to the modal-mdata attribute
                            scope.$watch("data.dstSel", function (newValue, oldValue) {
                                if (!angular.isUndefinedOrNull(newValue)) {
                                    localScope.$parent.data.dstSel = newValue;
                                }
                                console.log("watch modalMdata scope.$parent data  : ");
                                console.debug(localScope.$parent.data);
                            });

                            scope.$watch('scope.$parent.showDestDialog', function (newValue, oldValue) {
                                console.log("scope.$watch newValue : " + newValue);
                                console.log("scope.$watch 'scope.$parent.showDestDialog' : " + scope.$parent.showDestDialog);
                                scope.showModal(newValue);
                                //attrs.modalVisible = false;
                            });

                        }
                        //Update the visible value when the dialog is closed through UI actions (Ok, cancel, etc.)
                        $(element).on('hidden.bs.modal', function () {
                            scope.modalVisible = localScope.$parent.showDestDialog = localScope.showDestDialog =false;
                            scope.status.detailsOpen = scope.status.destChoicesOpen = false;
                            scope.$parent.data.title = '';
                            scope.$parent.data.icon = null;
                            scope.$parent.data.snippet = '';
                            console.log("hide event called");
                            if (!scope.$$phase && !scope.$root.$$phase) {
                                scope.$apply();
                            }
                            scope.$apply();
                            console.log("hidden modalMdata : ");
                            console.debug(scope.$parent.data);
                            console.log("whichDismiss : " + scope.$parent.data.whichDismiss);
                            if (scope.$parent.data.whichDismiss === "Accept") {
                                scope.$parent.onAcceptDestination();
                            }
                        });
                    }

                };
            });

            return DestWndSetupCtrl;
        }

        return {
            start: init,
            showDialog : DestWndSetupCtrl.prototype.showDialog,
            isInitialized : DestWndSetupCtrl.prototype.isInitialized
        };

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


<commit_before>module.exports = (RED) => {
  RED.nodes.registerType('mxd-new-assets', function NODE(config) {
    RED.nodes.createNode(this, config);
    const node = this;

    if (!config.interval) {
      node.error('config is missing');
    }
    node.log(`initialize mxd-new-assets node with an interval of ${config.interval} seconds`);

    const { AssetsQuery, heimdall } = RED.nodes.getNode(config.heimdall);

    let lastRunAssets = new Set();
    let timeoutHandle;

    async function RUN() {
      node.log('start check for new assets');

      const queries = [];

      if (['all', 'movies'].includes(config.content)) {
        node.log('include check for movies');
        const query = (new AssetsQuery())
          .filter('movies');
        queries.push(query);
      }

      if (['all', 'seasons'].includes(config.content)) {
        node.log('include check for seasons');
        const query = (new AssetsQuery())
          .filter('seasons');
        queries.push(query);
      }

      if (config.area !== 'all') {
        node.log(`restrict area to ${config.area}`);
        queries.forEach((query) => {
          query
            .filter({ package: 'hasPackageContent', store: 'availableWithoutPackage' }[config.area]);
        });
      }

      const requests = queries.map((query) => {
        query
          .filter('new')
          .filter('notUnlisted')
          .sort('activeLicenseStart', 'desc');
        return heimdall.getAssets(query);
      });
      let responses;
      try {
        responses = await Promise.all(requests);
      } catch (e) {
        node.warn(`requesting error, skip this run (${e.message})`);
        return;
      }

      const assets = responses.reduce((a, b) => a.concat(b));
      if (assets.length === 0) {
        node.warn('no assets in the responses');
        return;
      }

      let firstRun = false;
      if (!lastRunAssets.size) {
        node.log('first run');
        firstRun = true;
      }
      const currentRunAssets = new Set();
      const newAssets = new Set();
      assets.forEach((asset) => {
        if (!firstRun && !lastRunAssets.has(asset.id)) {
          node.log(`new asset (id: ${asset.id})`);
          newAssets.add(asset);
        }
        currentRunAssets.add(asset.id);
      });
      lastRunAssets = currentRunAssets;

      if (newAssets.size > 0) {
        node.log(`send ${newAssets.size} new assets`);
        node.send({ payload: Array.from(newAssets) });
      }

      node.log('end check for new assets');
      timeoutHandle = setTimeout(RUN, config.interval * 1000);
    }

    process.nextTick(RUN);

    node.on('close', () => {
      if (timeoutHandle) {
        clearTimeout(timeoutHandle);
      }
    });
  });
};
<commit_msg>Add node status about the current/last run<commit_after>module.exports = (RED) => {
  RED.nodes.registerType('mxd-new-assets', function NODE(config) {
    RED.nodes.createNode(this, config);
    const node = this;

    if (!config.interval) {
      node.error('config is missing');
    }
    node.log(`initialize mxd-new-assets node with an interval of ${config.interval} seconds`);

    const { AssetsQuery, heimdall } = RED.nodes.getNode(config.heimdall);

    let lastRunAssets = new Set();
    let timeoutHandle;

    async function RUN() {
      node.status({ fill: 'grey', shape: 'dot', text: `check for new assets...` });
      node.log('start check for new assets');

      const queries = [];

      if (['all', 'movies'].includes(config.content)) {
        node.log('include check for movies');
        const query = (new AssetsQuery())
          .filter('movies');
        queries.push(query);
      }

      if (['all', 'seasons'].includes(config.content)) {
        node.log('include check for seasons');
        const query = (new AssetsQuery())
          .filter('seasons');
        queries.push(query);
      }

      if (config.area !== 'all') {
        node.log(`restrict area to ${config.area}`);
        queries.forEach((query) => {
          query
            .filter({ package: 'hasPackageContent', store: 'availableWithoutPackage' }[config.area]);
        });
      }

      const requests = queries.map((query) => {
        query
          .filter('new')
          .filter('notUnlisted')
          .sort('activeLicenseStart', 'desc');
        return heimdall.getAssets(query);
      });
      let responses;
      try {
        responses = await Promise.all(requests);
      } catch (e) {
        node.status({ fill: 'red', shape: 'dot', text: 'requesting error' });
        node.warn(`requesting error, skip this run (${e.message})`);
        return;
      }

      const assets = responses.reduce((a, b) => a.concat(b));
      if (assets.length === 0) {
        node.warn('no assets in the responses');
        return;
      }

      let firstRun = false;
      if (!lastRunAssets.size) {
        node.log('first run');
        firstRun = true;
      }
      const currentRunAssets = new Set();
      const newAssets = new Set();
      assets.forEach((asset) => {
        if (!firstRun && !lastRunAssets.has(asset.id)) {
          node.log(`new asset (id: ${asset.id})`);
          newAssets.add(asset);
        }
        currentRunAssets.add(asset.id);
      });
      lastRunAssets = currentRunAssets;

      node.status({ fill: 'green', shape: 'dot', text: `sent ${newAssets.size} new assets` });
      if (newAssets.size > 0) {
        node.log(`send ${newAssets.size} new assets`);
        node.send({ payload: Array.from(newAssets) });
      }

      node.log('end check for new assets');
      timeoutHandle = setTimeout(RUN, config.interval * 1000);
    }

    process.nextTick(RUN);

    node.on('close', () => {
      if (timeoutHandle) {
        clearTimeout(timeoutHandle);
      }
    });
  });
};
<|endoftext|>