Modifying Titanium Widgets without using global variables - javascript

I am trying to modify the following titanium Widget.
https://github.com/pablorr18/TiFlexiGrid
It is a photo gallery, where you can fetch remote images and store it in a gallery. See this question for a background to what the problem is:
Modifying widgets in an alloy project
The trouble I am having is that, like the poster in that thread said, I am unable to get the variable passed (in this case image URL) to my controller using callback functions. In Widget.JS, at the bottom of the file, I added the following code:
Widget.xml
<Alloy>
<View id="fgMain">
<Button title="Click me!" onTouchend="buttonClicked"/>
<View id="fgWrapper">
<ScrollView id="fgScrollView"/>
</View>
</View>
</Alloy>
Widget.js
// This will hold our callback
var onClickCallback;
// The button has been clicked, call callback
function buttonClicked(e) {
if(typeof(onClickCallback) === 'function') {
onClickCallback({ type:'clicked!' }); }
}
// Assign our callback
function onClick(callback) {
onClickCallback = callback;
};
// Make the onClick function public
exports.onClick = onClick;
I was then hoping to go into my main app and get the values from the callback function like this:
photoGallery.xml
<Alloy>
<Window>
<Widget id="myWidget" src="myWidget" />
</Window>
</Alloy>
photoGallery.js
// Now we can intercept the click within the widget
// and use the values passed
$.myWidget.onClick(function(e) {
alert(e.type);
});
The trouble is, as the onTouchend event did not fire, I was unable to pass the variable to the controller which inherits the widget as the callback functions are not set.
The original widget.js code is as follows:
exports.createGrid = function(args){
var params = args || {};
//Ti.API.info('Params es ---> '+ JSON.stringify(params));
var columns = params.columns || 4;
var space = params.space || 5;
var data = params.data || {};
var options = params.params || {};
var layout = params.layout || 'gallery';
var screenWidth = params.width || Ti.Platform.displayCaps.getPlatformWidth();
if (OS_ANDROID) {
screenWidth /= Ti.Platform.displayCaps.logicalDensityFactor;
}
var newWidth = screenWidth - space;
var columnWidth = (newWidth / columns) - space;
var frameBGcolor = options.backgroundColor || '#fff';
//ADJUST THE SCROLLVIEW
$.fgScrollView.left = space;
$.fgScrollView.top = space;
$.fgScrollView.right = -1;
$.fgMain.backgroundColor = frameBGcolor;
for (var x=0;x < data.length; x++){
var frame = Ti.UI.createView({
width:columnWidth,
height:columnWidth,
backgroundColor:options.gridColor || '#eee',
top:0,
left:0,
right:space,
bottom:space
});
var overlay = Ti.UI.createView({
width:Ti.UI.FILL,
height:Ti.UI.FILL,
backgroundColor:'transparent',
zIndex:1,
strImage:data[x].image
});
var gridElement;
//TYPE OF LAYOUT
switch(layout){
case('gallery'):
gridElement = Widget.createController('gallery',{
image:data[x].image,
title:data[x].title,
width:columnWidth,
padding:options.padding || 10,
showTitle:options.showTitle || false
}).getView();
overlay.addEventListener('click',function(e){
exports.openModal(e.source.strImage);
});
break;
case('customView'):
gridElement = data[x];
break;
}
frame.add(gridElement);
// This condition makes the overlay not be added if it's not gallery layout.
// It's used to make the custom view, caputre the click method. If not,
// The overlay is on top of it and captures the click.
if(layout == 'gallery')
frame.add(overlay);
$.fgScrollView.add(frame);
};
};
exports.openModal = function(url){
var overlay = Ti.UI.createView({
width:Ti.UI.FILL,
height: Ti.UI.FILL,
backgroundColor:'#000',
opacity:0,
zIndex:100
});
var topView = Ti.UI.createView({
width:Ti.UI.FILL,
height: Ti.UI.FILL,
zIndex:1200,
visible:false
});
//this gets image , adds it to top view
var imgView = Ti.UI.createImageView({
image: url,
width:Ti.UI.SIZE,
height: Ti.UI.SIZE
});
//add it
topView.add(imgView);
$.fgMain.add(overlay);
if (OS_IOS){
//ANIMATION OF OVERLAY
overlay.animate({opacity:0.7,duration:200});
//ANIMATION FOR POP EFFECT
var t = Titanium.UI.create2DMatrix();
t = t.scale(0);
var a = Titanium.UI.createAnimation();
a.transform = t;
a.duration = 200;
$.fgMain.add(topView);
topView.animate(a);
a.addEventListener('complete', function(){
topView.visible = true;
var t2 = Titanium.UI.create2DMatrix();
t2 = t2.scale(1.2);
topView.animate({transform:t2, duration:200},function(e){
var t4 = Titanium.UI.create2DMatrix();
t4 = t4.scale(1.0);
topView.animate({transform:t4, duration:200});
//alert('animation complete');
//hide cancel button
});
});
}
else{
//ANIMATION OF OVERLAY
overlay.animate({opacity:0.7,duration:200},function(e){
topView.visible = true;
$.fgMain.add(topView);
});
}
topView.addEventListener('click',function(e){
if (OS_IOS){
var t3 = Titanium.UI.create2DMatrix();
t3 = t3.scale(1.2);
var a2 = Titanium.UI.createAnimation();
a2.transform = t3;
a2.duration = 200;
topView.animate(a2);
a2.addEventListener('complete', function(){
var t5 = Titanium.UI.create2DMatrix();
t5 = t5.scale(0);
topView.animate({transform:t5, duration:200},function(e){
$.fgMain.remove(topView);
overlay.animate({opacity:0,duration:200},function(e){
$.fgMain.remove(overlay);
});
});
});
}
else{
$.fgMain.remove(topView);
overlay.animate({opacity:0,duration:200},function(e){
$.fgMain.remove(overlay);
});
}
});
};
exports.clearGrid = function(){
$.fgScrollView.removeAllChildren();
};
From looking at it, the only way it seems to get an event handler to register is to put it one of the functions (createGrid) and call it like this:
exports.createGrid in widget.xml
var button = Titanium.UI.createButton({
title : 'Use Picture',
top : 10,
width : 100,
height : 50
});
button.addEventListener('click',function(e)
{
Titanium.API.info(url);
//do not want to store in global variable
Alloy.Globals.urlFromGal = url;
});
//add it
topView.add(imgView);
topView.add(button);
$.fgMain.add(overlay);
However I am not sure how to adapt that code to this:
// This will hold our callback
var onClickCallback;
// The button has been clicked, call callback
function buttonClicked(e) {
if(typeof(onClickCallback) === 'function') {
onClickCallback({ type:'clicked!' }); }
}
// Assign our callback
function onClick(callback) {
onClickCallback = callback;
};
// Make the onClick function public
exports.onClick = onClick;
So that I am able to access the url variable outside of the widget.js scope. I could use GLOBAL Variables - by that store the variable in one instead for this task, however I want to avoid doing things this way.
UPDATE:
Unable to get it working, but I have added the callback into an event handler. Got the event handler to fire, but not sure how to pass the callback data to onClick function:
var onClickCallback;
exports.createGrid = function(args){
var params = args || {};
//Ti.API.info('Params es ---> '+ JSON.stringify(params));
var columns = params.columns || 4;
var space = params.space || 5;
var data = params.data || {};
var options = params.params || {};
var layout = params.layout || 'gallery';
var screenWidth = params.width || Ti.Platform.displayCaps.getPlatformWidth();
if (OS_ANDROID) {
screenWidth /= Ti.Platform.displayCaps.logicalDensityFactor;
}
var newWidth = screenWidth - space;
var columnWidth = (newWidth / columns) - space;
var frameBGcolor = options.backgroundColor || '#fff';
//ADJUST THE SCROLLVIEW
$.fgScrollView.left = space;
$.fgScrollView.top = space;
$.fgScrollView.right = -1;
$.fgMain.backgroundColor = frameBGcolor;
for (var x=0;x < data.length; x++){
var frame = Ti.UI.createView({
width:columnWidth,
height:columnWidth,
backgroundColor:options.gridColor || '#eee',
top:0,
left:0,
right:space,
bottom:space
});
var overlay = Ti.UI.createView({
width:Ti.UI.FILL,
height:Ti.UI.FILL,
backgroundColor:'transparent',
zIndex:1,
strImage:data[x].image
});
var gridElement;
//TYPE OF LAYOUT
switch(layout){
case('gallery'):
gridElement = Widget.createController('gallery',{
image:data[x].image,
title:data[x].title,
width:columnWidth,
padding:options.padding || 10,
showTitle:options.showTitle || false
}).getView();
overlay.addEventListener('click',function(e){
exports.openModal(e.source.strImage);
});
break;
case('customView'):
gridElement = data[x];
break;
}
frame.add(gridElement);
// This condition makes the overlay not be added if it's not gallery layout.
// It's used to make the custom view, caputre the click method. If not,
// The overlay is on top of it and captures the click.
if(layout == 'gallery')
frame.add(overlay);
$.fgScrollView.add(frame);
};
};
exports.openModal = function(url){
var overlay = Ti.UI.createView({
width:Ti.UI.FILL,
height: Ti.UI.FILL,
backgroundColor:'#000',
opacity:0,
zIndex:100
});
var topView = Ti.UI.createView({
width:Ti.UI.FILL,
height: Ti.UI.FILL,
zIndex:1200,
visible:false
});
//this gets image , adds it to top view
var imgView = Ti.UI.createImageView({
image: url,
width:Ti.UI.SIZE,
height: Ti.UI.SIZE
});
var button = Titanium.UI.createButton({
title : 'Use Picture',
top : 10,
width : 100,
height : 50
});
button.addEventListener('touchend',function(e)
{
//Titanium.API.info(url);
if(typeof(onClickCallback) === 'function') {
onClickCallback({ type:'clicked!' }); }
});
//pass callback, not working
onClick();
//add it
topView.add(imgView);
topView.add(button);
$.fgMain.add(overlay);
if (OS_IOS){
//ANIMATION OF OVERLAY
overlay.animate({opacity:0.7,duration:200});
//ANIMATION FOR POP EFFECT
var t = Titanium.UI.create2DMatrix();
t = t.scale(0);
var a = Titanium.UI.createAnimation();
a.transform = t;
a.duration = 200;
$.fgMain.add(topView);
topView.animate(a);
a.addEventListener('complete', function(){
topView.visible = true;
var t2 = Titanium.UI.create2DMatrix();
t2 = t2.scale(1.2);
topView.animate({transform:t2, duration:200},function(e){
var t4 = Titanium.UI.create2DMatrix();
t4 = t4.scale(1.0);
topView.animate({transform:t4, duration:200});
//alert('animation complete');
//hide cancel button
});
});
}
else{
//ANIMATION OF OVERLAY
overlay.animate({opacity:0.7,duration:200},function(e){
topView.visible = true;
$.fgMain.add(topView);
});
}
topView.addEventListener('click',function(e){
if (OS_IOS){
var t3 = Titanium.UI.create2DMatrix();
t3 = t3.scale(1.2);
var a2 = Titanium.UI.createAnimation();
a2.transform = t3;
a2.duration = 200;
topView.animate(a2);
a2.addEventListener('complete', function(){
var t5 = Titanium.UI.create2DMatrix();
t5 = t5.scale(0);
topView.animate({transform:t5, duration:200},function(e){
$.fgMain.remove(topView);
overlay.animate({opacity:0,duration:200},function(e){
$.fgMain.remove(overlay);
});
});
});
}
else{
$.fgMain.remove(topView);
overlay.animate({opacity:0,duration:200},function(e){
$.fgMain.remove(overlay);
});
}
});
};
exports.clearGrid = function(){
$.fgScrollView.removeAllChildren();
};
// Assign our callback
function onClick(callback) {
onClickCallback = callback;
alert(onClickCallback);
};
// Make the onClick function public
exports.onClick = onClick;

Related

How to get the parent child relationship between connected mxgraph

I'm trying to get simplified relationship between connected mxgraph
Question: I'm trying to get the simplified relationship, once the graph is drawn.
i'm trying to get relationship between connected nodes in json.
Note: solution must work for every drawn state.
Here is codepen:
https://codepen.io/eabangalore/pen/pmELpL
i want to get the relationship from above code snippet.
Expected output (from drawn relationship):
[
{"id":0,"parent":"#","text":"A","child":[{"cid":1,"connectionText":"Bangalore"}]},
{"id":1,"parent":0,"text":"B","child":[{"cid":2,"connectionText":""}]},
{"id":2,"parent":1,"text":"C","child":[{"cid":null,"connectionText":""}]}
];
Please refer codepen, as below snippet is not working.
<!--
Copyright (c) 2006-2013, JGraph Ltd
Dynamic toolbar example for mxGraph. This example demonstrates changing the
state of the toolbar at runtime.
-->
<html>
<head>
<title>Toolbar example for mxGraph</title>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
function setGraphData(){
var graphState ={"tagName":"mxGraphModel","children":[{"tagName":"root","children":[{"tagName":"mxCell","attributes":{"id":"0"}},{"tagName":"mxCell","attributes":{"id":"1","parent":"0"}},{"tagName":"mxCell","attributes":{"id":"2","value":"A","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"271.56251525878906","y":"82.44792175292969","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"3","value":"B","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"678.2291717529297","y":"106.89236450195312","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"4","value":"Bangalore","edge":"1","parent":"1","source":"2","target":"3"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"5","value":"C","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"1013.7847747802734","y":"83.55902862548828","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"6","edge":"1","parent":"1","source":"3","target":"5"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"}}]}]}]};
localStorage.setItem('graphState',JSON.stringify(graphState));
}
function html2json(html){
if(html.nodeType==3){
return {
"tagName":"#text",
"content":html.textContent
}
}
var element = {
"tagName":html.tagName
};
if(html.getAttributeNames().length>0){
element.attributes = html.getAttributeNames().reduce(
function(acc,at){acc[at]=html.getAttribute(at); return acc;},
{}
);
}
if(html.childNodes.length>0){
element.children = Array.from(html.childNodes)
.filter(
function(el){
return el.nodeType!=3
||el.textContent.trim().length>0
})
.map(function(el){return html2json(el);});
}
return element;
}
function json2html(json){
var xmlDoc = document.implementation.createDocument(null, json.tagName);
var addAttributes = function(jsonNode, node){
if(jsonNode.attributes){
Object.keys(jsonNode.attributes).map(
function(name){
node.setAttribute(name,jsonNode.attributes[name]);
}
);
}
}
var addChildren = function(jsonNode,node){
if(jsonNode.children){
jsonNode.children.map(
function(jsonChildNode){
json2htmlNode(jsonChildNode,node);
}
);
}
}
var json2htmlNode = function(jsonNode,parent){
if(jsonNode.tagName=="#text"){
return xmlDoc.createTextNode(jsonNode.content);
}
var node = xmlDoc.createElement(jsonNode.tagName);
addAttributes(jsonNode,node);
addChildren(jsonNode,node);
parent.appendChild(node);
}
addAttributes(json,xmlDoc.firstElementChild);
addChildren(json,xmlDoc.firstElementChild);
return xmlDoc;
}
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
function main()
{
setGraphData();
// Checks if browser is supported
if (!mxClient.isBrowserSupported())
{
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
}
else
{
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Creates the div for the toolbar
var tbContainer = document.createElement('div');
tbContainer.style.position = 'absolute';
tbContainer.style.overflow = 'hidden';
tbContainer.style.padding = '2px';
tbContainer.style.left = '0px';
tbContainer.style.top = '0px';
tbContainer.style.width = '24px';
tbContainer.style.bottom = '0px';
document.body.appendChild(tbContainer);
// Creates new toolbar without event processing
var toolbar = new mxToolbar(tbContainer);
toolbar.enabled = false
// Creates the div for the graph
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.overflow = 'hidden';
container.style.left = '24px';
container.style.top = '0px';
container.style.right = '0px';
container.style.bottom = '0px';
container.style.background = 'url("editors/images/grid.gif")';
document.body.appendChild(container);
// Workaround for Internet Explorer ignoring certain styles
if (mxClient.IS_QUIRKS)
{
document.body.style.overflow = 'hidden';
new mxDivResizer(tbContainer);
new mxDivResizer(container);
}
// Creates the model and the graph inside the container
// using the fastest rendering available on the browser
var model = new mxGraphModel();
var graph = new mxGraph(container, model);
// Enables new connections in the graph
graph.setConnectable(true);
graph.setMultigraph(false);
// Stops editing on enter or escape keypress
var keyHandler = new mxKeyHandler(graph);
var rubberband = new mxRubberband(graph);
var addVertex = function(icon, w, h, style)
{
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function()
{
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rectangle.gif', 100, 40, '');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rounded.gif', 100, 40, 'shape=rounded');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/actor.gif', 30, 40, 'shape=actor');
// read state on load
if(window.localStorage.graphState){
var doc = json2html(JSON.parse(localStorage.graphState));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}
// save state on change
graph.getModel().addListener('change',function(){
var codec = new mxCodec();
window.localStorage.graphState = JSON.stringify(html2json(codec.encode(
graph.getModel()
)));
});
}
}
function addToolbarItem(graph, toolbar, prototype, image)
{
// Function that is executed when the image is dropped on
// the graph. The cell argument points to the cell under
// the mousepointer if there is one.
var funct = function(graph, evt, cell, x, y)
{
graph.stopEditing(false);
var vertex = graph.getModel().cloneCell(prototype);
vertex.geometry.x = x;
vertex.geometry.y = y;
graph.addCell(vertex);
graph.setSelectionCell(vertex);
}
// Creates the image which is used as the drag icon (preview)
var img = toolbar.addMode(null, image, function(evt, cell)
{
var pt = this.graph.getPointForEvent(evt);
funct(graph, evt, cell, pt.x, pt.y);
});
// Disables dragging if element is disabled. This is a workaround
// for wrong event order in IE. Following is a dummy listener that
// is invoked as the last listener in IE.
mxEvent.addListener(img, 'mousedown', function(evt)
{
// do nothing
});
// This listener is always called first before any other listener
// in all browsers.
mxEvent.addListener(img, 'mousedown', function(evt)
{
if (img.enabled == false)
{
mxEvent.consume(evt);
}
});
mxUtils.makeDraggable(img, graph, funct);
return img;
}
</script>
</head>
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
<body onload="main();" >
</body>
</html>
please help me thanks in advance!!!
Check out this CodePen link which has the modified version of the snippet you gave. In it, you can add new elements and change texts and see an updated json string representing the relationships in the graph: https://codepen.io/tien-q-nguyen2/pen/GaQXBO
I also edited the function a little from the first time I posted this answer.
Note: based on the expected output you put in the original question, there is only one parent per vertex element ({"id":1,"parent":0 <= in the example you gave), so if there are multiple nodes pointing to the same child , the child's parent property will only refer to the first parent's id. I can change the parent's property to be an array that can keep multiple parent's ids if you want.
function getNodesFrom(graph){
var cells = graph.getModel().cells;
var vertices = [], edges = [];
for (var key in cells) {
if (cells[key].isVertex()){
vertices.push(cells[key]);
}
else if (cells[key].isEdge()){
edges.push(cells[key]);
}
}
var simpleVertices = [], simpleEdges = [];
var newIndex = 0;
var newIndexOf = [];
vertices.forEach(function(vertex){
simpleVertices.push({id: newIndex, value: vertex.value});
newIndexOf[vertex.id] = newIndex++;
});
edges.forEach(function(edge){
if (edge.target === null || edge.source === null) return;
simpleEdges.push({
parentId: newIndexOf[edge.source.id],
childId: newIndexOf[edge.target.id],
value: edge.value
});
});
var edgesForParentIndex = [];
var edgesForChildIndex = [];
simpleEdges.forEach(function(edge){
if (edgesForParentIndex[edge.parentId] === undefined){
edgesForParentIndex[edge.parentId] = [];
}
edgesForParentIndex[edge.parentId].push(edge);
if (edgesForChildIndex[edge.childId] === undefined){
edgesForChildIndex[edge.childId] = [];
}
edgesForChildIndex[edge.childId].push(edge);
});
var nodes = [];
simpleVertices.forEach(function(vertex){
var node = {};
node.id = vertex.id;
if (edgesForChildIndex[node.id] === undefined){
node.parent = '#';
} else {
node.parent = edgesForChildIndex[node.id][0].parentId;
}
node.text = (vertex.value === undefined || vertex.value === null) ? '' : vertex.value;
node.child = [];
if (edgesForParentIndex[node.id] === undefined){
node.child.push({cid: null, connectionText: ""});
} else {
edgesForParentIndex[node.id].forEach(function(edge){
var text = (edge.value === undefined || edge.value === null) ? '' : edge.value;
node.child.push({cid: edge.childId, connectionText: text});
});
}
nodes.push(node);
});
return nodes;
}

Reload this javascript file after an ajax page load (YITH Infinite Scroll)

I have a woocommerce shop that has an ajax page load navigation. So a user clicks next page and more products are loaded via ajax.
However, I have a javascript file that manipulates the colors on each product on a page load. But since I have introduced this ajax page load, the script no longer loads after a next page is requested.
I am using YITH Infinite Page scroll. They have a trigger yith_infs_added_elem that I can use to do some code after the ajax has been loaded.
So I currently have:
jQuery(document).on('yith_infs_added_elem', function() {
});
This is the YITH trigger I have to use to run my script after the ajax has loaded.
But I am stuck. I have read many other solutions for other people, but I cannot seem to figure out how to reload my javascript file - /js/javascript/colors/dominant-color-shop.js.
My javascript file that normally runs on page load is:
jQuery( document ).ready( function( $ ) {
var image = new Image;
var colorThief = new ColorThief();
var bg;
var vibrant_color;
var vibrantText;
$('.post-image-hidden-container').each(function() {
bg = $(this).text();
var rgbs_text = [];
image.onload = function() {
$('.shop-page-item-thumb').each(function() {
var thumb = $(this);
var rgbs = [];
thumb.find('img').each(function() {
var vibrant = new Vibrant(this);
var swatches = vibrant.swatches();
var dominantColor = colorThief.getColor(this);
var productColorPalette = colorThief.getPalette(this, 12);
var productLightestColor = productColorPalette.reduce(function(previousValue, currentValue) {
var currLightNess = (0.2126*currentValue[0] + 0.7152*currentValue[1] + 0.0722*currentValue[2]);
var prevLightNess = (0.2126*previousValue[0] + 0.7152*previousValue[1] + 0.0722*previousValue[2]);
return (prevLightNess < currLightNess) ? currentValue : previousValue;
});
/* Create Shades and Tints of Lightest Color */
var lightShadeRGB = productLightestColor.join();
lightShadeRGB = lightShadeRGB.split(',');
var r = lightShadeRGB[0],
g = lightShadeRGB[1],
b = lightShadeRGB[2];
var rpt = lightShadeRGB[0] - 35,
gpt = lightShadeRGB[1] - 35,
bpt = lightShadeRGB[2] - 35;
var tintDk = 'rgb('+rpt+', '+gpt+', '+bpt+')';
for (var swatch in swatches) {
if (swatches.hasOwnProperty(swatch) && swatches[swatch]) {
rgbs.push(swatches[swatch].getHex());
rgbs_text.push(swatches[swatch].getTitleTextColor());
}
}
vibrant_color = rgbs[0];
vibrant_color_2 = rgbs[1];
darkVibrant = rgbs[2];
darkMuted = rgbs[3];
lightVibrant = rgbs[4];
vibrantText = rgbs_text[0];
vibrantText_2 = rgbs_text[1];
darkVibrantText = rgbs_text[2];
darkMutedText = rgbs_text[3];
lightVibrantText = rgbs_text[4];
thumb.parent().find('.product-bottom-info-container').css({
borderTop: '4px solid ' + vibrant_color
});
thumb.parent().find('.hot-badge').css({
backgroundColor: darkMuted,
color: darkMutedText
});
thumb.parent().find('.mp3-badge').css({
backgroundColor: vibrant_color,
color: vibrantText
});
thumb.parent().find('.mp3-badge-link').css({
color: vibrantText
});
thumb.parent().find('.wav-badge').css({
backgroundColor: vibrant_color_2,
color: vibrantText_2
});
thumb.parent().find('.wav-badge-link').css({
color: vibrantText_2
});
thumb.parent().find('.hot-post-bookmark').css({
color: vibrant_color
});
thumb.parent().find('.the-rating-stars-icons').css({
color: vibrant_color
});
thumb.parent().find('.progress-bar').css({
backgroundColor: vibrant_color
});
});
});
}
image.src = bg;
});
$('#player-toggler-id').css({
backgroundColor: '#181f24'
});
});
It works fine until I request the next page. The javscript no longer works. How exactly can I call this script all over again, once the yith ajax has loaded with this trigger - yith_infs_added_elem.
I have read up on .on() .live() (which is deprecated), etc. Can anyone help?
Your function only runs on page load...
To trigger it again later, you should make it a named function.
So the script stays exactly the same, but wrapped with function arrangeColors(){ (You can name it as you wish) and }.
Then, in the ajax success callback, call this function again.
jQuery( document ).ready( function( $ ) {
function arrangeColors(){ // Make the script a named function
var image = new Image;
var colorThief = new ColorThief();
var bg;
var vibrant_color;
var vibrantText;
$('.post-image-hidden-container').each(function() {
bg = $(this).text();
var rgbs_text = [];
image.onload = function() {
$('.shop-page-item-thumb').each(function() {
var thumb = $(this);
var rgbs = [];
thumb.find('img').each(function() {
var vibrant = new Vibrant(this);
var swatches = vibrant.swatches();
var dominantColor = colorThief.getColor(this);
var productColorPalette = colorThief.getPalette(this, 12);
var productLightestColor = productColorPalette.reduce(function(previousValue, currentValue) {
var currLightNess = (0.2126*currentValue[0] + 0.7152*currentValue[1] + 0.0722*currentValue[2]);
var prevLightNess = (0.2126*previousValue[0] + 0.7152*previousValue[1] + 0.0722*previousValue[2]);
return (prevLightNess < currLightNess) ? currentValue : previousValue;
});
/* Create Shades and Tints of Lightest Color */
var lightShadeRGB = productLightestColor.join();
lightShadeRGB = lightShadeRGB.split(',');
var r = lightShadeRGB[0],
g = lightShadeRGB[1],
b = lightShadeRGB[2];
var rpt = lightShadeRGB[0] - 35,
gpt = lightShadeRGB[1] - 35,
bpt = lightShadeRGB[2] - 35;
var tintDk = 'rgb('+rpt+', '+gpt+', '+bpt+')';
for (var swatch in swatches) {
if (swatches.hasOwnProperty(swatch) && swatches[swatch]) {
rgbs.push(swatches[swatch].getHex());
rgbs_text.push(swatches[swatch].getTitleTextColor());
}
}
vibrant_color = rgbs[0];
vibrant_color_2 = rgbs[1];
darkVibrant = rgbs[2];
darkMuted = rgbs[3];
lightVibrant = rgbs[4];
vibrantText = rgbs_text[0];
vibrantText_2 = rgbs_text[1];
darkVibrantText = rgbs_text[2];
darkMutedText = rgbs_text[3];
lightVibrantText = rgbs_text[4];
thumb.parent().find('.product-bottom-info-container').css({
borderTop: '4px solid ' + vibrant_color
});
thumb.parent().find('.hot-badge').css({
backgroundColor: darkMuted,
color: darkMutedText
});
thumb.parent().find('.mp3-badge').css({
backgroundColor: vibrant_color,
color: vibrantText
});
thumb.parent().find('.mp3-badge-link').css({
color: vibrantText
});
thumb.parent().find('.wav-badge').css({
backgroundColor: vibrant_color_2,
color: vibrantText_2
});
thumb.parent().find('.wav-badge-link').css({
color: vibrantText_2
});
thumb.parent().find('.hot-post-bookmark').css({
color: vibrant_color
});
thumb.parent().find('.the-rating-stars-icons').css({
color: vibrant_color
});
thumb.parent().find('.progress-bar').css({
backgroundColor: vibrant_color
});
});
});
}
image.src = bg;
});
$('#player-toggler-id').css({
backgroundColor: '#181f24'
});
} // Add this closing bracket
// Call the function on load
arrangeColors();
});
I make it work now like this:
<script type="text/javascript">
jQuery(document).ready(function ($) {
loadAnimation();
jQuery(document).on("yith_infs_added_elem", function () {
loadAnimation();
});
function loadAnimation() {
//here put your code for something to doin my case .add-to-cart trigger
}
//here other functions for example flyToElement
});
</script>

How can I replace the mouseout event with touchend for iOS Safari?

It seems from the Safari docs docs that I should be able to add an event listener to an element in order to get a touchend event to fire (I have assumed this to be the event which happens when the user lifts their finger having first touched the element).
However, I am not getting the desired behaviour with this code:
/*
Easy Scroll v1.0
written by Alen Grakalic, provided by Css Globe (cssglobe.com)
please visit http://cssglobe.com/post/1495/easy-scroll-accessible-content-scroller
*/
this.easyscroll = function(){
// id of the container element
var id = "myContent";
// navigation buttons text
//dave var nav = ["Scroll Up", "Scroll Down", "Reset"];
var nav = ["Scroll Up", "Scroll Down"];
// id for each navigation button (OPTIONAL)
//dave var navId = ["btnUp", "btnDown", "btnReset"];
var navId = ["btnUp", "btnDown"];
// movement speed
var speed = 10;
// desired height of the container element (in pixels)
var height = 300;
//
// END CONFIG
// do not edit below this line (unless you want to of course :) )
//
var obj = document.getElementById(id);
obj.up = false;
obj.down = false;
obj.fast = false;
var container = document.createElement("div");
var parent = obj.parentNode;
container.id="easyscroll";
parent.insertBefore(container,obj);
parent.removeChild(obj);
container.style.position = "relative";
container.style.height = height + "px";
container.style.overflow = "hidden";
obj.style.position = "absolute";
obj.style.top = "0";
obj.style.left = "0";
container.appendChild(obj);
var btns = new Array();
var ul = document.createElement("ul");
ul.id="easyscrollnav";
for (var i=0;i<nav.length;i++){
var li = document.createElement("li");
li.innerHTML = nav[i];
li.id = navId[i];
btns.push(li);
ul.appendChild(li);
}
parent.insertBefore(ul,container);
btns[0].onmouseover = function(){
obj.up = true;
this.className = "over";
};
btns[0].onmouseout = function(){
obj.up = false;
this.className = "";
};
btns[1].onmouseover = function(){
obj.down = true;
this.className = "over";
};
btns[1].onmouseout = function(){
obj.down = false;
this.className = "";
};
btns[0].onmousedown = btns[1].onmousedown = function(){
obj.fast = true;
};
btns[0].onmouseup = btns[1].onmouseup = function(){
obj.fast = false;
};
btns[0].addEventListener("touchend", function(){
obj.up = false;
}, false);
btns[1].addEventListener("touchend", function(){
obj.down = false;
}, false);
//btns[2].onmouseover = function(){
// this.className = "over";
// };
//btns[2].onmouseout = function(){
//this.className = "";
//};
//btns[2].onclick = function(){
// obj.style.top = "0px";
// };
this.start = function(){
var newTop;
var objHeight = obj.offsetHeight;
var top = obj.offsetTop;
var fast = (obj.fast) ? 2 : 1;
if(obj.down){
newTop = ((objHeight+top) > height) ? top-(speed*fast) : top;
obj.style.top = newTop + "px";
};
if(obj.up){
newTop = (top < 0) ? top+(speed*fast) : top;
obj.style.top = newTop + "px";
};
};
obj.interval = setInterval("start()",50);
};
//
// script initiates on page load.
//
this.addEvent = function(obj,type,fn){
if(obj.attachEvent){
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn](window.event );}
obj.attachEvent('on'+type, obj[type+fn]);
} else {
obj.addEventListener(type,fn,false);
};
};
addEvent(window,"load",easyscroll);
In Safari on iOS, I have to click btn[0] and btn[1] again to get the scrolling to stop. The desired behaviour is that the scrolling stops as soon as the user removes their finger, having started the scroll with the first touch. The easyscroll library is designed for desktop browsers and I am trying to adapt it to work on iOS too.
Without the modifications, it is impossible to stop the scrolling on a touch device.

Run native JavaScript function on page load

With the help of you fantastic JavaScript wizards I've got a native JavaScript function that beefs up the size of an HTML5 video when clicking a button and then re-runs whenever the window is resized.
I'd like to remove the button from the equation instead launching it on page load, remove the class name dependencies (if they're still in after button removal), while maintaining the window resize trigger.
Thanks for your help! Couldn't do it without you. A demo can be seen at http://kzmnt.com/test/
JavaScript:
var clicked = document.getElementById("buttonImportant")
var videoContainer = document.getElementById('video_container');
var video = videoContainer.getElementsByTagName('video')[0];
video.style.height="auto";
video.style.width="1280px";
clicked.addEventListener('click',function(){
if( videoContainer.className.lastIndexOf("fullScreen")>=0 ){
videoContainer.className="video-js-box";
video.style.height = "";
video.style.width="";
}
else
{
videoContainer.className="video-js-box fullScreen";
video.style.height = "";
video.style.width="";
}
myResizerObject.prevWidth = video.offsetWidth;
myResizerObject.prevHeight = video.offsetHeight;
myResizerObject.Init();
},false);
var RESIZER = function(){
this.prevWidth = video.offsetWidth;
this.prevHeight = video.offsetHeight;
this.videoContainer = document.getElementById('video_container');
this.video = videoContainer.getElementsByTagName('video')[0];
this.videoStyle = this.video.style;
var ratio = this.video.offsetHeight/this.video.offsetWidth;
var that = this;
this.Init = function(){
if( that.videoContainer.className.lastIndexOf("fullScreen")>=0 )
{
var videoContOffsetWidth = that.videoContainer.offsetWidth;
var videoOffsetWidth = that.video.offsetWidth;
var videoContOffsetHeight = that.videoContainer.offsetHeight;
var videoOffsetHeight = that.video.offsetHeight;
if(that.prevWidth!= videoContOffsetWidth)
{
that.prevWidth = videoContOffsetWidth;
var desired = videoContainer.offsetHeight/videoContainer.offsetWidth;
if(desired>ratio){
that.videoStyle.width=videoContOffsetWidth*desired+videoContOffsetWidth*desired+"px";
that.videoStyle.left = -1*(videoOffsetWidth-videoContOffsetWidth)/2+'px';
}
else{
that.videoStyle.cssText="height:auto;width:100%;left:0px;top:0px;";
}
}
if(that.prevHeight!=videoContOffsetHeight)
{
that.prevHeight = videoContOffsetHeight;
var desired = videoContOffsetHeight/videoContOffsetWidth;
if(desired>ratio){ console.log(ratio);
that.videoStyle.top = '0px';
that.videoStyle.left = -1*(videoOffsetWidth-videoContOffsetWidth)/2+'px';
that.videoStyle.width = videoContOffsetHeight*desired+videoContOffsetHeight/desired+'px';
}
else
{
that.videoStyle.top = -1*(videoOffsetHeight-videoContOffsetHeight)/2+'px';
}
}
}
};
};
var myResizerObject = new RESIZER();
window.onresize = myResizerObject.Init;
Are you looking for something like...
window.onload = mySuperCoolFunction;
And why do you need jQuery? Maybe you could explicitly state your goals/questions at the bottom in bold so that we can help you :)
If you're only looking to init another function, why not just add the call to your superCoolFunction while you're calling your other pre-written jQuery function. For example:
$(document).ready(function() {
center();
mySuperCoolFunction(); // not necessary to init in another file...
});
$(window).load(function(){
center();
mySuperCoolFunction();
});
$(window).resize(function(){
center();
mySuperCoolFunction();
});
Rewritten in jQuery:
$(function() {
$('video').eq(0).css({
height: "auto",
width: "1280px"
});
if ($('#video_container').hasClass('fullScreen')) {
$('#video_container').attr('class', 'video-js-box');
$('video').eq(0).css({
height: "auto",
width: "1280px"
});
}
else {
$('#video_container').attr('class', 'video-js-box fullscreen');
$('video').eq(0).css({
height: "auto",
width: "1280px"
});
}
myResizerObject.prevWidth = video.offsetWidth;
myResizerObject.prevHeight = video.offsetHeight;
myResizerObject.Init();
});

Loading content with ajax while scrolling

I'm using jQuery Tools Plugin as image slider (image here), but due to large amount of images I need to load them few at a time. Since it's javascript coded, I can't have the scroll position as far as I know. I want to load them as soon as the last image shows up or something like that. I have no idea where I put and event listener neither anything.
Here is my code http://jsfiddle.net/PxGTJ/
Give me some light, please!
I just had to use jQuery Tools' API, the onSeek parameter within the scrollable() method.
It was something like that
$(".scrollable").scrollable({
vertical: true,
onSeek: function() {
row = this.getIndex();
// Check if it's worth to load more content
if(row%4 == 0 && row != 0) {
var id = this.getItems().find('img').filter(':last').attr('id');
id = parseInt(id);
$.get('galeria.items.php?id='+id, null, function(html) {
$('.items').append(html);
});
}
}
});
That could be made the following way:
//When the DOM is ready...
$(document).ready(function() {
//When the user scrolls...
$(window).scroll(function() {
var tolerance = 800,
scrollTop = $(window).scrollTop();
//If the the distance to the top is greater than the tolerance...
if(scrollTop > tolerance) {
//Do something. Ajax Call, Animations, whatever.
}
}) ;
});
That should do the trick.
EDIT: Because you're not using the native scroll, we've got to do a little fix to the code:
//When the DOM is ready...
$(document).ready(function() {
//When the user scrolls...
$("div.scrollable").find(".next").click(function() {
var tolerance = 800,
// The absolute value of the integer associated
// to the top css property
scrollTop = Math.abs(parseInt($("div.items").css("top")));
//If the the distance to the top is greater than the tolerance...
if(scrollTop > tolerance) {
//Do something. Ajax Call, Animations, whatever.
}
}) ;
});
try something like this
$('#scrollable').find('img:last').load(function() {
//load the content
});
OR find the offset location/position of the last image and try loading your content when you reach the offset position on scrolling
HTML :
<div>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<span>Hello !!</span>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
some CSS :
div {
width:200px;
height:200px;
overflow:scroll;
}
Javascript :
$(document).ready(function() {
$('div').scroll(function() {
var pos = $('div').scrollTop();
var offset = $('span').offset().top;
if(pos >= offset ) {
alert('you have reached your destiny');
}
});
});
here's a quick demo http://jsfiddle.net/8QbwU/
Though Demo doesn't met your full requirements, I believe It does give you some light to proceed further :)
First, you'll want to use jQuery for this
Second, put a placeholder on your page to contain your data.
<table id="dataTable" class="someClass" style="border-collapse: collapse;">
<colgroup>
<col width="12%" />
<col width="12%" />
<col width="12%" />
<!-- Define your column widths -->
</colgroup>
</table>
You'll need to code your own GetData method in a webservice, but this is the general idea (And call Refresh(); from your page load)
function Refresh() {
var getData = function(callback, context, startAt, batchSize) {
MyWebservice.GetData(
startAt, //What record to start at (1 to start)
batchSize, //Results per page
3, //Pages of data
function(result, context, method) {
callback(result, context, method);
},
null,
context
);
};
$('#dataTable').scrolltable(getData);
}
The getData function variable is passed into the scrolltable plugin, it will be called as needed when the table is being scrolled. The callback and context are passed in, and used by the plugin to manage the object you are operating on (context) and the asynchronous nature of the web (callback)
The GetData (note the case) webmethod needs to return a JSON object that contains some critical information, how your server side code does this is up to you, but the object this plugin expects is the following. The Prior and Post data are used to trigger when to load more data, basically, you can scroll through the middle/active page, but when you start seeing data in the prior or post page, we're going to need to fetch more data
return new {
// TotalRows in the ENTIRE result set (if it weren't paged/scrolled)
TotalRows = tableElement.Element("ResultCount").Value,
// The current position we are viewing at
Position = startAt,
// Number of items per "page"
PageSize = tableElement.Element("PageSize").Value,
// Number of pages we are working with (3)
PageCount = tableElement.Element("PageCount").Value,
// Data page prior to active results
PriorData = tbodyTop.Html(),
// Data to display as active results
CurrentData = tbodyCtr.Html(),
// Data to display after active results
PostData = tbodyBot.Html()
};
Next is the plugin itself
/// <reference path="../../js/jquery-1.2.6.js" />
(function($) {
$.fn.scrolltable = function(getDataFunction) {
var setData = function(result, context) {
var timeoutId = context.data('timeoutId');
if (timeoutId) {
clearTimeout(timeoutId);
context.data('timeoutId', null);
}
var $table = context.find("table");
var $topSpacer = $table.find('#topSpacer');
var $bottomSpacer = $table.find('#bottomSpacer');
var $newBodyT = $table.children('#bodyT');
var $newBodyC = $table.children('#bodyC');
var $newBodyB = $table.children('#bodyB');
var preScrollTop = context[0].scrollTop;
$newBodyT.html(result.PriorData);
$newBodyC.html(result.CurrentData);
$newBodyB.html(result.PostData);
var rowHeight = $newBodyC.children('tr').height() || 20;
var rowCountT = $newBodyT.children('tr').length;
var rowCountC = $newBodyC.children('tr').length;
var rowCountB = $newBodyB.children('tr').length;
result.Position = parseInt(result.Position);
$newBodyC.data('firstRow', result.Position);
$newBodyC.data('lastRow', (result.Position + rowCountC));
context.data('batchSize', result.PageSize);
context.data('totalRows', result.TotalRows);
var displayedRows = rowCountT + rowCountC + rowCountB;
var rowCountTopSpacer = Math.max(result.Position - rowCountT - 1, 0);
var rowCountBottomSpacer = result.TotalRows - displayedRows - rowCountTopSpacer;
if (rowCountTopSpacer == 0) {
$topSpacer.closest('tbody').hide();
} else {
$topSpacer.closest('tbody').show();
$topSpacer.height(Math.max(rowCountTopSpacer * rowHeight, 0));
}
if (rowCountBottomSpacer == 0) {
$bottomSpacer.closest('tbody').hide();
} else {
$bottomSpacer.closest('tbody').show();
$bottomSpacer.height(Math.max(rowCountBottomSpacer * rowHeight, 0));
}
context[0].scrollTop = preScrollTop; //Maintain Scroll Position as it sometimes was off
};
var onScroll = function(ev) {
var $scrollContainer = $(ev.target);
var $dataTable = $scrollContainer.find('#dataTable');
var $bodyT = $dataTable.children('tbody#bodyT');
var $bodyC = $dataTable.children('tbody#bodyC');
var $bodyB = $dataTable.children('tbody#bodyB');
var rowHeight = $bodyC.children('tr').height();
var currentRow = Math.floor($scrollContainer.scrollTop() / rowHeight);
var displayedRows = Math.floor($scrollContainer.height() / rowHeight);
var batchSize = $scrollContainer.data('batchSize');
var totalRows = $scrollContainer.data('totalRows');
var prevRowCount = $bodyT.children('tr').length;
var currRowCount = $bodyC.children('tr').length;
var postRowCount = $bodyB.children('tr').length;
var doGetData = (
(
(currentRow + displayedRows) < $bodyC.data('firstRow') //Scrolling up
&& (($bodyC.data('firstRow') - prevRowCount) > 1) // ...and data isn't already there
)
||
(
(currentRow > $bodyC.data('lastRow')) //Scrolling down
&& (($bodyC.data('firstRow') + currRowCount + postRowCount) < totalRows) // ...and data isn't already there
)
);
if (doGetData) {
var batchSize = $scrollContainer.data('batchSize');
var timeoutId = $scrollContainer.data('timeoutId');
if (timeoutId) {
clearTimeout(timeoutId);
$scrollContainer.data('timeoutId', null);
}
timeoutId = setTimeout(function() {
getDataFunction(setData, $scrollContainer, currentRow, batchSize);
}, 50);
$scrollContainer.data('timeoutId', timeoutId);
}
};
return this.each(function() {
var $dataTable = $(this);
if (!getDataFunction)
alert('GetDataFunction is Required');
var batchSize = batchSize || 25;
var outerContainerCss = outerContainerCss || {};
var defaultContainerCss = {
overflow: 'auto',
width: '100%',
height: '200px',
position: 'relative'
};
var containerCss = $.extend({}, defaultContainerCss, outerContainerCss);
if (! $dataTable.parent().hasClass('_outerContainer')) {
$dataTable
.wrap('<div class="_outerContainer" />')
.append($('<tbody class="spacer"><tr><td><div id="topSpacer" /></td></tr></tbody>'))
.append($('<tbody id="bodyT" />'))
.append($('<tbody id="bodyC" />'))
.append($('<tbody id="bodyB" />'))
.append($('<tbody class="spacer"><tr><td><div id="bottomSpacer" /></td></tr></tbody>'));
}
var $scrollContainer = $dataTable.parent();
$scrollContainer
.css(containerCss)
.scroll(onScroll);
getDataFunction(setData, $scrollContainer, 1, batchSize);
});
};
})(jQuery);
You'll likely need to tweak some things. I just converted it to a jQuery plugin and it's probably still a little glitchy.

Categories