I'm trying to use the yahoo ui history library. I don't see a great way to avoid wrapping all my function contents with the Y.use so that I can get access to the history object. I tried declaring it globally outside of the use() command, but this didn't seem to work. If you look at my showDashboard() and showReport1() methods, you can see I'm wrapping the contents, which seems redundant to have to do this for every function that uses the history. Is there a better way to do this?
All of the yahoo examples I've seen don't se functions at all and keep the entire sample inside a single use method.
<div>
Dashboard |
Report 1
</div>
<script type="text/javascript">
// Global reference to Yahoo UI object
var Y = YUI();
function showDashboard() {
Y.use('*', function (Y) {
var history = new Y.HistoryHash();
history.addValue("report", "dashboard");
});
}
function showReport1() {
Y.use('*', function (Y) {
var history = new Y.HistoryHash();
history.addValue('report', "report1");
//var x = { 'report': 'report1', 'date': '11/12/2012' };
//history.addValue("report", x);
});
}
Y.use('history', 'tabview', function (Y) {
var history = new Y.HistoryHash();
var tabview = new Y.TabView({ srcNode: '#demo' });
// Render the TabView widget to turn the static markup into an
// interactive TabView.
tabview.render();
// Set the selected report to the bookmarked history state, or to
// the first report if there's no bookmarked state.
tabview.selectChild(history.get('report') || 0);
// Store a new history state when the user selects a report.
tabview.after('selectionChange', function (e) {
// If the new tab index is greater than 0, set the "tab"
// state value to the index. Otherwise, remove the "tab"
// state value by setting it to null (this reverts to the
// default state of selecting the first tab).
history.addValue('report', e.newVal.get('index') || 0);
});
// Listen for history changes from back/forward navigation or
// URL changes, and update the report selection when necessary.
Y.on('history:change', function (e) {
// Ignore changes we make ourselves, since we don't need
// to update the selection state for those. We're only
// interested in outside changes, such as the ones generated
// when the user clicks the browser's back or forward buttons.
if (e.src === Y.HistoryHash.SRC_HASH) {
if (e.changed.report) {
// The new state contains a different report selection, so
// change the selected report.
tabview.selectChild(e.changed.report.newVal);
} else if (e.removed.report) {
// The report selection was removed in the new state, so
// select the first report by default.
tabview.selectChild(0);
}
}
if (e.changed.report) {
alert("New value: " + e.changed.report.newVal);
alert("Old value: " + e.changed.report.prevVal);
}
});
});
</script>
</form>
</body>
</html>
Instead of using plain function on click, attach handlers with YUI.
If you can change the HTML code - add id or class to the links, for example
<a id="btnShowDashboard" href="#">Dashboard</a>
Then in your use() add click handler to the buttons
Y.use('history', 'tabview', 'node', 'event', function (Y) {
var bntShowDashboard = Y.one('#btnShowDashboard');
if (bntShowDashboard) {
bntShowDashboard.on('click', function(e) {
e.preventDefault();
var history = new Y.HistoryHash();
history.addValue("report", "dashboard");
});
}
...
})
That way you will be sure than on the moment of execution "history" is loaded.
BUT there is one drawback - until YUI modules are loaded, if you click the links nothing will happen.
Related
I want to add a 3rd Inspector which will open only for an element(not a link) of specific type, for example only for basic.Rect in Rappid.
So far, there are 2 Inspectors.For elements and for links.
Is there any way it can be done?
The following code is a part of the KitchenSkink version of Rappid.
Here is function createInspector:
createInspector: function(cellView) {
var cell = cellView.model || cellView;
// No need to re-render inspector if the cellView didn't change.
if (!this.inspector || this.inspector.options.cell !== cell) {
// Is there an inspector that has not been removed yet.
// Note that an inspector can be also removed when the underlying cell is removed.
if (this.inspector && this.inspector.el.parentNode) {
this.inspectorClosedGroups[this.inspector.options.cell.id] = _.map(app.inspector.$('.group.closed'), function(g) {
return $(g).attr('data-name');
});
// Clean up the old inspector if there was one.
this.inspector.updateCell();
this.inspector.remove();
}
var inspectorDefs = InspectorDefs[cell.get('type')];
this.inspector = new joint.ui.Inspector({
inputs: inspectorDefs ? inspectorDefs.inputs : CommonInspectorInputs,
groups: inspectorDefs ? inspectorDefs.groups : CommonInspectorGroups,
cell: cell
});
this.initializeInspectorTooltips();
this.inspector.render();
$('.inspector-container').html(this.inspector.el);
if (this.inspectorClosedGroups[cell.id]) {
_.each(this.inspectorClosedGroups[cell.id], this.inspector.closeGroup, this.inspector);
} else {
this.inspector.$('.group:not(:first-child)').addClass('closed');
}
}
}
If you use joint.ui.Inspector.create('#path', inspectorProperties) any previous instance of the Inspector in a specific DOM element is removed and new one is created and rendered into the DOM automatically (it avoids creating a new instance of joint.ui.Inspector(), rendering it, adding the rendered result manually and removing the previous instance).
It also keeps track on open/closed groups and restore them based on the last used state.
Besides this, you may always have several different inspectorProperties objects previously defined when you are about to create() the inspector. So following the code you pasted, you could perform the tests you need first and then create the appropriate inspector:
if(cell instanceof joint.basic.Rect){
var customInputs = _.clone(CommonInspectorInputs);
// extend more inputs into `customInputs` from a variable previously defined
// OR modify the default rectangle's inspector directly, example:
customInputs.attrs.text = {
type: 'textarea',
label: 'Multiline text',
text: 'Type\nhere!',
group: joint.util.getByPath(CommonInspectorInputs.attrs, 'text/group', '/');
};
joint.ui.Inspector.create('.extra-inspector-container', {
cell: cell
inputs: customInputs,
groups: CommonInspectorGroups,
});
} // if only ONE inspector needs to be loaded add an ELSE block here
// and use '.inspector-container' in the `create()` above
// If `InspectorDefs` is a global variable with all the cells inspectors properties
// create and load the default inspector
joint.ui.Inspector.create('.inspector-container', _.extend({cell: cell},
InspectorDefs[cell.get('type')])
);
I am using this function in NW.JS to get a file locations of images. I use that file location in the callback to modify a div background using .css() in jquery. My problem is that the script seems to remember the last div that it modified. When I try to use this to change the background of another div after previously having used it to change the background on a different div BOTH divs change their backgrounds. I guess I need to be able to get this script to know what button clicked it and to forget anything that another button asked it to do. As you can tell I am new to javascript. How can I do that?
function chooseFile(name, handleFile) {
var chooser = document.querySelector(name);
chooser.addEventListener("change", function(evt) {
for(var f of this.files){
console.log(f.name);
console.log(f.path);
handleFile(f.name, f.path);
}
}, false);
chooser.click();
}
chooseFile('#fileDialog', function(name, path){ ... /* do something with the file(s) */ });
In many cases, it’ll make sense to write your script so that it can react to new files:
const chooser = document.getElementById('fileDialog');
// Treat chooser as a stream of new files that can be added at any time
chooser.addEventListener("change", function (evt) {
for (const f of this.files) {
console.log(f.name);
console.log(f.path);
handleFile(f.name, f.path);
}
}, false);
// Then, when you want to prompt for a new file at any point in the future…
function promptForFiles() {
chooser.click();
}
When that’s not possible, you can instead have it hold a maximum of one handler at a time by assigning to the old but reliable onchange property:
function chooseFile(name, handleFile) {
const chooser = document.querySelector(name);
chooser.onchange = function () {
for (const f of this.files) {
console.log(f.name);
console.log(f.path);
handleFile(f.name, f.path);
}
};
chooser.click();
}
I am trying to prevent the class .activeAdv being added if the link within has it's URL stored in cookie, this cooke is added if user clicks links so basically I am trying to stop returning users clicking same link twice.
The link is by default hidden underneath a div which animates on addition of .activeAdv class, revealing link.
Current code is below along with codepen example of project. I'm guessing I need to wrap the activeAdv addClass in an IF conditional which:
Gets value of child link's href
Check to see if a matching cookie exists
Only add .activeAdv if condition returns false
I think I have the right idea and have got as far as setting cookie on click of link, I am struggling with the IF statement though, could anyone lend a hand?
http://codepen.io/Jambob/pen/wKyoRr
<article>
<div id="on-1" class="box">
<h2>1 dec</h2>
</div>
<div class="present">
content
www.google.com
</div>
</article>
// Checks for content within .present, if TRUE adds .activeAdv animation class
$(".present").filter(function(){
return $(this).html().trim().length > 0;
}).parent().addClass('activeAdv');
// When link clicked, store its URL in cookie
$( ".present a" ).click(function() {
$.cookie($(this).attr('href'), true);
});
if ( '(".present").html().trim().length > 0;' ) {
if ( '(".present a").attr("href")' === "http://www.google.com") {
$(this).parent().addClass('activeAdv');
}
}
After a bit of thinking I have come up with a different IF statement which may be along the right lines
// Checks if content exists
if ( '(".present").html().trim().length > 0;' ) {
// Checks if HREF of child link matches existing cookie (using a string for testing)
if ( '(".present a").attr("href")' === "http://www.google.com") {
$(this).parent().addClass('activeAdv');
}
}
So if :visited CSS pseudoclass isn't sufficient (it matches visited links), you can make use of localStorage, which is much more dedicated to this purpose. I created script which can be run out the Firebug console and colors non-visited links:
(function(links) {
// Load visited links from local storage
var visited = JSON.parse(localStorage["visited"]||"[]");
// Safety check
if(!visited instanceof Array) {
visited = [];
localStorage["visited"] = [];
}
function setClassToLinks() {
links.each(function(){
// Remember state - asume not visited
var linkVisited = false;
// Check for inner HTML
if(this.innerHTML.trim().length > 0) {
// Check if in list of visited links
if(visited.indexOf(this.href)==-1)
linkVisited = true;
else
console.log("Already visited: "+this.href);
}
else
// Skip empty links
return console.log("No inner HTML.");
// Reset color
this.style.color = !linkVisited?"":"red";
// And remove class
if(linkVisited)
$(this).removeClass("activeAdv");
else
$(this).addClass("activeAdv");
})
}
setClassToLinks();
// When link clicked, store its URL in LocalStorage
links.click(function() {
// Prevent duplicities
if(visited.indexOf(this.href)==-1) {
visited.push(this.href);
localStorage["visited"] = JSON.stringify(visited);
}
});
// [OPTIONAL] Update links realtime - triggers when local storage changes
window.addEventListener('storage', function (event) {
if(event.key=="visited") {
visited = JSON.parse(localStorage["visited"]||"[]");
// Change CSS
setClassToLinks();
}
});
})($("a"));
Neat aspect of my solution is, that the links automatically update when you browse in different tab (provided this tab has run the script).
The visited array may quickly grow, which is why I think cookie is such a bad idea.
I have an instance of Sigma.Js 1.0.0 rendering a graph in my Canvas element. (The code is below, but you can simply scroll to Step 2 of Tutorial on the main sigmajs.org page.
As you can see from that code, when the node is clicked, clickNode event occurs, which then applies filtering to the graph, showing only the clicked node and its neighborhood and dimming the others. That's quite clear.
However, how would I make exactly the same thing happen from the outside? Suppose I have the graph rendered already and I have a Tag Cloud next to it. And I want that when I click on a #hashtag, only that node is shown in the graph and the rest are dimmed. How would I do that?
Thanks!
<div id="sigma-container"></div>
<script src="path/to/sigma.js"></script>
<script src="path/to/sigma.parsers.min.gexf.js"></script>
<script>
// Add a method to the graph model that returns an
// object with every neighbors of a node inside:
sigma.classes.graph.addMethod('neighbors', function(nodeId) {
var k,
neighbors = {},
index = this.allNeighborsIndex[nodeId] || {};
for (k in index)
neighbors[k] = this.nodesIndex[k];
return neighbors;
});
sigma.parsers.gexf(
'path/to/les-miserables.gexf',
{
container: 'sigma-container'
},
function(s) {
// We first need to save the original colors of our
// nodes and edges, like this:
s.graph.nodes().forEach(function(n) {
n.originalColor = n.color;
});
s.graph.edges().forEach(function(e) {
e.originalColor = e.color;
});
// When a node is clicked, we check for each node
// if it is a neighbor of the clicked one. If not,
// we set its color as grey, and else, it takes its
// original color.
// We do the same for the edges, and we only keep
// edges that have both extremities colored.
s.bind('clickNode', function(e) {
var nodeId = e.data.node.id,
toKeep = s.graph.neighbors(nodeId);
toKeep[nodeId] = e.data.node;
s.graph.nodes().forEach(function(n) {
if (toKeep[n.id])
n.color = n.originalColor;
else
n.color = '#eee';
});
s.graph.edges().forEach(function(e) {
if (toKeep[e.source] && toKeep[e.target])
e.color = e.originalColor;
else
e.color = '#eee';
});
// Since the data has been modified, we need to
// call the refresh method to make the colors
// update effective.
s.refresh();
});
// When the stage is clicked, we just color each
// node and edge with its original color.
s.bind('clickStage', function(e) {
s.graph.nodes().forEach(function(n) {
n.color = n.originalColor;
});
s.graph.edges().forEach(function(e) {
e.color = e.originalColor;
});
// Same as in the previous event:
s.refresh();
});
}
);
</script>
<!-- [...] -->
I hope this goes some way to answering your question.
You have a tagcloud full of words, and when a word is clicked, you want to trigger the neighbors method on your sigma instance, for which you need the node id.
Simply put, you need the function which is called when the #hashtag is clicked, to be in the same scope as the sigma instantiation.
s= new sigma({
settings: {...}
})
//more code instantiating methods etc
//let's assume your tags are in elements with class='tagword' and have the hashtag stored in a 'name' attribute
$('.tagword').on('click', function(){
var name = this.attr('name');
s.graph.nodes().forEach(function(n){
if (n.label == name){
//use the node to trigger an event in sigma
//i.e. s.graph.neighbors(n.id);
};
};
};
I'd like to mimic a particular behavior of spreadsheets with SlickGrid. The user:
clicks on a cell to activate it
enters =sum(, or whatever formula,
the original cell address is saved
the user selects the cell range (I assume that the original cell closes the editor)
focus is returned to the original cell with the new cell range appended. i.e. =sum(r1c1,r2c2).
What's throwing me off is the need to change focus.
var cell_with_formula = null;
grid = new Grid($("#myGrid"), data, columns, options);
// save original cell address, but there is no onBlur event
grid.onBlur = function(args){
cell_with_formula = args; // save address
};
grid.onCellRangeSelected = function(){
if(cell_with_formula){
// check if cell_with_formula has `=` at begining
// if so, append selected range
cell_with_formula = null;
}
};
Thanks in advance!
This is not possible in SlickGrid 1.4.x, but is going to be supported in the version 2.0 that is currently still under active development. The alpha is hosted in a separate branch on GitHub - https://github.com/mleibman/SlickGrid/tree/v2.0a, and I just checked in preliminary code that supports this with an example. Please see https://github.com/mleibman/SlickGrid/commit/17b1bb8f3c43022ee6aec89dcab185cd368b8785.
Here's a basic formula editor implementation:
function FormulaEditor(args) {
var _self = this;
var _editor = new TextCellEditor(args);
var _selector;
$.extend(this, _editor);
function init() {
// register a plugin to select a range and append it to the textbox
// since events are fired in reverse order (most recently added are executed first),
// this will override other plugins like moverows or selection model and will
// not require the grid to not be in the edit mode
_selector = new Slick.CellRangeSelector();
_selector.onCellRangeSelected.subscribe(_self.handleCellRangeSelected);
args.grid.registerPlugin(_selector);
}
this.destroy = function() {
_selector.onCellRangeSelected.unsubscribe(_self.handleCellRangeSelected);
grid.unregisterPlugin(_selector);
_editor.destroy();
};
this.handleCellRangeSelected = function(e, args) {
_editor.setValue(_editor.getValue() + args.range);
};
init();
}