The Raphael function for ex. element.unclick(handler) (which actually calls eve.unbind(event, handler)) allows you remove handlers by event hanler, but how to unbind all event handlers
This might be one way to do this.
Raphael.el.unbindAll = function(){
while(this.events.length){
var e = this.events.pop();
e.unbind();
}
}
and then to unbind all events of an element
mypath.unbindAll();
I found solution with replacing the source of the raphael.js
replace in line 2031
R["un" + eventName] = elproto["un" + eventName] = function (fn) {
var events = this.events,
l = events.length;
while (l--) if (events[l].name == eventName && || events[l].f == fn) {
events[l].unbind();
events.splice(l, 1);
!events.length && delete this.events;
return this;
}
return this;
};
with this
R["un" + eventName] = elproto["un" + eventName] = function (fn) {
var events = this.events,
l = events.length;
while (l--) if (events[l].name == eventName && (!fn || events[l].f == fn) ) {
events[l].unbind();
events.splice(l, 1);
!events.length && delete this.events;
return this;
}
return this;
};
so currently if handler function is not provided to unbind functions it will unbind all handler functions for that event
usage example element.unclick(); (same for other events e.g. element.unmouseout())
Related
I use tinyMCE 5 in my web site to enter data stored in a database. Therefore I need to limit the entered size, including format information, to the size of the data field. How can I prohibit the user to enter more then the allowed number of bytes, say 2000?
Best of all if I could add some information like "42/2000" on the status bar.
We had a similar requirement in our project (difference: the output should be <entered_chars>/<chars_left> instead of <entered_chars>/<max_chars>), and it ended up being a custom plugin, based on the wordcount plugin. There is some hacks in there, which could make it fail whenever tinyMCE changes, since there is no API for the statusbar in version 5 at this point of time.
But maybe you will still find it useful:
(function () {
'use strict';
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
var maxChars = function (editor) {
return editor.getParam('number_max_chars', 3600);
};
var applyMaxChars = function (editor) {
return editor.getParam('restrict_to_max_chars', true);
};
var Settings = {
maxChars: maxChars,
applyMaxChars: applyMaxChars
};
var global$1 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
var getText = function (node, schema) {
var blockElements = schema.getBlockElements();
var shortEndedElements = schema.getShortEndedElements();
var isNewline = function (node) {
return blockElements[node.nodeName] || shortEndedElements[node.nodeName];
};
var textBlocks = [];
var txt = '';
var treeWalker = new global$1(node, node);
while (node = treeWalker.next()) {
if (node.nodeType === 3) {
txt += node.data;
} else if (isNewline(node) && txt.length) {
textBlocks.push(txt);
txt = '';
}
}
if (txt.length) {
textBlocks.push(txt);
}
return textBlocks;
};
var strLen = function (str) {
return str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length;
};
var countCharacters = function (node, schema) {
var text = getText(node, schema).join('');
return strLen(text);
};
var createBodyCounter = function (editor, count) {
return function () {
return count(editor.getBody(), editor.schema);
};
};
var createMaxCount = function (editor) {
return function () {
return Settings.maxChars(editor);
}
}
var createRestrictToMaxCount = function (editor) {
return function () {
return Settings.applyMaxChars(editor);
}
}
var get = function (editor) {
return {
getCount: createBodyCounter(editor, countCharacters),
getMaxCount: createMaxCount(editor),
getRestrictToMaxCount: createRestrictToMaxCount(editor)
};
};
var global$2 = tinymce.util.Tools.resolve('tinymce.util.Delay');
function isAllowedKeycode(event) {
// allow arrow keys, backspace and delete
const key = event.keyCode;
return key === 37 || key === 38 || key === 39 || key === 40 || key === 8
|| key === 46;
}
var updateCount = function (editor, api) {
editor.getContainer().getElementsByClassName(
'tox-statusbar__text-container')[0].textContent = String(
api.getCount()) + " / " + String(
Settings.maxChars(editor) - api.getCount());
};
var setup = function (editor, api, delay) {
var debouncedUpdate = global$2.debounce(function () {
return updateCount(editor, api);
}, delay);
editor.on('init', function () {
updateCount(editor, api);
global$2.setEditorTimeout(editor, function () {
editor.on('SetContent BeforeAddUndo Undo Redo keyup', debouncedUpdate);
editor.on('SetContent BeforeAddUndo Undo Redo keydown', function (e) {
if (!isAllowedKeycode(e) && Settings.applyMaxChars(editor) &&
api.getCount() >= Settings.maxChars(editor)) {
e.preventDefault();
e.stopPropagation();
}
});
}, 0);
});
};
function Plugin(delay) {
if (delay === void 0) {
delay = 300;
}
global.add('charactercount', function (editor) {
var api = get(editor);
setup(editor, api, delay);
return api;
});
}
Plugin();
}());
Currently I'm working on a preprocessor for the paste plugin, so that the max_length effects also pasted text. That's why you see the charactercount API in the code.
I have a requirement where I need to restrict my jstree node not to move outside its parent. It can freely move inside within its parent.
This is the code I am trying:
$('#divUC').jstree({
'core': {
multiple: false,
'check_callback': function (o, n, p, i, m) {
if (o == "move_node" && n.type == "view") {
}
Here shortcuts indicates:
o = operation,
n = node,
p = node_parent,
i = node_position,
m = more
You can check in callback for this.
if(operation =='move_node'){
if (this.get_node(node).parent === this.get_node(node_parent).id) {
return true;
} else {
return false;
}
}
Here's a simple example of how the Extends mutator can be used:
var firstClass = new Class({
aMethod: function(){
console.log('firstClass method');
}
});
var secondClass = new Class({
Extends:firstClass,
aMethod: function(){
this.parent();
console.log('secondClass method');
},
initialize: function(){
this.aMethod();
}
});
var instance = new secondClass();
//Output:
// firstClass method
// secondClass method
I would like to achieve the same behavior, but with class constructors like this:
var firstClass = new Class({
methods: {
aMethod: function(){
console.log('firstClass method');
}
}
});
var secondClass = new Class({
Extends:firstClass,
methods: {
aMethod: function(){
this.parent();
console.log('secondClass method');
}
},
initialize: function(){
this.methods.aMethod.call(this);
}
});
var instance = new secondClass();
//Output:
// Error: The method "initialize" has no parent.
instance.methods.aMethod.call(this);
//Output:
// TypeError: this.parent is not a function
My Mootools Fu is unfortunately not very strong, so I'm kind of at a loss. Is this possible with the way Extends currently works? If it's not then I'd like to work on implementing a solution to do this, however I'd need some pointers to get me started in the right direction (make a custom Mutator? modify the Core source? something else?). Any suggestions or ideas on how to proceed would be appreciated.
Update
I came up with a way to do this using pattern based mutators (Github). Mark's code needs to be included for the following to work:
(function(){
/* Utility functions */
Object.extend({
containsMethods:function(obj){
if(typeOf(obj) != 'object') return false;
return Object.getLength(Object.filter(obj, function(item){return typeOf(item) == 'function';})) > 0;
},
containsObjects:function(obj){
if(typeOf(obj) != 'object') return false;
return Object.getLength(Object.filter(obj, function(item){return typeOf(item) == 'object';})) > 0;
}
});
var getInheritance = function(obj, key){
var temp = {};
if(obj){
if(obj.inheritance !== undefined){
temp[key] = obj.inheritance;
}
delete obj.inheritance;
}
return temp;
}
/* Recursive function to turn specially keyed object literals in the constructor into class instances with inheritance */
var classify = function(obj, inherit, key, imap){
if(typeOf(obj) != 'object'){return obj;}
if(key == undefined){
key = '';
}
var inheritance = 'extends';
if(imap != null){
imap = Object.append(imap, getInheritance(obj, key));
imap = Object.append(imap, getInheritance(inherit, key));
if(imap[key] != undefined){
inheritance = imap[key];
}
}
var i = 'extends';
for(var k in inherit){
if(!inherit.hasOwnProperty(k)) {continue;}
i = 'extends';
if(imap != null){
if(typeOf(inherit[k]) == 'object'){
imap = Object.append(imap, getInheritance(inherit[k], k));
}
if(typeOf(obj[k]) == 'object'){
imap = Object.append(imap, getInheritance(obj[k], k));
}
if(imap[k] != undefined){
i = imap[k];
}
}
/* Needed to clean up properties when inheritance == false */
if(inherit[k] === undefined){
delete inherit[k];
}
if(typeOf(inherit[k]) == 'object' && i === false && !Object.keys(obj).contains(k)){
obj[k] = undefined;
continue;
}
if(obj[k] && typeOf(obj[k]) == 'object' && i !== false){
obj[k] = classify(obj[k], inherit[k], k, imap);
}
}
if(Object.containsMethods(obj)){
var constructor = {};
if(inherit != undefined && inheritance !== false){
if(inheritance == 'implements'){
constructor.Implements = (inherit.$constructor ? inherit.$constructor : new Class(inherit));
}else {
constructor.Extends = (inherit.$constructor ? inherit.$constructor : new Class(inherit));
}
}
obj = new (new Class(Object.append(constructor, obj)));
}else {
if(!Object.containsMethods(inherit)){
obj = Object.append({}, inherit, obj);
}
}
return obj;
};
/* Mutator */
Class.defineMutator(/^_(\w+)_/, function(params, key){
var old_key = key;
var key = key.replace(/_/g, "");
var c = null;
var imap = null;
if(this.$constructor){
imap = this.$constructor.imap = (this.$constructor.imap || {});
}
if(this.prototype[key] != undefined){
c = classify.call(this, params, this.prototype[key], '', imap);
}else {
c = classify.call(this, params, undefined, '', imap);
}
this.prototype[key] = c;
delete this[old_key];
});
})();
The mutator pattern is simply a key name surrounded with single underscores (ex: _methods_). The nested objects in the class constructor can contain any combination of properties and methods or other nested objects. Each individual object can have a property with the key 'inheritance' that takes the following values: 'implements', 'extends' or false. Implements and extends correspond to the behavior of the Implements and Extends Mootools class mutators. Extends is the default, and is the method used when nothing is supplied for the inheritance property. A value of false will keep the given object (and any objects nested within it) from being inherited altogether.
The entire code including examples can be found here
I'm using the following code to create tabbed panels and it's the only one that it's doing the job pretty good. I tried easytabs but it doesn't work properly with what I have.
So, here we go:
/*
* The following lines are for main tabbed panels
*/
$(function() {
$('.nav a').click(function() {
// save $(this) in a variable for efficiency
var $this = $(this);
// hide panels
$('.panel').hide();
$('.nav a.active').removeClass('active');
// add active state to new tab
$this.addClass('active').blur();
// retrieve href from link (is id of panel to display)
var panel = $this.attr('href');
// show panel
$(panel).fadeIn(250);
// don't follow link down page
return (false);
});
// end click
// open first tab
$('.nav li:first a').click();
});
// end function
I have found a plugin called BBQ, that uses Hashchange.
http://benalman.com/projects/jquery-bbq-plugin/
I'm getting crazy to understand how to use that with my code in order to be able to use the back button in the browser. Please help me.
Thank you in advance for helping.
Regards,
Deviad
I solved the problem this way.
/*
* jQuery hashchange event - v1.2 - 2/11/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($, i, b) {
var j, k = $.event.special, c = "location", d = "hashchange", l = "href", f = $.browser, g = document.documentMode, h = f.msie && (g === b || g < 8), e = "on" + d in i && !h;
function a(m) {
m = m || i[c][l];
return m.replace(/^[^#]*#?(.*)$/, "$1")
}
$[d + "Delay"] = 100;
k[d] = $.extend(k[d], {
setup : function() {
if (e) {
return false
}
$(j.start)
},
teardown : function() {
if (e) {
return false
}
$(j.stop)
}
});
j = (function() {
var m = {}, r, n, o, q;
function p() {
o = q = function(s) {
return s
};
if (h) {
n = $('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;
q = function() {
return a(n.document[c][l])
};
o = function(u, s) {
if (u !== s) {
var t = n.document;
t.open().close();
t[c].hash = "#" + u
}
};
o(a())
}
}
m.start = function() {
if (r) {
return
}
var t = a();
o || p();
(function s() {
var v = a(), u = q(t);
if (v !== t) {
o( t = v, u);
$(i).trigger(d)
} else {
if (u !== t) {
i[c][l] = i[c][l].replace(/#.*/, "") + "#" + u
}
}
r = setTimeout(s, $[d + "Delay"])
})()
};
m.stop = function() {
if (!n) {
r && clearTimeout(r);
r = 0
}
};
return m
})()
})(jQuery, this);
/*
*
* Tabbed Browsing with back button support. Requires Hashchange plugin.
*
*/
$(function () {
$(".nav a").live("click", function(e) {
updateTabs($($(this).attr("href")));
});
//Grab hash off URL (default to first tab) and update
$(window).bind("hashchange", function(e) {
var anchor = $(location.hash);
if (anchor.length === 0) {
anchor = $(".panel div:eq(0)");
}
updateTabs(anchor);
});
//Pass in the tab and show appropriate contents
function updateTabs(tab) {
$(".nav a.active")
.removeClass("active")
.filter(function(index) {
return $(this).attr("href") === '#' + tab.attr("id");
}).addClass("active");
$(".panel").hide();
tab.show();
}
//Fire the hashchange event when the page first loads
$(window).trigger('hashchange');
$('.nav li:first a').click();
});
});
When implementing SuperBoxSelect (http://www.sencha.com/forum/showthread.php?69307-3.x-Ext.ux.form.SuperBoxSelect), I've realized that it currently does not support shift + click selection of multiple items. Has anyone been able to implement this functionality or found a similar plugin that offers this functionality?
beforeadditem:function(self, recordValue) {
var start = 0;
var end = 0;
var record = this.findRecord(this.valueField, recordValue);
var recordIndex = this.store.indexOf(record);
if(window.event.shiftKey) {
this.multiSelectMode = true;
if(this.firstChoiceIndex == undefined) {
this.firstChoiceIndex = recordIndex;
this.view.all.item(recordIndex).addClass('x-combo-selected-shift');
return false;
} else {
this.secondChoiceIndex = recordIndex;
if(this.firstChoiceIndex > this.secondChoiceIndex) {
start = this.secondChoiceIndex;
end = this.firstChoiceIndex;
} else if(this.secondChoiceIndex > this.firstChoiceIndex) {
start = this.firstChoiceIndex;
end = this.secondChoiceIndex;
}
var selectedRecords = this.store.getRange(start, end);
Ext.each(selectedRecords, function(item, index, allitems) {
self.addRecord(item)
});
this.firstChoiceIndex = undefined;
this.secondChoiceIndex = undefined;
return false;
}
} else {
this.firstChoiceIndex = undefined;
this.secondChoiceIndex = undefined;
return true;
}
}
Add that listener and it works. The x-combo-selected-shift class is identical to the x-combo-selected class. It's just named different so the highlighting sticks to the item you shift+clicked on after you mouse out.