Callback function on every set event on the webpage - javascript

For instance, I have an animation, I play it with setInterval. When an event that I previously set on the webpage happens I want a callback function to be fired to call clearInterval. Curious if there is any way to setup such callback function for all events existing in the webpage. Because otherwise I will have to go over every event I set previously. In form of code I am interested in something like that:
const i = setInterval(function() {
//do something
}, 50);
AdditionalCallbackForEveryEventSet(function() {
clearInterval(i);
});

I had a similar experience, but wanted to clean up any listeners when a custom component was removed from the DOM (hence, me using .call()). It might be a bit too complex for you, but what I want to point out is that you need to build a functionality that you need to use instead of setting the interval/listeners with setInterval/addEventListener directly.
Some Examples
Add listener
FXListenerBehavior.setListener.call(window, 'aKeyToDiffentiate', functionName);
Remove listener
FXListenerBehavior.clearListener.call(window, 'aKeyToDiffentiate', functionName);
Add interval
const TIME_IN_MILLIS = 2000;
FXListenerBehavior.setInterval.call(window, 'aKeyToDiffentiate', functionName, TIME_IN_MILLIS);
Remove all intervals and listeners
FXListenerBehavior.clearAllIntervals.call(window);
Print all listeners and intervals
Nice when debugging.
FXListenerBehavior.printAll.call(window);
Remove all listeners and intervals
FXListenerBehavior.clearAll.call(window);
FXListenerBehavior = {
/* === SET AND UPDATE === */
/**
* #description sets a listener
* #param {String} name any descriptive name
* #param {function} method the function to be run with the listener
* #param {setOnElement} Boolean if the listener should be set on the element or default to window
* #return {Boolean} true if set, false if listener already exist
*/
setListener: function(name, method, setOnElement) {
if (FXListenerBehavior._alreadyExist.call(this, name)) {
var elementName = FXListenerBehavior._getElementName.call(this);
console.warn(elementName + '\"' + name + '\" already exist. Use updateListener() if you\'re unsure if the listener exists');
return false;
}
return FXListenerBehavior._setListener.call(this, name, method, setOnElement);
},
/**
* #description sets an interval
* #param {String} name any descriptive name
* #param {function} method the method to be repeated
* #param {Integer} time repeats method after this many milliseconds
* #return {Boolean} true if set, false if listener already exist
*/
setInterval: function(name, method, time) {
if (FXListenerBehavior._alreadyExist.call(this, name)) {
var elementName = FXListenerBehavior._getElementName.call(this);
console.warn(elementName + '\"' + name + '\" already exist. Use updateInterval() if you\'re unsure if the interval exists');
return false;
}
return FXListenerBehavior._setInterval.call(this, name, method, time);
},
/**
* #description replaces a listener or interval, or creates one if it doesn't exist
* #param {String} name any descriptive name
* #param {function} method the method to be run
* #param {Integer} time repeats method after this many milliseconds (only for intervals)
* #return {Boolean} always true
*/
update: function(name, method, time) {
if (time > 0) {
return FXListenerBehavior.updateInterval.call(this, name, method, time);
} else {
var setOnElement = time;
return FXListenerBehavior.updateListener.call(this, name, method, setOnElement);
}
},
updateListener: function(name, method, setOnElement) {
FXListenerBehavior.clearListener.call(this, name);
return FXListenerBehavior._setListener.call(this, name, method, setOnElement);
},
updateInterval: function(name, method, time) {
FXListenerBehavior.clearInterval.call(this, name);
return FXListenerBehavior._setInterval.call(this, name, method, time);
},
_setListener: function(name, method, setOnElement) {
if (name == undefined || method == undefined) {
traceDebug('name or method is undefined', name, method)
return
}
this._listenersAndIntervals[name] = method;
if (setOnElement) {
this.addEventListener(name, method, {
'passive': true
});
} else {
window.addEventListener(name, method, {
'passive': true
});
}
return true;
},
_setInterval: function(name, method, time) {
if (name == undefined || method == undefined) {
traceDebug('name or method is undefined', name, method)
return
}
var intervalId = setInterval(method, time);
this._listenersAndIntervals[name] = intervalId;
return true;
},
_alreadyExist: function(name) {
if (typeof this._listenersAndIntervals === 'undefined') {
this._listenersAndIntervals = {};
}
if (this._listenersAndIntervals[name]) {
return true;
}
return false;
},
/* === GET AND PRINT === */
getAll: function() {
return Object.keys(this._listenersAndIntervals);
},
printAll: function() {
if (this._listenersAndIntervals) {
var isPolymer2Element = this.is == 'function';
let elementName = (isPolymer2Element) ? this.is() : this.is;
console.info("All listeners and intervals for " + elementName + ":\n" + JSON.stringify(Object.keys(this._listenersAndIntervals)));
}
},
/* === CLEAR === */
/**
* #description clears a listener
* #param {String} name any descriptive name, previously set with setListener
*/
clearListener: function(name) {
if (FXListenerBehavior._alreadyExist.call(this, name)) {
window.removeEventListener(name, this._listenersAndIntervals[name])
delete this._listenersAndIntervals[name];
}
},
/**
* #description clears an interval
* #param {String} name any descriptive name, previously set with setInterval
*/
clearInterval: function(name) {
if (FXListenerBehavior._alreadyExist.call(this, name)) {
clearInterval(this._listenersAndIntervals[name]);
delete this._listenersAndIntervals[name];
}
},
clearAllListeners: function() {
FXListenerBehavior._clear.call(this, 'listener');
},
clearAllIntervals: function() {
FXListenerBehavior._clear.call(this, 'interval');
},
clearAll: function(print) {
if (print) {
FXListenerBehavior.printAll.call(this)
}
FXListenerBehavior._clear.call(this);
if (print) {
FXListenerBehavior.printAll.call(this)
}
},
_clear: function(listenerOrInterval) {
if (typeof this._listenersAndIntervals === 'object') {
var keys = Object.keys(this._listenersAndIntervals);
for (var i = keys.length - 1; i >= 0; i--) {
if (typeof this._listenersAndIntervals[keys[i]] === 'function' && listenerOrInterval !== 'interval') {
FXListenerBehavior.clearListener.call(this, keys[i]);
} else if (typeof this._listenersAndIntervals[keys[i]] === 'number' && listenerOrInterval !== 'listener') {
FXListenerBehavior.clearInterval.call(this, keys[i]);
} else {
var elementName = FXListenerBehavior._getElementName.call(this);
console.warn(elementName + keys[i] + ' is not a ' + (listenerOrInterval || 'listener'));
}
}
}
},
_getElementName: function() {
var elementName = this.is ||  this.tagName || this.nodeName;
if (!elementName) {
traceDebug('FXListenerBehavior lacks element information');
}
return (this) ? elementName + ': ' : '';
}
}

Related

Uncaught TypeError: Cannot read property 'documentElement' of null at Iframe.initializeIframe [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
after upgrade Chrome browser to v. 91.0.4472.106, console show this error:
Uncaught TypeError: Cannot read property 'documentElement' of null
at Iframe.initializeIframe (Iframe.js?bust=f74493421b3bb4c9f2ea18198ca25746b5ef8a20:202)
this is content of Iframe.js:
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Iframe
* The editor iframe
*/
define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/Walker',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/TYPO3',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/KeyMap'],
function (UserAgent, Walker, Typo3, Util, Dom, Event, KeyMap) {
/**
* Editor iframe constructor
*
* #param {Object} config
* #constructor
* #exports TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Iframe
*/
var Iframe = function (config) {
Util.apply(this, config);
};
Iframe.prototype = {
/**
* Render the iframe (called by framework rendering)
*
* #param object container: the container into which to insert the iframe (that is the framework)
* #return void
*/
render: function (container) {
this.config = this.getEditor().config;
this.createIframe(container);
if (!this.config.showStatusBar) {
Dom.addClass(this.getEl(), 'noStatusBar');
}
this.initStyleChangeEventListener();
if (UserAgent.isOpera) {
var self = this;
Event.one(this.getEl(), 'load', function (event) { self.initializeIframe(); return true; })
} else {
this.initializeIframe();
}
},
/**
* Get the element to which the iframe is rendered
*/
getEl: function () {
return this.el;
},
/**
* The editor iframe may become hidden with style.display = "none" on some parent div
* This breaks the editor in Firefox: the designMode attribute needs to be reset after the style.display of the container div is reset to "block"
* In all browsers, it breaks the evaluation of the framework dimensions
*/
initStyleChangeEventListener: function () {
if (this.isNested) {
if (typeof MutationObserver === 'function') {
var self = this;
this.mutationObserver = new MutationObserver( function (mutations) { self.onNestedShowMutation(mutations); });
var options = {
attributes: true,
attributeFilter: ['class', 'style']
};
for (var i = this.nestedParentElements.sorted.length; --i >= 0;) {
var nestedElement = document.getElementById(this.nestedParentElements.sorted[i]);
this.mutationObserver.observe(nestedElement, options);
this.mutationObserver.observe(nestedElement.parentNode, options);
}
} else {
this.initMutationEventsListeners();
}
}
},
/**
* When Mutation Observer is not available, listen to DOMAttrModified events
*/
initMutationEventsListeners: function () {
var self = this;
var options = {
delay: 50
};
for (var i = this.nestedParentElements.sorted.length; --i >= 0;) {
var nestedElement = document.getElementById(this.nestedParentElements.sorted[i]);
Event.on(
nestedElement,
'DOMAttrModified',
function (event) { return self.onNestedShow(event); },
options
);
Event.on(
nestedElement.parentNode,
'DOMAttrModified',
function (event) { return self.onNestedShow(event); },
options
);
}
},
/**
* editorId should be set in config
*/
editorId: null,
/**
* Get a reference to the editor
*/
getEditor: function () {
return RTEarea[this.editorId].editor;
},
/**
* Get a reference to the toolbar
*/
getToolbar: function () {
return this.framework.toolbar;
},
/**
* Get a reference to the statusBar
*/
getStatusBar: function () {
return this.framework.statusBar;
},
/**
* Get a reference to a button
*/
getButton: function (buttonId) {
return this.getToolbar().getButton(buttonId);
},
/**
* Flag set to true when the iframe becomes usable for editing
*/
ready: false,
/**
* Create the iframe element at rendering time
*
* #param object container: the container into which to insert the iframe (that is the framework)
* #return void
*/
createIframe: function (container) {
if (this.autoEl && this.autoEl.tag) {
this.el = document.createElement(this.autoEl.tag);
if (this.autoEl.id) {
this.el.setAttribute('id', this.autoEl.id);
}
if (this.autoEl.cls) {
this.el.setAttribute('class', this.autoEl.cls);
}
if (this.autoEl.src) {
this.el.setAttribute('src', this.autoEl.src);
}
this.el = container.appendChild(this.el);
}
},
/**
* Get the content window of the iframe
*/
getIframeWindow: function () {
return this.el.contentWindow ? this.el.contentWindow : this.el.contentDocument;
},
/**
* Proceed to build the iframe document head and ensure style sheets are available after the iframe document becomes available
*/
initializeIframe: function () {
var self = this;
var iframe = this.getEl();
// All browsers
if (!iframe || (!iframe.contentWindow && !iframe.contentDocument)) {
window.setTimeout(function () {
self.initializeIframe();
}, 50);
// All except WebKit
} else if (iframe.contentWindow && !UserAgent.isWebKit && (!iframe.contentWindow.document || !iframe.contentWindow.document.documentElement)) {
window.setTimeout(function () {
self.initializeIframe();
}, 50);
// WebKit
} else if (UserAgent.isWebKit && (!iframe.contentDocument.documentElement || !iframe.contentDocument.body)) {
window.setTimeout(function () {
self.initializeIframe();
}, 50);
} else {
this.document = iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument;
this.getEditor().document = this.document;
this.createHead();
// Style the document body
Dom.addClass(this.document.body, 'htmlarea-content-body');
// Start listening to things happening in the iframe
// For some unknown reason, this is too early for Opera
if (!UserAgent.isOpera) {
this.startListening();
}
// Hide the iframe
this.hide();
// Set iframe ready
this.ready = true;
/**
* #event HTMLAreaEventIframeReady
* Fires when the iframe style sheets become accessible
*/
Event.trigger(this, 'HTMLAreaEventIframeReady');
}
},
/**
* Show the iframe
*/
show: function () {
this.getEl().style.display = '';
Event.trigger(this, 'HTMLAreaEventIframeShow');
},
/**
* Hide the iframe
*/
hide: function () {
this.getEl().style.display = 'none';
},
/**
* Build the iframe document head
*/
createHead: function () {
var head = this.document.getElementsByTagName('head')[0];
if (!head) {
head = this.document.createElement('head');
this.document.documentElement.appendChild(head);
}
if (this.config.baseURL) {
var base = this.document.getElementsByTagName('base')[0];
if (!base) {
base = this.document.createElement('base');
base.href = this.config.baseURL;
head.appendChild(base);
}
this.getEditor().appendToLog('HTMLArea.Iframe', 'createHead', 'Iframe baseURL set to: ' + base.href, 'info');
}
var link0 = this.document.getElementsByTagName('link')[0];
if (!link0) {
link0 = this.document.createElement('link');
link0.rel = 'stylesheet';
link0.type = 'text/css';
link0.href = this.config.editedContentStyle;
head.appendChild(link0);
this.getEditor().appendToLog('HTMLArea.Iframe', 'createHead', 'Skin CSS set to: ' + link0.href, 'info');
}
var pageStyle;
for (var i = 0, n = this.config.pageStyle.length; i < n; i++) {
pageStyle = this.config.pageStyle[i];
var link = this.document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = pageStyle;
head.appendChild(link);
this.getEditor().appendToLog('HTMLArea.Iframe', 'createHead', 'Content CSS set to: ' + link.href, 'info');
}
},
/**
* Focus on the iframe
*/
focus: function () {
try {
if (UserAgent.isWebKit) {
this.getEl().focus();
}
this.getEl().contentWindow.focus();
} catch(e) { }
},
/**
* Flag indicating whether the framework is inside a tab or inline element that may be hidden
* Should be set in config
*/
isNested: false,
/**
* All nested tabs and inline levels in the sorting order they were applied
* Should be set in config
*/
nestedParentElements: {},
/**
* Set designMode
*
* #param boolean on: if true set designMode to on, otherwise set to off
*
* #rturn void
*/
setDesignMode: function (on) {
if (on) {
if (!UserAgent.isIE) {
if (UserAgent.isGecko) {
// In Firefox, we can't set designMode when we are in a hidden TYPO3 tab or inline element
if (!this.isNested || Typo3.allElementsAreDisplayed(this.nestedParentElements.sorted)) {
this.document.designMode = 'on';
this.setOptions();
}
} else {
this.document.designMode = 'on';
this.setOptions();
}
}
if (UserAgent.isIE || UserAgent.isWebKit) {
this.document.body.contentEditable = true;
}
} else {
if (!UserAgent.isIE) {
this.document.designMode = 'off';
}
if (UserAgent.isIE || UserAgent.isWebKit) {
this.document.body.contentEditable = false;
}
}
},
/**
* Set editing mode options (if we can... raises exception in Firefox 3)
*
* #return void
*/
setOptions: function () {
if (!UserAgent.isIE) {
try {
if (this.document.queryCommandEnabled('insertBrOnReturn')) {
this.document.execCommand('insertBrOnReturn', false, this.config.disableEnterParagraphs);
}
if (this.document.queryCommandEnabled('styleWithCSS')) {
this.document.execCommand('styleWithCSS', false, this.config.useCSS);
} else if (UserAgent.isGecko && this.document.queryCommandEnabled('useCSS')) {
this.document.execCommand('useCSS', false, !this.config.useCSS);
}
if (UserAgent.isGecko) {
if (this.document.queryCommandEnabled('enableObjectResizing')) {
this.document.execCommand('enableObjectResizing', false, !this.config.disableObjectResizing);
}
if (this.document.queryCommandEnabled('enableInlineTableEditing')) {
this.document.execCommand('enableInlineTableEditing', false, (this.config.buttons.table && this.config.buttons.table.enableHandles) ? true : false);
}
}
} catch(e) {}
}
},
/**
* Mutations handler invoked when an hidden TYPO3 hidden nested tab or inline element is shown
*/
onNestedShowMutation: function (mutations) {
for (var i = mutations.length; --i >= 0;) {
var targetId = mutations[i].target.id;
if (this.nestedParentElements.sorted.indexOf(targetId) !== -1 || this.nestedParentElements.sorted.indexOf(targetId.replace('_div', '_fields')) !== -1) {
this.onNestedShowAction();
}
}
},
/**
* Handler invoked when an hidden TYPO3 hidden nested tab or inline element is shown
*/
onNestedShow: function (event) {
Event.stopEvent(event);
var target = event.target;
var delay = event.data.delay;
var self = this;
window.setTimeout(function () {
var styleEvent = true;
// In older versions of Gecko attrName is not set and referring to it causes a non-catchable crash
if ((UserAgent.isGecko && navigator.productSub > 2007112700) || UserAgent.isOpera || UserAgent.isIE) {
styleEvent = (event.originalEvent.attrName === 'style') || (event.originalEvent.attrName === 'className') || (event.originalEvent.attrName === 'class');
}
if (styleEvent && (self.nestedParentElements.sorted.indexOf(target.id) != -1 || self.nestedParentElements.sorted.indexOf(target.id.replace('_div', '_fields')) != -1)) {
self.onNestedShowAction();
}
}, delay);
return false;
},
/**
* Take action when nested tab or inline element is shown
*/
onNestedShowAction: function () {
// Check if all container nested elements are displayed
if (Typo3.allElementsAreDisplayed(this.nestedParentElements.sorted)) {
if (this.getEditor().getMode() === 'wysiwyg') {
if (UserAgent.isGecko) {
this.setDesignMode(true);
}
Event.trigger(this, 'HTMLAreaEventIframeShow');
} else {
Event.trigger(this.framework.getTextAreaContainer(), 'HTMLAreaEventTextAreaContainerShow');
}
this.getToolbar().update();
}
},
/**
* Instance of DOM walker
*/
htmlRenderer: null,
/**
* Getter for the instance of DOM walker
*/
getHtmlRenderer: function () {
if (!this.htmlRenderer) {
this.htmlRenderer = new Walker({
keepComments: !this.config.htmlRemoveComments,
removeTags: this.config.htmlRemoveTags,
removeTagsAndContents: this.config.htmlRemoveTagsAndContents,
baseUrl: this.config.baseURL
});
}
return this.htmlRenderer;
},
/**
* Get the HTML content of the iframe
*/
getHTML: function () {
return this.getHtmlRenderer().render(this.document.body, false);
},
/**
* Start listening to things happening in the iframe
*/
startListening: function () {
var self = this;
// Create keyMap so that plugins may bind key handlers
this.keyMap = new KeyMap(this.document.documentElement, (UserAgent.isIE || UserAgent.isWebKit) ? 'keydown' : 'keypress');
// Special keys map
this.keyMap.addBinding(
{
key: [Event.DOWN, Event.UP, Event.LEFT, Event.RIGHT],
alt: false,
handler: function (event) { return self.onArrow(event); }
}
);
this.keyMap.addBinding(
{
key: Event.TAB,
ctrl: false,
alt: false,
handler: function (event) { return self.onTab(event); }
}
);
this.keyMap.addBinding(
{
key: Event.SPACE,
ctrl: true,
shift: false,
alt: false,
handler: function (event) { return self.onCtrlSpace(event); }
}
);
if (UserAgent.isGecko || UserAgent.isIE || UserAgent.isWebKit) {
this.keyMap.addBinding(
{
key: [Event.BACKSPACE, Event.DELETE],
alt: false,
handler: function (event) { return self.onBackSpace(event); }
});
}
if (!UserAgent.isIE && !this.config.disableEnterParagraphs) {
this.keyMap.addBinding(
{
key: Event.ENTER,
shift: false,
handler: function (event) { return self.onEnter(event); }
});
}
if (UserAgent.isWebKit) {
this.keyMap.addBinding(
{
key: Event.ENTER,
alt: false,
handler: function (event) { return self.onWebKitEnter(event); }
});
}
// Hot key map (on keydown for all browsers)
var hotKeys = [];
for (var key in this.config.hotKeyList) {
if (key.length === 1) {
hotKeys.push(key);
}
}
// Make hot key map available, even if empty, so that plugins may add bindings
this.hotKeyMap = new KeyMap(this.document.documentElement, 'keydown');
if (hotKeys.length > 0) {
this.hotKeyMap.addBinding({
key: hotKeys,
ctrl: true,
shift: false,
alt: false,
handler: function (event) { return self.onHotKey(event); }
});
}
Event.on(
this.document.documentElement,
(UserAgent.isIE || UserAgent.isWebKit) ? 'keydown' : 'keypress',
function (event) { return self.onAnyKey(event); }
);
Event.on(
this.document.documentElement,
'mouseup',
function (event) { return self.onMouse(event); }
);
Event.on(
this.document.documentElement,
'click',
function (event) { return self.onMouse(event); }
);
if (UserAgent.isGecko) {
Event.on(
this.document.documentElement,
'paste',
function (event) { return self.onPaste(event); }
);
}
Event.on(
this.document.documentElement,
'drop',
function (event) { return self.onDrop(event); }
);
if (UserAgent.isWebKit) {
Event.on(
this.document.body,
'dragend',
function (event) { return self.onDrop(event); }
);
}
},
/**
* Handler for other key events
*/
onAnyKey: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
/**
* #event HTMLAreaEventWordCountChange
* Fires when the word count may have changed
*/
Event.trigger(this, 'HTMLAreaEventWordCountChange', [100]);
if (!event.altKey && !(event.ctrlKey || event.metaKey)) {
var key = Event.getKey(event);
// Detect URL in non-IE browsers
if (!UserAgent.isIE && (key !== Event.ENTER || (event.shiftKey && !UserAgent.isWebKit))) {
this.getEditor().getSelection().detectURL(event);
}
// Handle option+SPACE for Mac users
if (UserAgent.isMac && key === Event.NON_BREAKING_SPACE) {
return this.onOptionSpace(key, event);
}
}
return true;
},
/**
* On any key input event, check if input is currently inhibited
*/
inhibitKeyboardInput: function (event) {
// Inhibit key events while server-based cleaning is being processed
if (this.getEditor().inhibitKeyboardInput) {
Event.stopEvent(event);
return true;
} else {
return false;
}
},
/**
* Handler for mouse events
*/
onMouse: function (event) {
// In WebKit, select the image when it is clicked
if (UserAgent.isWebKit && /^(img)$/i.test(event.target.nodeName) && event.type === 'click') {
this.getEditor().getSelection().selectNode(event.target);
}
this.getToolbar().updateLater(100);
return true;
},
/**
* Handler for paste operations in Gecko
*/
onPaste: function (event) {
// Make src and href urls absolute
if (UserAgent.isGecko) {
var self = this;
window.setTimeout(function () {
Dom.makeUrlsAbsolute(self.getEditor().document.body, self.config.baseURL, self.getHtmlRenderer());
}, 50);
}
return true;
},
/**
* Handler for drag and drop operations
*/
onDrop: function (event) {
var self = this;
// Clean up span elements added by WebKit
if (UserAgent.isWebKit) {
window.setTimeout(function () {
self.getEditor().getDomNode().cleanAppleStyleSpans(self.getEditor().document.body);
}, 50);
}
// Make src url absolute in Firefox
if (UserAgent.isGecko) {
window.setTimeout(function () {
Dom.makeUrlsAbsolute(event.target, self.config.baseURL, self.getHtmlRenderer());
}, 50);
}
this.getToolbar().updateLater(100);
return true;
},
/**
* Handler for UP, DOWN, LEFT and RIGHT arrow keys
*/
onArrow: function (event) {
this.getToolbar().updateLater(100);
return true;
},
/**
* Handler for TAB and SHIFT-TAB keys
*
* If available, BlockElements plugin will handle the TAB key
*/
onTab: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
var keyName = (event.shiftKey ? 'SHIFT-' : '') + 'TAB';
if (this.config.hotKeyList[keyName] && this.config.hotKeyList[keyName].cmd) {
var button = this.getButton(this.config.hotKeyList[keyName].cmd);
if (button) {
Event.stopEvent(event);
/**
* #event HTMLAreaEventHotkey
* Fires when the button hotkey is pressed
*/
Event.trigger(button, 'HTMLAreaEventHotkey', [keyName, event]);
return false;
}
}
return true;
},
/**
* Handler for BACKSPACE and DELETE keys
*/
onBackSpace: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
if ((!UserAgent.isIE && !event.shiftKey) || UserAgent.isIE) {
if (this.getEditor().getSelection().handleBackSpace()) {
Event.stopEvent(event);
return false;
}
}
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return true;
},
/**
* Handler for ENTER key in non-IE browsers
*/
onEnter: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
this.getEditor().getSelection().detectURL(event);
if (this.getEditor().getSelection().checkInsertParagraph()) {
Event.stopEvent(event);
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return false;
}
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return true;
},
/**
* Handler for ENTER key in WebKit browsers
*/
onWebKitEnter: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
if (event.shiftKey || this.config.disableEnterParagraphs) {
var editor = this.getEditor();
editor.getSelection().detectURL(event);
if (UserAgent.isSafari) {
var brNode = editor.document.createElement('br');
editor.getSelection().insertNode(brNode);
brNode.parentNode.normalize();
// Selection issue when an URL was detected
if (editor._unlinkOnUndo) {
brNode = brNode.parentNode.parentNode.insertBefore(brNode, brNode.parentNode.nextSibling);
}
if (!brNode.nextSibling || !/\S+/i.test(brNode.nextSibling.textContent)) {
var secondBrNode = editor.document.createElement('br');
secondBrNode = brNode.parentNode.appendChild(secondBrNode);
}
editor.getSelection().selectNode(brNode, false);
Event.stopEvent(event);
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return false;
}
}
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return true;
},
/**
* Handler for CTRL-SPACE keys
*/
onCtrlSpace: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
this.getEditor().getSelection().insertHtml(' ');
Event.stopEvent(event);
return false;
},
/**
* Handler for OPTION-SPACE keys on Mac
*/
onOptionSpace: function (key, event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
this.getEditor().getSelection().insertHtml(' ');
Event.stopEvent(event);
return false;
},
/**
* Handler for configured hotkeys
*/
onHotKey: function (event) {
var key = Event.getKey(event);
if (this.inhibitKeyboardInput(event)) {
return false;
}
var hotKey = String.fromCharCode(key).toLowerCase();
/**
* #event HTMLAreaEventHotkey
* Fires when the button hotkey is pressed
*/
Event.trigger(this.getButton(this.config.hotKeyList[hotKey].cmd), 'HTMLAreaEventHotkey', [hotKey, event]);
return false;
},
/**
* Cleanup (called by framework)
*/
onBeforeDestroy: function () {
// Remove listeners on nested elements
if (this.isNested) {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
} else {
for (var i = this.nestedParentElements.sorted.length; --i >= 0;) {
var nestedElement = document.getElementById(this.nestedParentElements.sorted[i]);
Event.off(nestedElement);
Event.off(nestedElement.parentNode);
}
}
}
Event.off(this);
Event.off(this.getEl());
Event.off(this.document.body);
Event.off(this.document.documentElement);
// Cleaning references to DOM in order to avoid IE memory leaks
this.document = null;
this.el = null;
}
};
return Iframe;
});
error on line 202 by documentElement || !iframe.contentDocument.body)) {:
} else if (UserAgent.isWebKit && (!iframe.contentDocument.documentElement || !iframe.contentDocument.body)) {
How i can fix it ?
I had the same problem and found the bugfix. In old versions, the iframe source was initialized with about:blank in the rtehtmlarea extension, this is the root cause for the problem you are facing. There was an official bugfix 2 years ago - link to Github: https://github.com/FriendsOfTYPO3/rtehtmlarea/commit/a20e23445ca760ba94ed06dca05266b6e22a25fb
You can backport the fix or update the extension, then it is working as expected in the new Chrome version.
Apparently you're using a TYPO3 version prior to v8.7 with rtehtmlarea which is outdated and unsupported since quite a while. With TYPO3 v8.7 ckeditor was introduced.
Maybe this StackOverflow answer helps you a little bit. Otherwise just use Firefox.
But you're strongly advised to upgrade your TYPO3 installation to the latest version v10.4.

How to return element object from a function that return an object in javascript?

This is only for learning purpose not for making something else.
In this code below I need some fixing. When I call the superUser('elm') it should return all the specific elements that were given instead of returning whole the objects. Similar to jQuery, (just for learning purpose).
So, It should work like jQuery.
When I called the only superUser('elm') It should return what jQuery return I mean all the elements. Again If I call superUser('elm').on('click', function) it should also work in this case.
!(function (global, factory) {
if (typeof factory === 'function')
global.superUser = factory();
}(typeof window !== undefined ? window : this, function () {
/**
*
* #object
* Initilize the main object
*
*/
let superUser_fn = {};
/**
* #targeted_element
* this will give an elm
*
*/
let elm = '';
/**
* #superUser
* the main function for the project
*
*/
const superUser = function (element) {
elm = document.querySelectorAll(element);
return superUser_fn;
}
/**
*
* #handling_event
* This will return an event listener if exist else
* it will attach a event for the client
*
* #arguments
* #event = it will give a event for action
* #callback = a callback function
*
*/
superUser_fn['on'] = function (event, callback) {
if (elm !== null)
if (elm.addEventListener)
elm.addEventListener(event, callback);
else if (elm.attachEvent)
elm.attachEvent(event, callback);
}
/**
*
* #return
* return the full object what ever we work in
*/
return superUser;
}));
console.log(superUser('h1'));
const superUser = function (element) {
elm = document.querySelectorAll(element);
elm.on = superUser_fn['on'];
return elm;
}
/**
*
* #handling_event
* This will return an event listener if exist else
* it will attach a event for the client
*
* #arguments
* #event = it will give a event for action
* #callback = a callback function
*
*/
superUser_fn['on'] = function (event, callback) {
const handler = () => callback(elm)
if (elm !== null)
if (elm.addEventListener)
elm.addEventListener(event, handler);
else if (elm.attachEvent)
elm.attachEvent(event, handler);
Not sure if I got your intention..but.. it seems close enough for me to share

When value is x, display text next to value

I am creating a form that includes a budget slider with the following code
<div class="budget_slider">
<input type="range" name="budget" min="1" max="12" step="1" value="0" data-orientation="horizontal" onchange="getVals(this, 'budget');" style="position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0;"><div class="rangeslider rangeslider--horizontal" id="js-rangeslider-0"><div class="rangeslider__fill" style="width: 10px;"></div><div class="rangeslider__handle" style="left: 0px;"></div></div><span>1</span>
</div>
It is a slider that goes from 1 to 12. When the slider hits 12, I want the value to show "12+" and not just "12". That is: when the slider is in any value between 1 and 11, just show that number, but the next value should be 12+, indicating that the client is willing to buy 12 or more items. Is there any way I can do that?
This is the rangeslider.js file:
/*! rangeslider.js - v2.3.0 | (c) 2016 #andreruffert | MIT license | https://github.com/andreruffert/rangeslider.js */
(function(factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function($) {
'use strict';
// Polyfill Number.isNaN(value)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
Number.isNaN = Number.isNaN || function(value) {
return typeof value === 'number' && value !== value;
};
/**
* Range feature detection
* #return {Boolean}
*/
function supportsRange() {
var input = document.createElement('input');
input.setAttribute('type', 'range');
return input.type !== 'text';
}
var pluginName = 'rangeslider',
pluginIdentifier = 0,
hasInputRangeSupport = supportsRange(),
defaults = {
polyfill: true,
orientation: 'horizontal',
rangeClass: 'rangeslider',
disabledClass: 'rangeslider--disabled',
activeClass: 'rangeslider--active',
horizontalClass: 'rangeslider--horizontal',
verticalClass: 'rangeslider--vertical',
fillClass: 'rangeslider__fill',
handleClass: 'rangeslider__handle',
startEvent: ['mousedown', 'touchstart', 'pointerdown'],
moveEvent: ['mousemove', 'touchmove', 'pointermove'],
endEvent: ['mouseup', 'touchend', 'pointerup']
},
constants = {
orientation: {
horizontal: {
dimension: 'width',
direction: 'left',
directionStyle: 'left',
coordinate: 'x'
},
vertical: {
dimension: 'height',
direction: 'top',
directionStyle: 'bottom',
coordinate: 'y'
}
}
};
/**
* Delays a function for the given number of milliseconds, and then calls
* it with the arguments supplied.
*
* #param {Function} fn [description]
* #param {Number} wait [description]
* #return {Function}
*/
function delay(fn, wait) {
var args = Array.prototype.slice.call(arguments, 2);
return setTimeout(function(){ return fn.apply(null, args); }, wait);
}
/**
* Returns a debounced function that will make sure the given
* function is not triggered too much.
*
* #param {Function} fn Function to debounce.
* #param {Number} debounceDuration OPTIONAL. The amount of time in milliseconds for which we will debounce the function. (defaults to 100ms)
* #return {Function}
*/
function debounce(fn, debounceDuration) {
debounceDuration = debounceDuration || 100;
return function() {
if (!fn.debouncing) {
var args = Array.prototype.slice.apply(arguments);
fn.lastReturnVal = fn.apply(window, args);
fn.debouncing = true;
}
clearTimeout(fn.debounceTimeout);
fn.debounceTimeout = setTimeout(function(){
fn.debouncing = false;
}, debounceDuration);
return fn.lastReturnVal;
};
}
/**
* Check if a `element` is visible in the DOM
*
* #param {Element} element
* #return {Boolean}
*/
function isHidden(element) {
return (
element && (
element.offsetWidth === 0 ||
element.offsetHeight === 0 ||
// Also Consider native `<details>` elements.
element.open === false
)
);
}
/**
* Get hidden parentNodes of an `element`
*
* #param {Element} element
* #return {[type]}
*/
function getHiddenParentNodes(element) {
var parents = [],
node = element.parentNode;
while (isHidden(node)) {
parents.push(node);
node = node.parentNode;
}
return parents;
}
/**
* Returns dimensions for an element even if it is not visible in the DOM.
*
* #param {Element} element
* #param {String} key (e.g. offsetWidth …)
* #return {Number}
*/
function getDimension(element, key) {
var hiddenParentNodes = getHiddenParentNodes(element),
hiddenParentNodesLength = hiddenParentNodes.length,
inlineStyle = [],
dimension = element[key];
// Used for native `<details>` elements
function toggleOpenProperty(element) {
if (typeof element.open !== 'undefined') {
element.open = (element.open) ? false : true;
}
}
if (hiddenParentNodesLength) {
for (var i = 0; i < hiddenParentNodesLength; i++) {
// Cache style attribute to restore it later.
inlineStyle[i] = hiddenParentNodes[i].style.cssText;
// visually hide
if (hiddenParentNodes[i].style.setProperty) {
hiddenParentNodes[i].style.setProperty('display', 'block', 'important');
} else {
hiddenParentNodes[i].style.cssText += ';display: block !important';
}
hiddenParentNodes[i].style.height = '0';
hiddenParentNodes[i].style.overflow = 'hidden';
hiddenParentNodes[i].style.visibility = 'hidden';
toggleOpenProperty(hiddenParentNodes[i]);
}
// Update dimension
dimension = element[key];
for (var j = 0; j < hiddenParentNodesLength; j++) {
// Restore the style attribute
hiddenParentNodes[j].style.cssText = inlineStyle[j];
toggleOpenProperty(hiddenParentNodes[j]);
}
}
return dimension;
}
/**
* Returns the parsed float or the default if it failed.
*
* #param {String} str
* #param {Number} defaultValue
* #return {Number}
*/
function tryParseFloat(str, defaultValue) {
var value = parseFloat(str);
return Number.isNaN(value) ? defaultValue : value;
}
/**
* Capitalize the first letter of string
*
* #param {String} str
* #return {String}
*/
function ucfirst(str) {
return str.charAt(0).toUpperCase() + str.substr(1);
}
/**
* Plugin
* #param {String} element
* #param {Object} options
*/
function Plugin(element, options) {
this.$window = $(window);
this.$document = $(document);
this.$element = $(element);
this.options = $.extend( {}, defaults, options );
this.polyfill = this.options.polyfill;
this.orientation = this.$element[0].getAttribute('data-orientation') || this.options.orientation;
this.onInit = this.options.onInit;
this.onSlide = this.options.onSlide;
this.onSlideEnd = this.options.onSlideEnd;
this.DIMENSION = constants.orientation[this.orientation].dimension;
this.DIRECTION = constants.orientation[this.orientation].direction;
this.DIRECTION_STYLE = constants.orientation[this.orientation].directionStyle;
this.COORDINATE = constants.orientation[this.orientation].coordinate;
// Plugin should only be used as a polyfill
if (this.polyfill) {
// Input range support?
if (hasInputRangeSupport) { return false; }
}
this.identifier = 'js-' + pluginName + '-' +(pluginIdentifier++);
this.startEvent = this.options.startEvent.join('.' + this.identifier + ' ') + '.' + this.identifier;
this.moveEvent = this.options.moveEvent.join('.' + this.identifier + ' ') + '.' + this.identifier;
this.endEvent = this.options.endEvent.join('.' + this.identifier + ' ') + '.' + this.identifier;
this.toFixed = (this.step + '').replace('.', '').length - 1;
this.$fill = $('<div class="' + this.options.fillClass + '" />');
this.$handle = $('<div class="' + this.options.handleClass + '" />');
this.$range = $('<div class="' + this.options.rangeClass + ' ' + this.options[this.orientation + 'Class'] + '" id="' + this.identifier + '" />').insertAfter(this.$element).prepend(this.$fill, this.$handle);
// visually hide the input
this.$element.css({
'position': 'absolute',
'width': '1px',
'height': '1px',
'overflow': 'hidden',
'opacity': '0'
});
// Store context
this.handleDown = $.proxy(this.handleDown, this);
this.handleMove = $.proxy(this.handleMove, this);
this.handleEnd = $.proxy(this.handleEnd, this);
this.init();
// Attach Events
var _this = this;
this.$window.on('resize.' + this.identifier, debounce(function() {
// Simulate resizeEnd event.
delay(function() { _this.update(false, false); }, 300);
}, 20));
this.$document.on(this.startEvent, '#' + this.identifier + ':not(.' + this.options.disabledClass + ')', this.handleDown);
// Listen to programmatic value changes
this.$element.on('change.' + this.identifier, function(e, data) {
if (data && data.origin === _this.identifier) {
return;
}
var value = e.target.value,
pos = _this.getPositionFromValue(value);
_this.setPosition(pos);
});
}
Plugin.prototype.init = function() {
this.update(true, false);
if (this.onInit && typeof this.onInit === 'function') {
this.onInit();
}
};
Plugin.prototype.update = function(updateAttributes, triggerSlide) {
updateAttributes = updateAttributes || false;
if (updateAttributes) {
this.min = tryParseFloat(this.$element[0].getAttribute('min'), 0);
this.max = tryParseFloat(this.$element[0].getAttribute('max'), 100);
this.value = tryParseFloat(this.$element[0].value, Math.round(this.min + (this.max-this.min)/2));
this.step = tryParseFloat(this.$element[0].getAttribute('step'), 1);
}
this.handleDimension = getDimension(this.$handle[0], 'offset' + ucfirst(this.DIMENSION));
this.rangeDimension = getDimension(this.$range[0], 'offset' + ucfirst(this.DIMENSION));
this.maxHandlePos = this.rangeDimension - this.handleDimension;
this.grabPos = this.handleDimension / 2;
this.position = this.getPositionFromValue(this.value);
// Consider disabled state
if (this.$element[0].disabled) {
this.$range.addClass(this.options.disabledClass);
} else {
this.$range.removeClass(this.options.disabledClass);
}
this.setPosition(this.position, triggerSlide);
};
Plugin.prototype.handleDown = function(e) {
e.preventDefault();
this.$document.on(this.moveEvent, this.handleMove);
this.$document.on(this.endEvent, this.handleEnd);
// add active class because Firefox is ignoring
// the handle:active pseudo selector because of `e.preventDefault();`
this.$range.addClass(this.options.activeClass);
// If we click on the handle don't set the new position
if ((' ' + e.target.className + ' ').replace(/[\n\t]/g, ' ').indexOf(this.options.handleClass) > -1) {
return;
}
var pos = this.getRelativePosition(e),
rangePos = this.$range[0].getBoundingClientRect()[this.DIRECTION],
handlePos = this.getPositionFromNode(this.$handle[0]) - rangePos,
setPos = (this.orientation === 'vertical') ? (this.maxHandlePos - (pos - this.grabPos)) : (pos - this.grabPos);
this.setPosition(setPos);
if (pos >= handlePos && pos < handlePos + this.handleDimension) {
this.grabPos = pos - handlePos;
}
};
Plugin.prototype.handleMove = function(e) {
e.preventDefault();
var pos = this.getRelativePosition(e);
var setPos = (this.orientation === 'vertical') ? (this.maxHandlePos - (pos - this.grabPos)) : (pos - this.grabPos);
this.setPosition(setPos);
};
Plugin.prototype.handleEnd = function(e) {
e.preventDefault();
this.$document.off(this.moveEvent, this.handleMove);
this.$document.off(this.endEvent, this.handleEnd);
this.$range.removeClass(this.options.activeClass);
// Ok we're done fire the change event
this.$element.trigger('change', { origin: this.identifier });
if (this.onSlideEnd && typeof this.onSlideEnd === 'function') {
this.onSlideEnd(this.position, this.value);
}
};
Plugin.prototype.cap = function(pos, min, max) {
if (pos < min) { return min; }
if (pos > max) { return max; }
return pos;
};
Plugin.prototype.setPosition = function(pos, triggerSlide) {
var value, newPos;
if (triggerSlide === undefined) {
triggerSlide = true;
}
// Snapping steps
value = this.getValueFromPosition(this.cap(pos, 0, this.maxHandlePos));
newPos = this.getPositionFromValue(value);
// Update ui
this.$fill[0].style[this.DIMENSION] = (newPos + this.grabPos) + 'px';
this.$handle[0].style[this.DIRECTION_STYLE] = newPos + 'px';
this.setValue(value);
// Update globals
this.position = newPos;
this.value = value;
if (triggerSlide && this.onSlide && typeof this.onSlide === 'function') {
this.onSlide(newPos, value);
}
};
// Returns element position relative to the parent
Plugin.prototype.getPositionFromNode = function(node) {
var i = 0;
while (node !== null) {
i += node.offsetLeft;
node = node.offsetParent;
}
return i;
};
Plugin.prototype.getRelativePosition = function(e) {
// Get the offset DIRECTION relative to the viewport
var ucCoordinate = ucfirst(this.COORDINATE),
rangePos = this.$range[0].getBoundingClientRect()[this.DIRECTION],
pageCoordinate = 0;
if (typeof e.originalEvent['client' + ucCoordinate] !== 'undefined') {
pageCoordinate = e.originalEvent['client' + ucCoordinate];
}
else if (
e.originalEvent.touches &&
e.originalEvent.touches[0] &&
typeof e.originalEvent.touches[0]['client' + ucCoordinate] !== 'undefined'
) {
pageCoordinate = e.originalEvent.touches[0]['client' + ucCoordinate];
}
else if(e.currentPoint && typeof e.currentPoint[this.COORDINATE] !== 'undefined') {
pageCoordinate = e.currentPoint[this.COORDINATE];
}
return pageCoordinate - rangePos;
};
Plugin.prototype.getPositionFromValue = function(value) {
var percentage, pos;
percentage = (value - this.min)/(this.max - this.min);
pos = (!Number.isNaN(percentage)) ? percentage * this.maxHandlePos : 0;
return pos;
};
Plugin.prototype.getValueFromPosition = function(pos) {
var percentage, value;
percentage = ((pos) / (this.maxHandlePos || 1));
value = this.step * Math.round(percentage * (this.max - this.min) / this.step) + this.min;
return Number((value).toFixed(this.toFixed));
};
Plugin.prototype.setValue = function(value) {
if (value === this.value && this.$element[0].value !== '') {
return;
}
// Set the new value and fire the `input` event
this.$element
.val(value)
.trigger('input', { origin: this.identifier });
};
Plugin.prototype.destroy = function() {
this.$document.off('.' + this.identifier);
this.$window.off('.' + this.identifier);
this.$element
.off('.' + this.identifier)
.removeAttr('style')
.removeData('plugin_' + pluginName);
// Remove the generated markup
if (this.$range && this.$range.length) {
this.$range[0].parentNode.removeChild(this.$range[0]);
}
};
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[pluginName] = function(options) {
var args = Array.prototype.slice.call(arguments, 1);
return this.each(function() {
var $this = $(this),
data = $this.data('plugin_' + pluginName);
// Create a new instance.
if (!data) {
$this.data('plugin_' + pluginName, (data = new Plugin(this, options)));
}
// Make it possible to access methods from public.
// e.g `$element.rangeslider('method');`
if (typeof options === 'string') {
data[options].apply(data, args);
}
});
};
return 'rangeslider.js is available in jQuery context e.g $(selector).rangeslider(options);';
}));
Thank you in advance!
I'm not 100% sure what exactly I am looking at above, but I won't be a jerk and vote down your question lol. Here's the basic idea of what you are trying to accomplish:
Create a condition in your JavaScript that applies to the numeric value of your range slider. If it's equal to 12 to set it equal to a string equal to "12+". This is pseudo code, but the basic idea would be:
if (numericValue == 12)
{displayedValue = "12+"}
If your range slider won't display a string value and requires and integer, then create a div tag with your "+" next to your numeric value and adjust it's visibility based on the value of your slider being equal to 12.
I would recommend not using the open source code you have above unless you absolutely have to. There is already a default range slider available in HTML that you can customize:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range
Avoid open source projects where possible unless it's something you can't live without that isn't readily available.
Always sucks being a beginner, but hang in there, and it'll come together. Hope this gets you moving in the right direction.

Passing non-breaking spaces before word in string

I have a JavaScript variable below which I need to pass some non-breaking spaces to, before the word.
var str = " Test";
How do I achieve this?
You can do something like this,
Creating a temporary div using $("<div/>")
Adding html content as the string using html(str)
Now to get the decoded string we can use text()
var str = " Test"
console.log($("<div/>").html(str).text());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
In pure javascript you can use a textarea,
You can create a textarea using document.createElement('textarea')
Now you can use innerHTML to set the html content
For retrieving the decoded data you can use ele.value
var str = " Test",
ele = document.createElement('textarea');
ele.innerHTML = str;
console.log(ele.value);
I have added slight variation in your plugin code
/**
* jquery.typist — animated text typing
* #author Alexander Burtsev, http://burtsev.me, 2014—2015
* #license MIT
*/
(function(factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(jQuery);
}
}(function($) {
'use strict';
$.fn.typistInit = function() {
return this.each(function() {
if (!$(this).data('typist')) {
new Typist(this);
}
});
};
$.fn.typist = function(opts) {
return this.each(function() {
new Typist(this, opts);
});
};
$.fn.typistAdd = function(text, callback) {
return this
.typistInit()
.each(function() {
var self = $(this).data('typist');
self.queue.push({
text: text,
callback: callback
});
self.type();
});
};
$.fn.typistRemove = function(length, callback) {
length = parseInt(length) || 0;
return this
.typistInit()
.each(function() {
var self = $(this).data('typist');
self.queue.push({
remove: length,
callback: callback
});
self.type();
});
};
$.fn.typistPause = function(delay, callback) {
delay = parseInt(delay) || 0;
return this
.typistInit()
.each(function() {
var self = $(this).data('typist');
self.queue.push({
delay: delay,
callback: callback
});
self.type();
});
};
$.fn.typistStop = function() {
return this
.typistInit()
.each(function() {
var self = $(this).data('typist');
self.queue.push({
stop: true
});
self.type();
});
};
/**
* #class
* #param {HTMLElement} element
* #param {Object} [opts]
* #param {String} [opts.text=''] Text for typing
* #param {Number} [opts.speed=10] Typing speed (characters per second)
* #param {Boolean} [opts.cursor=true] Shows blinking cursor
* #param {Number} [opts.blinkSpeed=2] Blinking per second
* #param {String} [opts.typeFrom='end'] Typing from start/end of element
* #param {Object} [opts.cursorStyles] CSS properties for cursor element
*/
function Typist(element, opts) {
$.extend(this, {
speed: 10, // characters per second
text: '',
cursor: true,
blinkSpeed: 2, // blink per second
typeFrom: 'end', // 'start', 'end'
cursorStyles: {
display: 'inline-block',
fontStyle: 'normal',
margin: '-2px 2px 0 2px'
}
}, opts || {});
this._cursor = null;
this._element = $(element);
this._element.data('typist', this);
this._container = null;
this.queue = [];
this.timer = null;
this.delay = 1000 / this.speed;
this.blinkTimer = null;
this.blinkDelay = 1000 / this.blinkSpeed;
if (this.text) {
this.queue.push({
text: this.text
});
this.type();
}
}
Typist.prototype =
/** #lends Typist */
{
/**
* Adds blinking cursor into element
*/
addCursor: function() {
if (this._cursor) {
clearInterval(this.blinkTimer);
this._cursor
.stop()
.remove();
}
this._cursor = $('<span>|</span>')
.css(this.cursorStyles)
.insertAfter(this._container);
this.cursorVisible = true;
this.blinkTimer = setInterval($.proxy(function() {
this.cursorVisible = !this.cursorVisible;
this._cursor.animate({
opacity: this.cursorVisible ? 1 : 0
}, 100);
}, this), this.blinkDelay);
},
/**
* Triggers event
* #param {String} event
* #return {Typist}
*/
fire: function(event) {
this._element.trigger(event, this);
return this;
},
/**
* New line to <br> tag
* #param {String} text
* #return {String}
*/
nl2br: function(text) {
return text.replace(/\n/g, '<br>');
},
/**
* <br> tag to new line
* #param {String} html
* #return {String}
*/
br2nl: function(html) {
return html.replace(/<br.*?>/g, '\n');
},
/**
* Removes given number of characters
* #param {Number} length
* #param {Function} [callback]
*/
remove: function(length, callback) {
if (length <= 0) {
callback();
this.timer = null;
return this
.fire('end_remove.typist')
.type();
}
var text = this._container.html();
length--;
text = this.br2nl(text);
text = text.substr(0, text.length - 1);
text = this.nl2br(text);
this.timer = setTimeout($.proxy(function() {
this._container.html(text);
this.remove(length, callback);
}, this), this.delay);
},
/**
* Adds given text character by character
* #param {String|Array} text
*/
step: function(text, callback) {
if (typeof text === 'string') {
text = text.split('').map(function(v){return v.replace(' ',' ');});
}
if (!text.length) {
callback();
this.timer = null;
return this
.fire('end_type.typist')
.type();
}
var character = text.shift();
//character = $('<div>').text(character).html();
character = this.nl2br(character);
this.timer = setTimeout($.proxy(function() {
this._container.html(this._container.html() + character);
this.step(text, callback);
}, this), this.delay);
},
/**
* Stops all animations and removes cursor
* #return {[type]} [description]
*/
stop: function() {
clearInterval(this.blinkTimer);
this.blinkTimer = null;
if (this._cursor) {
this._cursor.remove();
this._cursor = null;
}
clearTimeout(this.timer);
this.timer = null;
},
/**
* Gets and invokes tasks from queue
*/
type: function() {
if (this.timer) {
return;
}
if (!this._container) {
this._container = $('<span>');
if (this.typeFrom === 'start') {
this._element.prepend(this._container);
} else {
this._element.append(this._container);
}
}
if (this.cursor) {
this.addCursor();
}
var item = this.queue.shift() || {},
callback = $.proxy(item.callback || $.noop, this);
if (item.delay) {
this
.fire('start_pause.typist')
.timer = setTimeout($.proxy(function() {
callback();
this.timer = null;
this
.fire('end_pause.typist')
.type();
}, this), item.delay);
return;
} else if (item.remove) {
this
.fire('start_remove.typist')
.remove(item.remove, callback);
return;
} else if (item.stop) {
this.stop();
return;
}
if (!item.text) {
return;
}
this
.fire('start_type.typist')
.step(item.text, callback);
}
};
}));
$('.typist')
.typist({
speed: 12,
text: 'Hello!\n'
})
.typistAdd('It working!');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="typist"></p>

jQuery Ajax call in an unexpected loop

I'm developing an application where I load content using ajax calls. But in a certain situation, I get an ajax call on a loop but it ends in random iterations. Below is the sequential code from the a double click event to the final ajax call.
//I use the double click event to fire the function
$('.item-container').on('click', '.item', function(e) {
var posX = e.pageX;
var posY = e.pageY;
var element = $(this);
var that = this;
setTimeout(function() {
var dblclick = parseInt($(that).data('double'), 10);
if (dblclick > 0) {
$(that).data('double', dblclick-1);
} else {
showToolTip(posX,posY,element);
}
}, 300);
}).on('dblclick', '.item', function(e) {
$(this).data('double', 2);
addItemsToBill(this);
});
function addItemsToBill(selectedItem) {
var element = null;
$(".bill-item-list").each(function() {
if ($(this).css("display") == "block") {
element = $(this);
return;
}
});
if (element == null) {
getSystemMessage(BILL_SELECT_VALIDATION_MSG);
return;
}
if ($('#open-tab .bill-list').find(".bill-item-list").length == 0
&& $(element).find('.bill-item').length == 0) {
getSystemMessage(BILL_SELECT_VALIDATION_MSG);
return;
}
var bill = $('.sample-elements').find('.bill-item');
var clone = bill.clone();
var itemId = $(selectedItem).attr('id');
var billId = $(element).parent().attr('id');
if ($(selectedItem).attr('isopen') == '1') {
$('.open-item-popup-overlay').lightbox_me( {
centered : true,
destroyOnClose : false,
});
$('.itmRate').val('');
$('#itmAmt').html('0.00');
$('.itemQty').text('1');
$('.itm-cancel').click(function() {
$('.open-item-popup-overlay').trigger('close');
});
//when the first run goes it's fine, bt after that, the looping starts from here. I double checked whether there is another code segment which fires this but there are none.
$('.itm-ok').click(function(){
if ($('.itmRate').val() == "") {
getSystemMessage(AMOUNT_INPUT);
return;
}
//This function gets iterated multiple times if I run the same event for multiple times.
//The function is only used in this two locations and no any other places.
billItemDrop(itemId, billId, clone, selectedItem, null,element, 1, true);
return false;
})
} else {
billItemDrop(itemId, billId, clone, selectedItem, null, element, 0,true);
}
}
function billItemDrop(itemId, billId, billItemClone, doubleClickedItem,draggableHelper, catchElement, isOpen, isDoubleClick) {
var openItemQuantity = stringToDouble($('.itemQty').val());
var openItemRate = stringToDouble($('.itmRate').val());
var openItemAmount = openItemQuantity * openItemRate;
var ajaxObject = 'itemId=' + itemId + '&billId=' + billId + '&qty='
+ openItemQuantity + '&rate=' + openItemRate + '&openItem='
+ isOpen;
ajaxCall(ADD_ITEM_TO_BILL,AJAX_POST,ajaxObject,function(response) {
var responseObject = response.billDetail;
var billTotal = responseObject.billTotal;
var total = response.billDetail.rate * response.billDetail.qty;
// Add Items to the bill failed
if (response.status != "success") {
getSystemMessage(ADD_ITEM_TO_PRINTED_BILL);
return;
}
billItemClone.attr('itemId', responseObject.itemId);
billItemClone.attr('billDetailId', responseObject.billDetailId);
billItemClone.find('.bill-item-name').text(
responseObject.itemName);
billItemClone.find('.bill-item-rate span').text(
responseObject.rate.toFixed(2));
billItemClone.find('.bill-item-amount').text(
total.toFixed(2));
billItemClone.find('.bill-item-qty span').text(responseObject.qty);
if (responseObject.kotBotNo != null) {
billItemClone.find('.kot-bot-num').text(
responseObject.kotBotNo);
}
billItemClone.draggable( {
revert : false,
addClasses : false,
zIndex : 1,
containment : "window",
opacity : 0.5,
cursor : "move",
scroll : false,
helper : function() {
return $(this).clone().appendTo('body').show();
}
});
if (isDoubleClick == true
&& (doubleClickedItem != undefined
|| doubleClickedItem != null || doubelClickedItem != "")) {
billItemClone.find('.bill-item-img img').attr('src',
$(doubleClickedItem).css('background-image'));
} else if (isDoubleClick == false
&& (draggableHelper != undefined
|| drdraggableHelper != null || draggableHelper != "")) {
billItemClone.find('.bill-item-img img').attr('src',
draggableHelper.css('background-image'));
}
if (catchElement.height() > 300) {
catchElement.css('overflow-y', 'scroll');
catchElement.css('height', '320px');
}
if (catchElement.find('.bill-item').length == 0) {
billItemClone.insertBefore(
catchElement.find('.item-drop-point')).hide()
.fadeIn("slow");
} else {
billItemClone.insertBefore(
catchElement.find('.bill-item').first()).hide()
.fadeIn("slow");
}
catchElement.parent().find('.amount')
.html(billTotal.toFixed(2));
if (draggableHelper != undefined || draggableHelper != null) {
draggableHelper.hide('fade', function() {
draggableHelper.remove()
});
}
$('.item-drop-point').removeClass('item-drop-hover');
},false);
}
/**
*
* Function for ajax calls - url : webservice url - type : ajax call type - data :
* data that needs to be passed - callback : function that needs to be executed
*
* when ajax call succeed
*
* #param url
* #param type
* #param data
* #param callback
*/
function ajaxCall(url, type, data, callback,async){
// Assign default value to async if its not specified
if (async===undefined) async = true;
$.ajax({
url: url,
type: type,
data: data,
dataType : "json",
cache: false,
beforeSend: function(x){
if(x && x.overrideMimeType){
x.overrideMimeType("application/json;charset=UTF-8");
}
},
async : async,
success:callback
});
}
The function gets iterated so the ajax call gets iterated too. I used break points to track the iteration but from the point of the click event I couldn't locate where it get's triggered after the first click event.
Is there anything wrong in the code which makes the function iterate. Please help.
Every time you call addItemsToBill(this); you are adding new event handlers to $('.itm-ok')
Try removing the call to billItemDrop(itemId, billId, clone, selectedItem, null,element, 1, true); and replacing it with a console.log('Test' If you find that multiple console logs are being made every time you click the issue is that every click is being handled by multiple handlers. If that is the case and you really want to use this structure you will have to unbind you click handler before rebinding it.
$('.itm-ok').off('click')
$('.itm-ok').on('click',function(){
if ($('.itmRate').val() == "") {
getSystemMessage(AMOUNT_INPUT);
return;
}
//This function gets iterated multiple times if I run the same event for multiple times.
//The function is only used in this two locations and no any other places.
billItemDrop(itemId, billId, clone, selectedItem, null,element, 1, true);
return false;
})

Categories

Resources