JS: Create new unique instance of my plugin - javascript

I have created a JavaScript plugin, I'd like to have the potential to use this multiple times, however, having it called one the page twice like follows doesn't seem to make a new instance:
var lightbox = new MarvLightbox({
imagesContainer: 'detail-hdr__detail-images',
limit: 11
});
var lightbox2 = new MarvLightbox({
imagesContainer: 'image-grid-2',
galleryFolder: 2,
mobileFolder: 0
});
The following code seems to be returning the same values, so for example, if I click an image to set the c value within the state object then it's changed for instance lightbox2 AND lightbox.
MarvLightbox.prototype.current = function() {
console.log(state);
};
I have tried to remove any none required code from my plugin. But what I'm basically doing is:
1. Compile a list of the images within a container.
2. Generate HTML from a JSON string.
3. Assigning click events to the images, and then when clicked they open the lightbox and change the image etc.
(function() {
var instance = null,
container;
// Constructor
this.MarvLightbox = function() {
instance = this;
// Create global variables
this.images_count = -1;
this.active = false;
this.error = false;
this.debug = false;
// Define option defaults
var defaults = {
activeClass: 'marvLightbox',
appendTo: '#wrapper',
imagesContainer: null,
thumbClass: null,
lightboxId: 'marvLightbox',
galleryFolder: null,
mobileFolder: null,
showAlt: true,
showMax: true,
limit: null,
html: '{ "div": { "id": "{lightboxId}", "0": { "div": { "class": "{lightboxId}__container", "data-click": "EventClose", "0": { "div": { "class": "{lightboxId}__controls {lightboxId}__controls--top", "0": { "div": { "class": "{lightboxId}__eschint", "content": "Press <span>ESC</span> to close" } }, "1": { "div": { "class": "{lightboxId}__close", "data-click": "EventClose" } } } }, "1": { "div": { "class": "{lightboxId}__image", "0": { "img": { "src": "", "class": "responsive-img image", "data-click": "EventRight" } } } }, "2": { "div": { "class": "{lightboxId}__controls {lightboxId}__controls--bottom", "3": { "div": { "class": "{lightboxId}__left", "data-click": "EventLeft", "data-hover": "EventClass(#{lightboxId}, toggle: left)" } }, "4": { "div": { "class": "{lightboxId}__right", "data-click": "EventRight", "data-hover": "EventClass(#{lightboxId}, toggle: right)" } }, "5": { "div": { "class": "{lightboxId}__alt" } }, "6": { "div": { "class": "{lightboxId}__num" } } } } } } } }'
};
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(defaults, arguments[0]);
}
this.options.html = this.options.html.replace(/\{(lightboxId)}/g, this.options.lightboxId);
// this.options.limit = this.options.limit + 1;
// Check if debugging is enabled
(function() {
var args = document.querySelectorAll('[data-external-arg]');
if(args.length > 0 && args[0].src.indexOf('marv.lightbox') !== -1) {
if (args[0].dataset.externalArg === 'debug') instance.debug = true;
}
}());
// Initialise plugin
this.init();
};
// Debugging messages
var debug = function(code, arg) {
var args = function(n) {
if (arg === undefined) {
return '[arg undefined]';
}
if (arg[n] === undefined) {
return '[arg undefined]';
} else {
return arg[n];
}
};
var messages = function(code, argument) {
return [
'marv.lightbox(debug): Debugging mode is on, make sure you turn this off before you launch',
'marv.lightbox(debug): HTMLElement(' + args(0) + ') with the name of ' + args(1) + ' has been detected',
'marv.lightbox(debug): Found '+ arg + ' image/s within your Lightbox container ',
'marv.lightbox(debug): ' + args(0) + ' key pressed, changing current (' + args(1) + '), number of images (' + args(2) + ')',
'marv.lightbox(debug): Current is set to null, closing lightbox',
'marv.lightbox(debug): Inserting Lightbox within HTMLElement(' + args(1) + '), using ' + args(0),
'marv.lightbox(debug): 1 image found, don\'t add previous/next arrows, don\'t show numbers either',
'marv.lightbox(debug): showAlt set to false, don\'t display alt text',
'marv.lightbox(debug): showMax set to false, don\'t display numbers below alt text',
'marv.lightbox(debug): Reverting to mobile version (' + instance.options.mobileFolder + ') of images',
'marv.lightbox(debug): Over-riding to ' + instance.options.galleryFolder + ' version of images'
][code];
};
if (instance.debug === true)
console.log(messages(code, arg));
};
// Error messages
var error = function(code, arg) {
var args = function(n) {
if (arg === undefined) {
return '[arg undefined]';
}
if (arg[n] === undefined) {
return '[arg undefined]';
} else {
return arg[n];
}
};
var messages = function(code, argument) {
return [
'marv.lightbox(error): I need to know which images to use... add { imagesContainer: "id/class" } to the plugin initialization',
'marv.lightbox(error): The HTML structure provided appears to have an error: ' + arg,
'marv.lightbox(error): Issue compiling list of images, speak to Dev, error: ' + arg,
'marv.lightbox(error): Your going to need some images for this to work... make sure they have the class: ' + instance.options.thumbClass,
'marv.lightbox(error): I was unable to find an element which matches ' + instance.options.imagesContainer + ', please double check this',
'marv.lightbox(error): I was unable to find a container with the name of ' + args[0],
'marv.lightbox(error): EventListener with arguments passed, had an issue seperating arguments, check formatting',
'marv.lightbox(error): Unable to apply class event to element, please check your attribute',
'marv.lightbox(error): You have attempted to over-ride the images folder with a size that doesn\'t exist, please choose a size between 0 and ' + arg
][code];
};
console.log(messages(code, arg));
};
var imageSizes = function(e) {
var sizes = [ '344x258', 'full', 'large1' ];
if (sizes[e] !== undefined) {
return sizes[e];
}
error(8, sizes.length);
};
// Initilise the plugin
MarvLightbox.prototype.init = function() {
if (this.options.imagesContainer === null) {
error(0);
return;
}
container = (instance.options.imagesContainer).objectType();
if (container === null || container === undefined) {
error(4);
return;
}
debug(0);
setupLightbox();
};
// Generate HTML from JSON
function buildHTML(json) {
"use strict";
var handleAttribute = function(element, attribute, value) {
if (value instanceof HTMLElement) {
return element.appendChild(value);
}
switch (attribute) {
case 'class':
case 'src':
case 'id':
case 'data-click':
case 'data-hover':
return element.setAttribute(attribute, value);
case 'content':
element.innerHTML = value;
return;
// other keys...
default:
console.log(element.tagName, attribute, value);
}
};
var htmlReviver = function(key, value) {
// parse as element
if (isNaN(key) && typeof value === 'object') {
var element = document.createElement(key);
var subValue;
for (var attribute in value) {
handleAttribute(element, attribute, value[attribute]);
}
return element;
// move element from { index: { tagName: Element } } to { index: Element }
} else if (!isNaN(key)) {
return value[Object.keys(value)[0]];
// leave property alone
} else {
return value;
}
};
try {
var htmlObject = JSON.parse(json, htmlReviver);
return htmlObject;
} catch (e) {
error(1, e);
}
}
// Manage item change
var images_compiled;
var state = {
c: null,
altValue: null,
maxValue: null,
get current() { return this.c; },
get max() { return this.maxValue; },
get alt() { return this.altValue; },
set max(e) { this.maxValue = e; },
set alt(e) { this.altValue = e; },
set current(e) {
if (this.c !== null) {
// Remove class from current
(images_compiled[this.c].target).classList.remove('expand');
}
if (e === null) {
debug(4);
// Collapse Lightbox
if (document.getElementById(instance.options.lightboxId)) {
(images_compiled[this.c].target).classList.remove('expand');
document.getElementsByTagName('body')[0].classList.remove(instance.options.activeClass);
(document.getElementById(instance.options.lightboxId).parentNode).removeChild(document.getElementById(instance.options.lightboxId));
}
this.c = e;
return;
}
// Change current element, update lightbox
this.c = e;
var lightbox = document.getElementById(instance.options.lightboxId),
res;
// Check lightbox exists, if so change the image src
if (lightbox) {
var image = images_compiled[e].image.src;
if (instance.options.galleryFolder !== null) {
var filter = image.match(/([^\/]*)/g).filter(Boolean);
image = image.replace(filter[filter.length - 2], imageSizes(instance.options.galleryFolder));
}
lightbox.getElementsByTagName('img')[0].src = image;
if (instance.options.showAlt) {
debug(7);
lightbox.getElementsByClassName(instance.options.lightboxId + '__alt')[0].innerHTML = images_compiled[e].alt;
}
if (instance.options.showMax && this.max > 1) {
debug(8);
lightbox.getElementsByClassName(instance.options.lightboxId + '__num')[0].innerHTML = (images_compiled[e].index + 1) + '/' + this.max;
}
} else {
res = generateExpand(images_compiled[e]);
}
// Add active class
if (res) {
(images_compiled[e].target).classList.add('expand');
document.getElementsByTagName('body')[0].classList.add(instance.options.lightboxId);
}
}
};
// Setup light box
function setupLightbox() {
var images;
images = container.getElementsByTagName('img');
if (instance.options.limit !== null) {
var tmp = [];
for (var i = 0, length = images.length; i < length; i++) {
if (i < instance.options.limit) {
tmp.push(images[i]);
}
}
images = tmp;
}
if (images.length < 1 || images === undefined) {
error(3);
return;
}
try {
images_compiled = Array.from(images, function(el) {
// Generate array of objects containing image information
instance.images_count++;
return {
target: function() {
if (el.parentElement.nodeName === 'A') {
return el.parentElement;
}
return el;
}(),
index: instance.images_count,
alt: ((el.alt) ? el.alt : ''),
image: function() {
// If class put on an A link then find the image
if (el.tagName === 'A') {
return el.getElementsByTagName('img')[0];
} else {
return el;
}
}()
};
});
} catch(e) {
// Issue with generating array
error(2, e);
}
debug(2, images_compiled.length);
// Add image click event
images_compiled.forEach(function(el) {
if (el !== null) {
var elm = el.target;
elm.addEventListener('click', function(e) {
if (elm.nodeName === 'A') {
e.preventDefault();
}
instance.active = true;
if (state.current === el.index) {
state.current = null;
return;
}
state.current = el.index;
state.alt = el.alt;
});
}
});
state.max = images_compiled.length;
}
function generateExpand(img) {
// Generate lightbox HTML and append
var html = buildHTML(instance.options.html);
instance.events = {
EventClose: function(evt) {
if (evt !== undefined) {
evt.stopPropagation();
}
instance.active = false;
state.current = null;
},
EventLeft: function(evt) {
if (evt !== undefined) {
evt.stopPropagation();
}
if (state.current !== 0 && state.max > 1) {
state.current = state.current - 1;
} else {
state.current = instance.images_count;
}
},
EventRight: function(evt) {
if (evt !== undefined) {
evt.stopPropagation();
}
if (state.current !== instance.images_count && state.max > 1) {
state.current = state.current + 1;
} else {
state.current = 0;
}
},
EventClass: function(evt) {
var arg = (evt.dataset.hover).replace(/ /g,''),
args = (arg.match(/[^(]*/g).filter(Boolean))[1].match(/(?:([^,()]+)?)+/g).filter(Boolean),
target = args[0].objectType(),
action = args[1].match(/^(.*):(.*)/).filter(Boolean);
switch(action[1]) {
case 'add':
// Add class
target.classList.add(action[2]);
break;
case 'remove':
// Remove class
target.classList.remove(action[2]);
break;
case 'toggle':
target.classList.add(action[2]);
evt.addEventListener('mouseout', function() {
target.classList.remove(action[2]);
});
break;
default:
// Error
error(7);
break;
}
}
};
// Lightbox is active
instance.active = true;
// Assign event listeners
Array.prototype.forEach.call(html.querySelectorAll('[data-click]'), function (e) {
e.addEventListener('click', instance.events[eventName(e.dataset.click)]);
});
Array.prototype.forEach.call(html.querySelectorAll('[data-hover]'), function (e) {
e.addEventListener('mouseover', function() { instance.events[eventName(e.dataset.hover)](e); });
});
// Insert lightbox into website
var appendTo = (instance.options.appendTo).objectType();
if (appendTo === null || (instance.options.appendTo).objectType() === undefined) {
error(5, [instance.options.appendTo]);
return false;
}
debug(5, ['id', instance.options.imagesContainer]);
appendTo.insertBefore(html, appendTo.firstChild);
return true;
}
MarvLightbox.prototype.current = function() {
console.log(state);
};
}());
As I said above though the state object appears to be shared between both instances, it should be unique.

The problem lies at instance i think:
var instance = null;
MarvLightbox = function() {
instance = this;
So whenever a new Lightbox is created, instance points to it. This also applies to asynchronous callback functions which will all point to the last instead of the current instance. You may scope instance locally:
var instance=this;
Same applies to state:
MarvLightbox.prototype.current = function() {
console.log(this.state); //make it an objects property, not global
};

Related

How to replace style property with proxy

I have code like this in my unit tests for jQuery Terminal:
// https://github.com/tmpvar/jsdom/issues/135
Object.defineProperties(window.HTMLElement.prototype, {
offsetLeft: {
get: function() { return parseFloat(window.getComputedStyle(this).marginLeft) || 0; }
},
offsetTop: {
get: function() { return parseFloat(window.getComputedStyle(this).marginTop) || 0; }
},
offsetHeight: {
get: function() { return parseFloat(window.getComputedStyle(this).height) || 0; }
},
offsetWidth: {
get: function() { return parseFloat(window.getComputedStyle(this).width) || 0; }
},
// this will test if setting 1ch change value to 1ch which don't work in jsdom used by jest
style: {
get: function() {
if (this.__style) {
return this.__style;
}
var self = this;
var attr = {};
function set_style_attr() {
var str = Object.keys(attr).map((key) => `${key}: ${attr[key]}`).join(';') + ';';
self.setAttribute('style', str);
}
var mapping = {
backgroundClip: 'background-clip',
className: 'class'
};
var reversed_mapping = {};
Object.keys(mapping).forEach(key => {
reversed_mapping[mapping[key]] = key;
});
return this.__style = new Proxy({}, {
set: function(target, name, value) {
name = mapping[name] || name;
if (!value) {
delete target[name];
delete attr[name];
} else {
attr[name] = target[name] = value;
}
set_style_attr();
return true;
},
get: function(target, name) {
if (name === 'setProperty') {
return function(name, value) {
attr[name] = target[name] = value;
set_style_attr();
};
} else {
return target[name];
}
},
deleteProperty: function(target, name) {
name = reversed_mapping[name] || name;
delete target[name];
delete attr[name];
set_style_attr();
}
});
}
}
});
It works for 1ch attribute in my tests that look like this:
it('should handle wider characters without formatting', function() {
var input = 'ターミナルウィンドウは黒[[;;]です]';
var string = $.terminal.format(input, {char_width: 7});
expect(string).toEqual('<span style="width: 24ch"><span style="widt'+
'h: 24ch">ターミナルウィンドウは黒</span></span'+
'><span style="width: 4ch" data-text="です">'+
'<span style="width: 4ch">です</span></span>');
});
If I don't use my Proxy I got width in pixels, because I have code like this to check if ch is supported in my code:
var agent = window.navigator.userAgent;
var is_IE = /MSIE|Trident/.test(agent) || /rv:11.0/i.test(agent);
var is_IEMobile = /IEMobile/.test(agent);
// -------------------------------------------------------------------------
var is_ch_unit_supported = (function() {
if (is_IE && !is_IEMobile) {
return false;
}
var div = document.createElement('div');
div.style.width = '1ch';
return div.style.width === '1ch';
})();
The problem is when I want to check the style property to get some value, like this test:
it('should find inside formatting', function() {
term.less(big_text.concat(['[[;red;]foo bar baz]']));
search('bar');
var spans = term.find('[data-index="0"] > div:first-child span');
['foo ', 'bar', ' baz'].forEach(function(string, i) {
expect(a0(spans.eq(i).text())).toEqual(string);
});
[true, false, true].forEach(function(check, i) {
console.log(spans.get(i).style.getPropertyValue('color'));
expect([i, !!spans.get(i).attr('style').match(/color:\s*red/)]).toEqual([i, check]);
});
});
I've tried:
spans.get(i).style.getPropertyValue('color')
This return error that's not a function and
spans.get(i).attr('style')
is undefined. This also don't work
spans.get(i).getAttribute('style')
which should be the same and the one before.
Is there a way to have ch unit support check work but in same way getting values from style attribute as well?
I'm using jest framework that use jsDom I running my tests from Node.
I've tried to create getPropertyValue function in get trap for the proxy but I don't know how to get original function so I can call it.
So to sum up, I need solution in jsDOM that allow to set width to 1ch and get that value back (so my code don't change), and that should work when creating new HTMLElement in DOM and get it's value out of, It don't need to be style object with props it can be style property as string. Alternative solution is to test if ch unit is supported that will work in jsDOM.
I've solved the issue by temporarily disabling getter when accessing properties:
(function() {
var style_descriptor = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'style');
Object.defineProperties(window.HTMLElement.prototype, {
offsetLeft: {
get: function() { return parseFloat(window.getComputedStyle(this).marginLeft) || 0; }
},
offsetTop: {
get: function() { return parseFloat(window.getComputedStyle(this).marginTop) || 0; }
},
offsetHeight: {
get: function() { return parseFloat(window.getComputedStyle(this).height) || 0; }
},
offsetWidth: {
get: function() { return parseFloat(window.getComputedStyle(this).width) || 0; }
},
// this will test if setting 1ch change value to 1ch which don't work in jsdom used by jest
style: {
get: function getter() {
if (this.__style) {
return this.__style;
}
var self = this;
var attr = {};
function set_style_attr() {
var str = Object.keys(attr).map((key) => `${key}: ${attr[key]}`).join(';') + ';';
self.setAttribute('style', str);
}
var mapping = {
backgroundClip: 'background-clip',
className: 'class'
};
var reversed_mapping = {};
Object.keys(mapping).forEach(key => {
reversed_mapping[mapping[key]] = key;
});
function disable(fn) {
// temporary disable proxy
Object.defineProperty(window.HTMLElement.prototype, "style", style_descriptor);
var ret = fn();
Object.defineProperty(window.HTMLElement.prototype, "style", {
get: getter
});
return ret;
}
return this.__style = new Proxy({}, {
set: function(target, name, value) {
name = mapping[name] || name;
if (!value) {
delete target[name];
delete attr[name];
} else {
attr[name] = target[name] = value;
}
set_style_attr();
disable(function() {
self.style[name] = name;
});
return true;
},
get: function(target, name) {
if (name === 'setProperty') {
return function(name, value) {
attr[name] = target[name] = value;
set_style_attr();
};
} else if (target[name]) {
return target[name];
} else {
return disable(function() {
return self.style[name];
});
}
},
deleteProperty: function(target, name) {
name = reversed_mapping[name] || name;
delete target[name];
delete attr[name];
disable(function() {
delete self.style[name];
});
set_style_attr();
}
});
}
}
});
})();

.JS not working on Chrome Extension

I'm working on my first Chrome extension to help me on my job.
Well, I have a HTML file as the extension pop-up and a .JS file to do all the magic. When I run the HTML in the browser, it works fine (it has to autocomplete the keywords are being typed) but when I run it as a Chrome extension it does not work at all.
Any idea?
JSON file
{
"name": "Autocompleter",
"version": "0",
"description": "It completes!",
"manifest_version": 2,
"browser_action": {
"name": "project with jquery",
"icons": ["icon.png"],
"default_icon": "icon.png",
"default_popup": "home.html"
},
"content_scripts": [ {
"js": [ "awesomplete.js" ],
"matches": [ "http://*/*", "https://*/*"]
}]
}
HTML
<!doctype html>
<html lang="en">
<head>
<script src="awesomplete.js"></script>
<link rel="stylesheet" href="awesomplete.css" />
</head>
<body>
<section id="multiple-values">
<input data-list="Brazil, Argentina, Uruguai, Paraguai, Nova Zelândia, Canadá" data-multiple data-minchars="1" /></label>
<pre class="language-javascript"><code><script>new Awesomplete('input[data-multiple]', {
filter: function(text, input) {
return Awesomplete.FILTER_CONTAINS(text, input.match(/[^,]*$/)[0]);
},
replace: function(text) {
var before = this.input.value.match(/^.+,\s*|/)[0];
this.input.value = before + text + ", ";
}
});</script></code></pre>
</section>
</body>
</html>
.JS file
(function () {
var _ = function (input, o) {
var me = this;
// Setup
this.isOpened = false;
this.input = $(input);
this.input.setAttribute("autocomplete", "off");
this.input.setAttribute("aria-autocomplete", "list");
o = o || {};
configure(this, {
minChars: 2,
maxItems: 10,
autoFirst: false,
data: _.DATA,
filter: _.FILTER_CONTAINS,
sort: _.SORT_BYLENGTH,
item: _.ITEM,
replace: _.REPLACE
}, o);
this.index = -1;
// Create necessary elements
this.container = $.create("div", {
className: "awesomplete",
around: input
});
this.ul = $.create("ul", {
hidden: "hidden",
inside: this.container
});
this.status = $.create("span", {
className: "visually-hidden",
role: "status",
"aria-live": "assertive",
"aria-relevant": "additions",
inside: this.container
});
// Bind events
$.bind(this.input, {
"input": this.evaluate.bind(this),
"blur": this.close.bind(this, { reason: "blur" }),
"keydown": function(evt) {
var c = evt.keyCode;
// If the dropdown `ul` is in view, then act on keydown for the following keys:
// Enter / Esc / Up / Down
if(me.opened) {
if (c === 13 && me.selected) { // Enter
evt.preventDefault();
me.select();
}
else if (c === 27) { // Esc
me.close({ reason: "esc" });
}
else if (c === 38 || c === 40) { // Down/Up arrow
evt.preventDefault();
me[c === 38? "previous" : "next"]();
}
}
}
});
$.bind(this.input.form, {"submit": this.close.bind(this, { reason: "submit" })});
$.bind(this.ul, {"mousedown": function(evt) {
var li = evt.target;
if (li !== this) {
while (li && !/li/i.test(li.nodeName)) {
li = li.parentNode;
}
if (li && evt.button === 0) { // Only select on left click
evt.preventDefault();
me.select(li, evt.target);
}
}
}});
if (this.input.hasAttribute("list")) {
this.list = "#" + this.input.getAttribute("list");
this.input.removeAttribute("list");
}
else {
this.list = this.input.getAttribute("data-list") || o.list || [];
}
_.all.push(this);
};
_.prototype = {
set list(list) {
if (Array.isArray(list)) {
this._list = list;
}
else if (typeof list === "string" && list.indexOf(",") > -1) {
this._list = list.split(/\s*,\s*/);
}
else { // Element or CSS selector
list = $(list);
if (list && list.children) {
var items = [];
slice.apply(list.children).forEach(function (el) {
if (!el.disabled) {
var text = el.textContent.trim();
var value = el.value || text;
var label = el.label || text;
if (value !== "") {
items.push({ label: label, value: value });
}
}
});
this._list = items;
}
}
if (document.activeElement === this.input) {
this.evaluate();
}
},
get selected() {
return this.index > -1;
},
get opened() {
return this.isOpened;
},
close: function (o) {
if (!this.opened) {
return;
}
this.ul.setAttribute("hidden", "");
this.isOpened = false;
this.index = -1;
$.fire(this.input, "awesomplete-close", o || {});
},
open: function () {
this.ul.removeAttribute("hidden");
this.isOpened = true;
if (this.autoFirst && this.index === -1) {
this.goto(0);
}
$.fire(this.input, "awesomplete-open");
},
next: function () {
var count = this.ul.children.length;
this.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) );
},
previous: function () {
var count = this.ul.children.length;
var pos = this.index - 1;
this.goto(this.selected && pos !== -1 ? pos : count - 1);
},
// Should not be used, highlights specific item without any checks!
goto: function (i) {
var lis = this.ul.children;
if (this.selected) {
lis[this.index].setAttribute("aria-selected", "false");
}
this.index = i;
if (i > -1 && lis.length > 0) {
lis[i].setAttribute("aria-selected", "true");
this.status.textContent = lis[i].textContent;
$.fire(this.input, "awesomplete-highlight", {
text: this.suggestions[this.index]
});
}
},
select: function (selected, origin) {
if (selected) {
this.index = $.siblingIndex(selected);
} else {
selected = this.ul.children[this.index];
}
if (selected) {
var suggestion = this.suggestions[this.index];
var allowed = $.fire(this.input, "awesomplete-select", {
text: suggestion,
origin: origin || selected
});
if (allowed) {
this.replace(suggestion);
this.close({ reason: "select" });
$.fire(this.input, "awesomplete-selectcomplete", {
text: suggestion
});
}
}
},
evaluate: function() {
var me = this;
var value = this.input.value;
if (value.length >= this.minChars && this._list.length > 0) {
this.index = -1;
// Populate list with options that match
this.ul.innerHTML = "";
this.suggestions = this._list
.map(function(item) {
return new Suggestion(me.data(item, value));
})
.filter(function(item) {
return me.filter(item, value);
})
.sort(this.sort)
.slice(0, this.maxItems);
this.suggestions.forEach(function(text) {
me.ul.appendChild(me.item(text, value));
});
if (this.ul.children.length === 0) {
this.close({ reason: "nomatches" });
} else {
this.open();
}
}
else {
this.close({ reason: "nomatches" });
}
}
};
// Static methods/properties
_.all = [];
_.FILTER_CONTAINS = function (text, input) {
return RegExp($.regExpEscape(input.trim()), "i").test(text);
};
_.FILTER_STARTSWITH = function (text, input) {
return RegExp("^" + $.regExpEscape(input.trim()), "i").test(text);
};
_.SORT_BYLENGTH = function (a, b) {
if (a.length !== b.length) {
return a.length - b.length;
}
return a < b? -1 : 1;
};
_.ITEM = function (text, input) {
var html = input === '' ? text : text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "<mark>$&</mark>");
return $.create("li", {
innerHTML: html,
"aria-selected": "false"
});
};
_.REPLACE = function (text) {
this.input.value = text.value;
};
_.DATA = function (item/*, input*/) { return item; };
// Private functions
function Suggestion(data) {
var o = Array.isArray(data)
? { label: data[0], value: data[1] }
: typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data };
this.label = o.label || o.value;
this.value = o.value;
}
Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", {
get: function() { return this.label.length; }
});
Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () {
return "" + this.label;
};
function configure(instance, properties, o) {
for (var i in properties) {
var initial = properties[i],
attrValue = instance.input.getAttribute("data-" + i.toLowerCase());
if (typeof initial === "number") {
instance[i] = parseInt(attrValue);
}
else if (initial === false) { // Boolean options must be false by default anyway
instance[i] = attrValue !== null;
}
else if (initial instanceof Function) {
instance[i] = null;
}
else {
instance[i] = attrValue;
}
if (!instance[i] && instance[i] !== 0) {
instance[i] = (i in o)? o[i] : initial;
}
}
}
// Helpers
var slice = Array.prototype.slice;
function $(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
}
function $$(expr, con) {
return slice.call((con || document).querySelectorAll(expr));
}
$.create = function(tag, o) {
var element = document.createElement(tag);
for (var i in o) {
var val = o[i];
if (i === "inside") {
$(val).appendChild(element);
}
else if (i === "around") {
var ref = $(val);
ref.parentNode.insertBefore(element, ref);
element.appendChild(ref);
}
else if (i in element) {
element[i] = val;
}
else {
element.setAttribute(i, val);
}
}
return element;
};
$.bind = function(element, o) {
if (element) {
for (var event in o) {
var callback = o[event];
event.split(/\s+/).forEach(function (event) {
element.addEventListener(event, callback);
});
}
}
};
$.fire = function(target, type, properties) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true );
for (var j in properties) {
evt[j] = properties[j];
}
return target.dispatchEvent(evt);
};
$.regExpEscape = function (s) {
return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
};
$.siblingIndex = function (el) {
/* eslint-disable no-cond-assign */
for (var i = 0; el = el.previousElementSibling; i++);
return i;
};
// Initialization
function init() {
$$("input.awesomplete").forEach(function (input) {
new _(input);
});
}
// Are we in a browser? Check for Document constructor
if (typeof Document !== "undefined") {
// DOM already loaded?
if (document.readyState !== "loading") {
init();
}
else {
// Wait for it
document.addEventListener("DOMContentLoaded", init);
}
}
_.$ = $;
_.$$ = $$;
// Make sure to export Awesomplete on self when in a browser
if (typeof self !== "undefined") {
self.Awesomplete = _;
}
// Expose Awesomplete as a CJS module
if (typeof module === "object" && module.exports) {
module.exports = _;
}
return _;
}());
Prints:
Working
Not working (as a Chrome Extension)

Find object that corresponds - getting a comment system to work?

This is how everything should work when the iFrameOn function runs:
Turn the designMode of all iFrames on
Find the three "buttons" ('a' link elements) - that when clicked affect its corresponding iFrame - depending on its className
Put those "buttons" into a multidimensional array/object i.e target.rtfID.x
Whenever a "button" is clicked, find its corresponding iFrame through the object and send the iFrame's id as an argument for another function.
Currently, however, whenever any of the buttons are clicked they all affect the same iFrame. I realize this is likely due to my usage of the loops, but I can't figure out how to make it all work. There are no errors in the console.
function iFrameOn() {
var rtfContainer, rtContainer, richTxt, richTxtId,
rtf = document.querySelectorAll('div > form > iframe'), //Rich Text Field
newPost = document.getElementById('richTextField').contentDocument.body,
target = {}, rtfIndex = 0;
//Turn iFrames On
while (rtfIndex < rtf.length) {
rtfID = rtf[rtfIndex].id;
if (rtf[rtfIndex].contentDocument.designMode != 'On') {
rtf[rtfIndex].contentDocument.designMode = 'On';
}
newPost.innerHTML = "<i style=\"color:#DDDDDD;\">What's up?</i>";
newPost.addEventListener('blur', function() {
if (newPost.innerHTML == '') {
newPost.innerHTML = "<i style=\"color:#DDDDDD;\">What's up?</i>";
}
}, false);
document.getElementById('richTextField').contentWindow.addEventListener(
'focus',
function() {
if (newPost.innerHTML == "<i style=\"color:#DDDDDD;\">What's up?</i>") {
newPost.innerHTML = '';
}
},
false
);
rtContainer = rtf[rtfIndex].nextElementSibling; //Next Element Sibling should be a div
console.log('rtContainer is: '+rtContainer);
richTxt = rtContainer.childNodes;
console.log('richTxt is: '+richTxt);
for (var i = 0; i < richTxt.length; i++) {
if (richTxt[i].nodeType != 1 ||
(richTxt[i].nodeType == 1 &&
(richTxt[i].className == 'submit_button sbmtPost'
|| richTxt[i].className == "")
)
) {
continue;
}
richTxtId = richTxt[i].id;
target.rtfID = {};
switch (richTxt[i].className) {
case 'richText bold':
if (target.rtfID.bold != richTxtId) {
target.rtfID.bold = richTxtId;
console.log(target.rtfID.bold+' is associated with: '+rtfID);
}
break;
case 'richText underline':
if (target.rtfID.underline != richTxtId) {
target.rtfID.underline = richTxtId;
console.log(target.rtfID.underline+' is associated with: '+rtfID);
}
break;
case 'richText italic':
if (target.rtfID.italic != richTxtId) {
target.rtfID.italic = richTxtId;
console.log(target.rtfID.italic+' is associated with: '+rtfID);
}
break;
default:
console.log('Error with commenting system!');
break;
}
}
var obj = target.rtfID;
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
console.log("prop: " + prop + " value: " + obj[prop]);
switch(prop) {
case 'bold':
document.getElementById(obj[prop]).addEventListener(
'click',
function() {
bold(obj[prop]);
},
false
);
break;
case 'underline':
document.getElementById(obj[prop]).addEventListener(
'click',
function() {
Underline(obj[prop]);
},
false
);
break;
case 'italic':
document.getElementById(obj[prop]).addEventListener(
'click',
function() {
Italic(obj[prop]);
},
false
);
break;
default:
console.log('Error in for...in loop');
}
} else {console.log('error');}
}
rtfIndex++;
}
}
You can do things like this:
function createBolder (val) {
return function () {
bold(val);
};
}
document.getElementById(obj[prop]).addEventListener(
'click',
createBolder(obj[prop]),
false
);
or if you really want to keep things inline:
document.getElementById(obj[prop]).addEventListener(
'click',
(function (val) {
return function () {
bold(val);
};
}(obj[prop])), // This function call executes immediately (i.e., while still
// going through the loop, so the current value of obj[prop]
// will be stored inside this function with its own local copy
// rather than using the value of obj and prop by the time
// the function is executed (probably the completion of the loop))
false
);

Instagram instafeed Image rotator/Slider

This is my first post on StackOverflow and might I say, what a great and helpful resource this is. I have been able to find many answers to my questions and hope to do the same with this one. On to the issue at hand... I am currently using the instafeed.js (http://instafeedjs.com/) script to pull in images from instagram and display them on my site.
What I'm trying to accomplish is to have 9 items display, then if I hit the "next" button, it would replace the current 9 images and load the next 9 images and, if the "previous" button is clicked it would go back and show the previous 9 images.
Currently it only renders one image thumb that scrolls through 9 images then stops. Swapping only the one image at a time. A sample of the current working code with only 1 image displaying can be found here - http://codepen.io/stevenschobert/pen/iHxfw
Here is my JavaScript for the instafeed call:
var count = 1;
var feed;
feed = new Instafeed({
clientId: '68be8b63013048ff81bb4ac8b02b606e',
limit: 9,
resolution: 'standard_resolution',
template: '<img src="{{image}}" /><div class="likes">♥ {{likes}}</div>',
mock: true,
after: function () {
var images = $("#instafeed").find('a');
$.each(images, function(index, image) {
var delay = (index * 75) + 'ms';
$(image).css('-webkit-animation-delay', delay);
$(image).css('-moz-animation-delay', delay);
$(image).css('-ms-animation-delay', delay);
$(image).css('-o-animation-delay', delay);
$(image).css('animation-delay', delay);
$(image).addClass('animated fadeInUp pic-'+count++);
});
},
custom: {
images: [],
currentImage: 0,
showImage: function () {
var result, image;
image = this.options.custom.images[this.options.custom.currentImage];
result = this._makeTemplate(this.options.template, {
model: image,
id: image.id,
link: image.link,
image: image.images[this.options.resolution].url,
caption: this._getObjectProperty(image, 'caption.text'),
likes: image.likes.count,
comments: image.comments.count,
location: this._getObjectProperty(image, 'location.name')
});
$("#instafeed").html(result);
}
},
success: function (data) {
this.options.custom.images = data.data;
this.options.custom.showImage.call(this);
}
});
feed.run();
$(".next").click(function () {
var length, current;
current = feed.options.custom.currentImage;
length = feed.options.custom.images.length;
if (current < length - 1) {
feed.options.custom.currentImage++;
feed.options.custom.showImage.call(feed);
}
});
$(".prev").click(function () {
var length, current;
current = feed.options.custom.currentImage;
length = feed.options.custom.images.length;
if (current > 0) {
feed.options.custom.currentImage--
feed.options.custom.showImage.call(feed);
}
});
My HTML:
<div id="instafeed"></div>
<div class="controls">
<div class="prev"><- prev</div>
<div class="next">next -></div>
</div>
The Instafeed.js code:
(function() { var Instafeed, root; Instafeed = (function() {
function Instafeed(params) {
var option, value;
this.options = {
target: 'instafeed',
get: 'popular',
resolution: 'thumbnail',
sortBy: 'most-recent',
links: true,
limit: 15,
mock: false
};
if (typeof params === 'object') {
for (option in params) {
value = params[option];
this.options[option] = value;
}
}
this.unique = this._genKey();
}
Instafeed.prototype.run = function() {
var header, instanceName, script;
if (typeof this.options.clientId !== 'string') {
if (typeof this.options.accessToken !== 'string') {
throw new Error("Missing clientId or accessToken.");
}
}
if (typeof this.options.accessToken !== 'string') {
if (typeof this.options.clientId !== 'string') {
throw new Error("Missing clientId or accessToken.");
}
}
if ((this.options.before != null) && typeof this.options.before === 'function') {
this.options.before.call(this);
}
if (typeof document !== "undefined" && document !== null) {
script = document.createElement('script');
script.id = 'instafeed-fetcher';
script.src = this._buildUrl();
header = document.getElementsByTagName('head');
header[0].appendChild(script);
instanceName = "instafeedCache" + this.unique;
window[instanceName] = new Instafeed(this.options);
window[instanceName].unique = this.unique;
}
return true;
};
Instafeed.prototype.parse = function(response) {
var anchor, fragment, header, htmlString, image, imageString, images, img, instanceName, reverse, sortSettings, _i, _j, _len, _len1;
if (typeof response !== 'object') {
if ((this.options.error != null) && typeof this.options.error === 'function') {
this.options.error.call(this, 'Invalid JSON data');
return false;
} else {
throw new Error('Invalid JSON response');
}
}
if (response.meta.code !== 200) {
if ((this.options.error != null) && typeof this.options.error === 'function') {
this.options.error.call(this, response.meta.error_message);
return false;
} else {
throw new Error("Error from Instagram: " + response.meta.error_message);
}
}
if (response.data.length === 0) {
if ((this.options.error != null) && typeof this.options.error === 'function') {
this.options.error.call(this, 'No images were returned from Instagram');
return false;
} else {
throw new Error('No images were returned from Instagram');
}
}
if ((this.options.success != null) && typeof this.options.success === 'function') {
this.options.success.call(this, response);
}
if (this.options.sortBy !== 'most-recent') {
if (this.options.sortBy === 'random') {
sortSettings = ['', 'random'];
} else {
sortSettings = this.options.sortBy.split('-');
}
reverse = sortSettings[0] === 'least' ? true : false;
switch (sortSettings[1]) {
case 'random':
response.data.sort(function() {
return 0.5 - Math.random();
});
break;
case 'recent':
response.data = this._sortBy(response.data, 'created_time', reverse);
break;
case 'liked':
response.data = this._sortBy(response.data, 'likes.count', reverse);
break;
case 'commented':
response.data = this._sortBy(response.data, 'comments.count', reverse);
break;
default:
throw new Error("Invalid option for sortBy: '" + this.options.sortBy + "'.");
}
}
if ((typeof document !== "undefined" && document !== null) && this.options.mock === false) {
document.getElementById(this.options.target).innerHTML = '';
images = response.data;
if (images.length > this.options.limit) {
images = images.slice(0, this.options.limit + 1 || 9e9);
}
if ((this.options.template != null) && typeof this.options.template === 'string') {
htmlString = '';
imageString = '';
for (_i = 0, _len = images.length; _i < _len; _i++) {
image = images[_i];
imageString = this._makeTemplate(this.options.template, {
model: image,
id: image.id,
link: image.link,
image: image.images[this.options.resolution].url,
caption: this._getObjectProperty(image, 'caption.text'),
likes: image.likes.count,
comments: image.comments.count,
location: this._getObjectProperty(image, 'location.name')
});
htmlString += imageString;
}
document.getElementById(this.options.target).innerHTML = htmlString;
} else {
fragment = document.createDocumentFragment();
for (_j = 0, _len1 = images.length; _j < _len1; _j++) {
image = images[_j];
img = document.createElement('img');
img.src = image.images[this.options.resolution].url;
if (this.options.links === true) {
anchor = document.createElement('a');
anchor.href = image.images['standard_resolution'].url;
anchor.rel = "lightbox";
anchor.appendChild(img);
fragment.appendChild(anchor);
} else {
fragment.appendChild(img);
}
}
document.getElementById(this.options.target).appendChild(fragment);
}
header = document.getElementsByTagName('head')[0];
header.removeChild(document.getElementById('instafeed-fetcher'));
instanceName = "instafeedCache" + this.unique;
delete window[instanceName];
}
if ((this.options.after != null) && typeof this.options.after === 'function') {
this.options.after.call(this);
}
return true;
};
Instafeed.prototype._buildUrl = function() {
var base, endpoint, final;
base = "https://api.instagram.com/v1";
switch (this.options.get) {
case "popular":
endpoint = "media/popular";
break;
case "tagged":
if (typeof this.options.tagName !== 'string') {
throw new Error("No tag name specified. Use the 'tagName' option.");
}
endpoint = "tags/" + this.options.tagName + "/media/recent";
break;
case "location":
if (typeof this.options.locationId !== 'number') {
throw new Error("No location specified. Use the 'locationId' option.");
}
endpoint = "locations/" + this.options.locationId + "/media/recent";
break;
case "user":
if (typeof this.options.userId !== 'number') {
throw new Error("No user specified. Use the 'userId' option.");
}
if (typeof this.options.accessToken !== 'string') {
throw new Error("No access token. Use the 'accessToken' option.");
}
endpoint = "users/" + this.options.userId + "/media/recent";
break;
default:
throw new Error("Invalid option for get: '" + this.options.get + "'.");
}
final = "" + base + "/" + endpoint;
if (this.options.accessToken != null) {
final += "?access_token=" + this.options.accessToken;
} else {
final += "?client_id=" + this.options.clientId;
}
final += "&count=" + this.options.limit;
final += "&callback=instafeedCache" + this.unique + ".parse";
return final;
};
Instafeed.prototype._genKey = function() {
var S4;
S4 = function() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
};
return "" + (S4()) + (S4()) + (S4()) + (S4());
};
Instafeed.prototype._makeTemplate = function(template, data) {
var output, pattern, varName, varValue, _ref;
pattern = /(?:\{{2})([\w\[\]\.]+)(?:\}{2})/;
output = template;
while (pattern.test(output)) {
varName = output.match(pattern)[1];
varValue = (_ref = this._getObjectProperty(data, varName)) != null ? _ref : '';
output = output.replace(pattern, "" + varValue);
}
return output;
};
Instafeed.prototype._getObjectProperty = function(object, property) {
var piece, pieces;
property = property.replace(/\[(\w+)\]/g, '.$1');
pieces = property.split('.');
while (pieces.length) {
piece = pieces.shift();
if ((object != null) && piece in object) {
object = object[piece];
} else {
return null;
}
}
return object;
};
Instafeed.prototype._sortBy = function(data, property, reverse) {
var sorter;
sorter = function(a, b) {
var valueA, valueB;
valueA = this._getObjectProperty(a, property);
valueB = this._getObjectProperty(b, property);
if (reverse) {
if (valueA > valueB) {
return 1;
} else {
return -1;
}
}
if (valueA < valueB) {
return 1;
} else {
return -1;
}
};
data.sort(sorter.bind(this));
return data;
};
return Instafeed;})(); root = typeof exports !== "undefined" && exports !== null ? exports : window; root.Instafeed = Instafeed;}).call(this);
Any help with this issue would be GREATLY appreciated.
Thank You,
Jason

Javascript tree Node Codes

ı want to use this http://www.jstree.com/documentation/contextmenu
ı can create , delete ,edit nodes but my question is,
how can ı get created node's value or id
how can ı get deleted node's value or id
because i will insert into database.
and here javascript codes
http://static.jstree.com/v.1.0pre/jquery.jstree.js
For Create Process i found this code probably this code belongs to create process under.
// Basic operations: create
create_node: function (obj, position, js, callback, is_loaded) {
obj = this._get_node(obj);
position = typeof position === "undefined" ? "last" : position;
var d = $("<li />"),
s = this._get_settings().core,
tmp;
if (obj !== -1 && !obj.length) { return false; }
if (!is_loaded && !this._is_loaded(obj)) { this.load_node(obj, function () { this.create_node(obj, position, js, callback, true); }); return false; }
this.__rollback();
if (typeof js === "string") { js = { "data": js }; }
if (!js) { js = {}; }
if (js.attr) { d.attr(js.attr); }
if (js.metadata) { d.data(js.metadata); }
if (js.state) { d.addClass("jstree-" + js.state); }
if (!js.data) { js.data = this._get_string("new_node"); }
if (!$.isArray(js.data)) { tmp = js.data; js.data = []; js.data.push(tmp); }
$.each(js.data, function (i, m) {
tmp = $("<a />");
if ($.isFunction(m)) { m = m.call(this, js); }
if (typeof m == "string") { tmp.attr('href', '#')[s.html_titles ? "html" : "text"](m); }
else {
if (!m.attr) { m.attr = {}; }
if (!m.attr.href) { m.attr.href = '#'; }
tmp.attr(m.attr)[s.html_titles ? "html" : "text"](m.title);
if (m.language) { tmp.addClass(m.language); }
}
tmp.prepend("<ins class='jstree-icon'> </ins>");
if (!m.icon && js.icon) { m.icon = js.icon; }
if (m.icon) {
if (m.icon.indexOf("/") === -1) { tmp.children("ins").addClass(m.icon); }
else { tmp.children("ins").css("background", "url('" + m.icon + "') center center no-repeat"); }
}
d.append(tmp);
});
d.prepend("<ins class='jstree-icon'> </ins>");
if (obj === -1) {
obj = this.get_container();
if (position === "before") { position = "first"; }
if (position === "after") { position = "last"; }
}
switch (position) {
case "before": obj.before(d); tmp = this._get_parent(obj); break;
case "after": obj.after(d); tmp = this._get_parent(obj); break;
case "inside":
case "first":
if (!obj.children("ul").length) { obj.append("<ul />"); }
obj.children("ul").prepend(d);
tmp = obj;
break;
case "last":
if (!obj.children("ul").length) { obj.append("<ul />"); }
obj.children("ul").append(d);
tmp = obj;
break;
default:
if (!obj.children("ul").length) { obj.append("<ul />"); }
if (!position) { position = 0; }
tmp = obj.children("ul").children("li").eq(position);
if (tmp.length) { tmp.before(d); }
else { obj.children("ul").append(d); }
tmp = obj;
break;
}
if (tmp === -1 || tmp.get(0) === this.get_container().get(0)) { tmp = -1; }
this.clean_node(tmp);
this.__callback({ "obj": d, "parent": tmp });
if (callback) { callback.call(this, d); }
return d;

Categories

Resources