I am using Alertify js 1.6.1 to show dialog box when user leaves a page. Apart from Ok and Cancel, I need to add one extra button "continue" in alertify js confirm dialog box. Is there a way to add custom button functionality? Let me know if you have any ideas on it. Thanks
You can build your own or extend the existing confirm:
alertify.dialog('myConfirm', function() {
var settings;
return {
setup: function() {
var settings = alertify.confirm().settings;
for (var prop in settings)
this.settings[prop] = settings[prop];
var setup = alertify.confirm().setup();
setup.buttons.push({
text: '<u>C</u>ontinue',
key: 67 /*c*/ ,
scope: 'auxiliary',
});
return setup;
},
settings: {
oncontinue: null
},
callback: function(closeEvent) {
if (closeEvent.index == 2) {
if (typeof this.get('oncontinue') === 'function') {
returnValue = this.get('oncontinue').call(this, closeEvent);
if (typeof returnValue !== 'undefined') {
closeEvent.cancel = !returnValue;
}
}
} else {
alertify.confirm().callback.call(this, closeEvent);
}
}
};
}, false, 'confirm');
see example
Related
I have 2 dialogs (A and B) with closeOnEscape="true".
Both dialogs are modal and have <p:focus context="innerForm" /> inside.
The dialog A opens the dialog B. Yeah, I know, this is a bad design, but...
The problem is that when I press ESC on dialog B it closes correctly and focus return to dialog A, but ESC do not close this dialog.
This is a bug and has been reported to PrimeFaces on GitHub:
https://github.com/primefaces/primefaces/issues/6677
PR: https://github.com/primefaces/primefaces/pull/6678
Will be in PF 9.0
Add this to your JS that loads after PF to fix it right now:
if (PrimeFaces.widget.Dialog) {
PrimeFaces.widget.Dialog.prototype.bindEvents = function() {
var $this = this;
//Move dialog to top if target is not a trigger for a PrimeFaces overlay
this.jq.on("mousedown", function(e) {
if (!$(e.target).data('primefaces-overlay-target')) {
$this.moveToTop();
}
});
this.icons.on('mouseover', function() {
$(this).addClass('ui-state-hover');
}).on('mouseout', function() {
$(this).removeClass('ui-state-hover');
}).on('focus', function() {
$(this).addClass('ui-state-focus');
}).on('blur', function() {
$(this).removeClass('ui-state-focus');
});
this.closeIcon.on('click', function(e) {
$this.hide();
e.preventDefault();
});
this.maximizeIcon.on("click", function(e) {
$this.toggleMaximize();
e.preventDefault();
});
this.minimizeIcon.on("click", function(e) {
$this.toggleMinimize();
e.preventDefault();
});
if (this.cfg.closeOnEscape) {
$(document).on('keydown.dialog_' + this.id, function(e) {
var keyCode = $.ui.keyCode;
if (e.which === keyCode.ESCAPE && $this.isVisible()) {
var active = parseInt($this.jq.css('z-index')) === parseInt($('.ui-dialog:visible').last().css('z-index'));
if (active) {
$this.hide();
}
};
});
}
};
}
I have a content script in a Chrome Extension that's passing messages. Every so often, when the content script calls
chrome.runtime.sendMessage({
message: 'hello',
});
it throws an error:
Uncaught Error: Extension context invalidated.
What does this error mean? I couldn't find any documentation on it.
It doesn't happen consistently. In fact, it's hard to reproduce. Seems to happen if I just leave the page open for a while in the background.
Another clue: I've written many Chrome Extensions with content scripts that pass messages and I haven't seen this error before. The main difference is that this content script is injected by the background page using
chrome.tabs.executeScript({
file: 'contentScript.js',
});
Does using executeScript instead of the manifest file somehow change the lifecycle of the content script?
This is certainly related to the message listener being lost in the middle of the connection between content and background scripts.
I've been using this approach in my extensions, so that I have a single module that I can use in both background and content scripts.
messenger.js
const context = (typeof browser.runtime.getBackgroundPage !== 'function') ? 'content' : 'background'
chrome.runtime.onConnect.addListener(function (port) {
port.onMessage.addListener(function (request) {
try {
const object = window.myGlobalModule[request.class]
object[request.action].apply(module, request.data)
} catch () {
console.error(error)
}
})
})
export function postMessage (request) {
if (context === 'content') {
const port = chrome.runtime.connect()
port.postMessage(request)
}
if (context === 'background') {
if (request.allTabs) {
chrome.tabs.query({}, (tabs) => {
for (let i = 0; i < tabs.length; ++i) {
const port = chrome.tabs.connect(tabs[i].id)
port.postMessage(request)
}
})
} else if (request.tabId) {
const port = chrome.tabs.connect(request.tabId)
port.postMessage(request)
} else if (request.tabDomain) {
const url = `*://*.${request.tabDomain}/*`
chrome.tabs.query({ url }, (tabs) => {
tabs.forEach((tab) => {
const port = chrome.tabs.connect(tab.id)
port.postMessage(request)
})
})
} else {
query({ active: true, currentWindow: true }, (tabs) => {
const port = chrome.tabs.connect(tabs[0].id)
port.postMessage(request)
})
}
}
}
export default { postMessage }
Now you'll just need to import this module in both content and background script. If you want to send a message, just do:
messenger.postMessage({
class: 'someClassInMyGlobalModuçe',
action: 'someMethodOfThatClass',
data: [] // any data type you want to send
})
You can specify if you want to send to allTabs: true, a specific domain tabDomain: 'google.com' or a single tab tabId: 12.
I have created a custom rating widget in openerp using Rateit.
But the widget is always editable, How can i make it editable only when i click 'Edit' button and How do i know it is in readonly mode?
xml
<field name="rating" widget="rating"/>
js
instance.my_module.Rating = instance.web.form.FieldChar.extend({
template : "rating",
init: function(field_manager, node){
this._super.apply(this, arguments);
},
start: function() {
var self = this;
$('#rateit').rateit({
value: 0,
resetable: false
});
},
});
Finally i got it working, here is my code
start: function() {
var self = this;
this.field_manager.has_been_loaded.done(function() {
$('#rateit').rateit({
value: 0,
resetable: false
});
self.field_manager.on("change:actual_mode", self, self.check_actual_mode);
self.check_actual_mode();
});
},
check_actual_mode: function(source, options) {
var self = this;
if(self.field_manager.get("actual_mode")=='view'){
$('#rateit').rateit('readonly',true);
}
else {
$('#rateit').rateit('readonly',false);
}
}
Presently I have set time interval in such a way that every 1 seconds,a function is executed.The problem is that,i am displaying notification through this function.There are notification buttons in notification.When I click on the notification action button,mulitple windows are being open.I found out that it is because I have set Timer.But in my extension , timer is necessary in order to check the output of a server file everytime.Anyone please help me.Is there any other way to deal this problem
Here is my background.js
var myNotificationID = null;
var oldChromeVersion = !chrome.runtime;
setInterval(function() {
updateIcon();
}, 1000);
function onInit() {
updateIcon();
if (!oldChromeVersion) {
chrome.alarms.create('watchdog',{periodInMinutes:5,delayInMinutes: 0});
}
}
function onAlarm(alarm) {
if (alarm && alarm.name == 'watchdog') {
onWatchdog();
}
else {
updateIcon();
}
}
function onWatchdog() {
chrome.alarms.get('refresh', function(alarm) {
if (alarm) {
console.log('Refresh alarm exists. Yay.');
}
else {
updateIcon();
}
});
}
if (oldChromeVersion) {
updateIcon();
onInit();
}
else {
chrome.runtime.onInstalled.addListener(onInit);
chrome.alarms.onAlarm.addListener(onAlarm);
}
function updateIcon(){
if(//something)
//something
else{
chrome.notifications.create(
'id1',{
type: 'basic',
iconUrl: '/calpine_not_logged_in.png',
title: 'Warning : Attendance',
message: 'Please mark your Attendance !',
buttons: [{ title: 'Mark',
iconUrl: '/tick.jpg'
},{ title: 'Ignore',
iconUrl: '/cross.jpg'}],
priority: 0},
function(id) { myNotificationID = id;}
);
chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
if (notifId === myNotificationID) {
if (btnIdx === 0) {
window.open("http://www.calpinemate.com/");
} else if (btnIdx === 1) {
notification.close();
}
}
});
chrome.notifications.onClosed.addListener(function() {
notification.close();
});
}
} }
});
}
}
onInit();
Here i found out that,when i remove the delayInminutes and laso the set timeineterval it opens only one tab,as i want.But both of them are necessary to do continuous checking of a server file.because whole my operation is based on the server file output.Is there any way to cope with this problem.Is there any way to set time interval for only that function?
The problem has nothing to do with what you "suspect". The problem is that you add a listener for chrome.notifications.onButtonClicked events inside the updateIcon() function. So this is what happens:
Every second you execute updateIcon().
Inside updateIcon() you set a new listener that listens for notification-buttons being clicked.
So, after 1 second there will be 1 listener, after 2 seconds there will be 2 listeners, after n seconds there will be n listeners.
When you click the button, each listener will catch the onButtonClicked event and open a new window. (So there will be so many windows as many seconds have elapsed since you loaded your extension.
How to fix this:
You need to create the listener only once (and not every second). To remove the following piece of code from inside the updateIcon() function:
chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
if (notifId === myNotificationID) {
if (btnIdx === 0) {
window.open("http://www.calpinemate.com/");
} else if (btnIdx === 1) {
notification.close();
}
}
});
And place it at the end of your background-page (just before onInit();). Make sure you don't place it inside any function.
I'm new to backbone.js and express and I have been adapting Christophe Coenraets Wine Cellar REST API example application for my own project.
I am building a form that has several menus needing to be populated from multiple unrelated collections in mongodb.
I am able to populate one menu with one collection, but I have no idea how to get more than one collection to my form View.
Here are the files I am using to populate one menu. How do I expand this to populate two menus?
I suppose I could make a new View for every menu I want to populate - but that seems like overkill.
Can I combine two mongodb find() collections into one object, and list them separately on a page? If so, how?
thanks in advance!
/routes/modules.js contains:
exports.findAllModules = function(req, res) {
db.collection('modules', function(err, collection) {
collection.find().toArray(function(err, items) {
res.send(items);
});
});
};
/server.js contains:
app.get('/modules', module.findAllModules);
/public/js/main.js contains:
routes: {
"modules" : "list" }
...
list: function(page) {
var p = page ? parseInt(page, 10) : 1;
var moduleList = new ModuleCollection();
moduleList.fetch({success: function(){
console.log('in list function');
$("#content").html(new ModuleListView({model: moduleList, page: p}).el);
}});
this.headerView.selectMenuItem('home-menu');
},
...
utils.loadTemplate([
'ModuleListItemView' ], function() {
app = new AppRouter();
Backbone.history.start(); });
/public/models/models.js contains:
window.Module = Backbone.Model.extend({
urlRoot: "/modules",
idAttribute: "_id",
initialize: function () {
this.validators = {};
this.validators.name = function (value) {
return value.length > 0 ? {isValid: true} : {isValid: false, message: "You must enter a name"};
};
validateItem: function (key) {
return (this.validators[key]) ? this.validators[key](this.get(key)) : {isValid: true};
},
validateAll: function () {
var messages = {};
for (var key in this.validators) {
if(this.validators.hasOwnProperty(key)) {
var check = this.validators[key](this.get(key));
if (check.isValid === false) {
messages[key] = check.message;
}
}
}
return _.size(messages) > 0 ? {isValid: false, messages: messages} : {isValid: true};
},
defaults: {
_id: null,
name: ""
} });
window.ModuleCollection = Backbone.Collection.extend({
model: Module,
url: "/modules"
});
/public/js/views/modulelist.js contains:
window.ModuleListView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
var modules = this.model.models;
$(this.el).html('<ul class="thumbnails"></ul>');
for (var i = 0; i < modules.length; i++) {
$('.thumbnails', this.el).append(new ModuleListItemView({model: modules[i]}).render().el);
}
return this;
} });
window.ModuleListItemView = Backbone.View.extend({
tagName: "li",
initialize: function () {
this.model.bind("change", this.render, this);
this.model.bind("destroy", this.close, this);
},
render: function () {
$(this.el).html(this.template(this.model.toJSON()));
return this;
} });
/public/tpl/ModuleListView.html contains:
Not entirely sure how your code works, but here are a few backbone tips.
If you wanna build a menu from a collection don't pass the collection as a model.
Instead of:
$("#content").html(new ModuleListView({model: moduleList, page: p}).el);
Use:
$("#content").empty().append(new ModuleListView({collection: moduleList, page: p}).el);
Instead of:
render: function () {
var modules = this.model.models;
$(this.el).html('<ul class="thumbnails"></ul>');
for (var i = 0; i < modules.length; i++) {
$('.thumbnails', this.el).append(new ModuleListItemView({model: modules[i]}).render().el);
}
return this;
}
Use:
render: function () {
this.$el.html('<ul class="thumbnails">');
this.collection.each(function(model) {
this.$('.thumbnails').append(new ModuleListItemView({model: model}).render().el);
}, this);
return this;
}
If you have no need in updating or deleting your models, it's enough to add the url path /modules only to the collection, for reading the initial modules.