I want to add functionality when clicking on items in the legend in Rickshaw. I use the standard code to add toggle functionality, which I want to extend to run my own function:
shelving = new Rickshaw.Graph.Behavior.Series.Toggle( {
graph: graph,
legend: legend
}
Is there a way add a function of my own here? I also tried looking through and editing the code in Rickshaw.Graph.Behavior.Series.Toggle.js, but I couldn't get anything to run upon clicking items in the legend. (Might it be that the imported js file is cached so that my edits don't take effect?)
As you explicitly asked about what source code to modify, I highlighted it below.
If you modify the source, upgrading to a new version becomes much harder, extending/creating own version would be the preferred way to go.
In Rickshaw.Graph.Behavior.Series.Toggle.js
'Rickshaw.Graph.Behavior.Series.Toggle.js' change the following
Rickshaw.Graph.Behavior.Series.Toggle = function(args) {
...
this._addBehavior = function() {
this.graph.series.forEach( function(s) {
s.disable = function() {
if (self.graph.series.length <= 1) {
throw('only one series left');
}
s.disabled = true;
alert('disabling ' + s.name); //HERE
self.graph.update();
};
s.enable = function() {
s.disabled = false;
alert('enabling ' + s.name); //HERE
self.graph.update();
};
} );
};
...
};
Related
I'm trying to give a user the ability to swap around the toolbar between presets based on a button press. Ideally that button would be in the CKEditor screen, but I can play around with it.
I've found this SO Article in which I get the concept that you destroy your instance, and re-initialize it with a 'New Config'.
To follow suit I took one of the higher ranked responses, modified it to match my table, and threw it onto a button.
function toolbarSwap(){
var editor = CKEDITOR.instances.editor;
if (editor) { editor.destroy(true); }
CKEDITOR.config.toolbar_Basic = [['Bold','Italic','Underline',
'-','JustifyLeft','JustifyCenter','JustifyRight','-','Undo','Redo']];
CKEDITOR.config.toolbar = 'Basic';
CKEDITOR.config.width=400;
CKEDITOR.config.height=300;
CKEDITOR.replace('editor', CKEDITOR.config);
}
The replace command concerns me since I can't see it working, as to if the data within the editor will go away but either way nothing is happening when I click the button that runs this function.
Is this the best method, or is there a way I can do this within CKEditor directly?
Update
function toolbarSwap(){
var editor = CKEDITOR.instances['editor'];
if (editor) { editor.destroy(true); }
CKEDITOR.config.toolbar_Basic = [['Bold','Italic','Underline',
'-','JustifyLeft','JustifyCenter','JustifyRight','-','Undo','Redo']];
CKEDITOR.config.toolbar = 'Basic';
CKEDITOR.config.width=400;
CKEDITOR.config.height=300;
CKEDITOR.replace('editor', CKEDITOR.config);
}
It seems like modified the instantiation of editor with the ID resolves the issue of it finding it, but the Editor is being emptied every time I click it. Is there a way to reload the config, instead of destroying the instance?
Update 2
function changeToolBar() {
var expanded = true;
if (expanded === true) {
var myToolBar = [{ name: 'verticalCustomToolbar', groups: [ 'basicstyles'], items: [ 'Bold', 'Italic'] }];
var config = {};
config.toolbar = myToolBar;
CKEDITOR.instances.editor.destroy();//destroy the existing editor
CKEDITOR.replace('editor', config);
expanded = false;
} else {
CKEDITOR.instances.editor.destroy();//destroy the existing editor
CKEDITOR.replace('editor', {toolbar: 'Full'});
expanded = true;
console.log("Expand me")
};
}
Here is where I'm at so far. I am not losing the data during the re-init, but I am unable to ever get the 'else' statement to trigger.
My Function was correct, but the var being initialized -in- the function was resetting it's purpose everything. AKA It's -always- true on click
<button type="button" onclick="changeToolBar()">Swap It</button>
function changeToolBar() {
if (expanded === true) {
var myToolBar = [{ name: 'verticalCustomToolbar', groups: [ 'basicstyles'], items: [ 'Bold', 'Italic'] }]; //Generic placeholder
var config = {};
config.toolbar = myToolBar;
CKEDITOR.instances.editor.destroy();//destroy the existing editor
CKEDITOR.replace('editor', config);
console.log(expanded); // Logging to ensure change
expanded = false;
console.log(expanded); // Logging to ensure the change
} else {
CKEDITOR.instances.editor.destroy();//destroy the existing editor
CKEDITOR.replace('editor', {toolbar: 'Full'}); // Toolbar 'full' is a default one
expanded = true;
console.log("Expand me") // Logging if it works
};
}
Can also swap in Full with a predefined config for toolbars.
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 need to use CKEDITOR ( http://ckeditor.com/ ) like a simple
<input type="text" />
I modified the file config.js adding the following settings:
config.toolbar = 'SimpleVersion';
config.toolbar_SimpleVersion =
[
['Cut','Copy','Paste','PasteText'],
];
Then, in the main page:
editor = CKEDITOR.replace("mydiv", { toolbar : 'SimpleVersion' });
editor.config.height = 50;
editor.config.removePlugins = 'resize';
editor.config.resize_enabled = false;
It works, but I need, if it's possible, to remove the bottom part where "body" is written, and also inhibit the new line whenever the Return button is pressed.
I think that for the last one, it may be possible to use a JQuery trigger.
Removing plugin works good. But on new line below code worked for me:
editor.on('key', function(e) {
var key = e.data.keyCode;
if(key==13){
e.cancel();
}
}
);
I found the solution:
1) when I create the editor there is a configuration option to toggle the bottom part of the editor
editor = CKEDITOR.replace("mydiv", { toolbar : 'SimpleVersion', removePlugins : 'elementspath' });
2) to inhibite the return button I've created the following trigger on key:
editor.on('key', function(e) {
var key = e.data.keyCode;
if(key==13){
return false;
}
}
);
I would like to use the functionality of CodeMirror (such as linenumbering, wrapping, search, etc.) for plain text, without particular need of code highlightening but instead with Google Chrome spell checker or some other natural language (especially English) spell checking activated (I do not need to have it work on other browsers). How can I do this? Is it possible to write a plain text mode add-on that enables spell checking?
I actually integrated typo.js with CodeMirror while coding for NoTex.ch; you can have a look at it here CodeMirror.rest.js; I needed a way to get the reStructuredText markup spell checked, and since I use CodeMirror's excellent syntax highlighting capabilities, it was quite straight forward to do.
You can check the code at the provided link, but I'll summarize, what I've done:
Initialize the typo.js library; see also the author's blog/documentation:
var typo = new Typo ("en_US", AFF_DATA, DIC_DATA, {
platform: 'any'
});
Define a regular expression for your word separators:
var rx_word = "!\"#$%&()*+,-./:;<=>?#[\\\\\\]^_`{|}~";
Define an overlay mode for CodeMirror:
CodeMirror.defineMode ("myoverlay", function (config, parserConfig) {
var overlay = {
token: function (stream, state) {
if (stream.match (rx_word) &&
typo && !typo.check (stream.current ()))
return "spell-error"; //CSS class: cm-spell-error
while (stream.next () != null) {
if (stream.match (rx_word, false)) return null;
}
return null;
}
};
var mode = CodeMirror.getMode (
config, parserConfig.backdrop || "text/x-myoverlay"
);
return CodeMirror.overlayMode (mode, overlay);
});
Use the overlay with CodeMirror; see the user manual to figure out how exactly you do this. I've done it in my code so you could check it out there too, but I recommend the user manual.
Define CSS class:
.CodeMirror .cm-spell-error {
background: url(images/red-wavy-underline.gif) bottom repeat-x;
}
This approach works great for German, English and Spanish. With the French dictionary typo.js seems to have some (accent) problems, and languages like Hebrew, Hungarian, and Italian - where the number of affixes is long or the dictionary is quite extensive - it does not work really, since typo.js at its current implementation uses too much memory and is too slow.
With German (and Spanish) typo.js can block the JavaScript VM for a few hundred milliseconds (but only during initialization!), so you might want to consider background threads with HTML5 web workers (see CodeMirror.typo.worker.js for an example). Further typo.js does not seem to support Unicode (due to JavaScript restrictions): At least, I did not manage to get it to work with non-Latin languages like Russian, Greek, Hindi etc.
I've not refactored the described solution into a nice separate project apart from (now quite big) NoTex.ch, but I might do it quite soon; till then you've to patch your own solution based on the above description or hinted code. I hope this helps.
In CodeMirror 5.18.0 and above, you can set inputStyle: 'contenteditable' with spellcheck: true to be able to use your web browser's spellcheck features. For example:
var myTextArea = document.getElementById('my-text-area');
var editor = CodeMirror.fromTextArea(myTextArea, {
inputStyle: 'contenteditable',
spellcheck: true,
});
The relevant commits that made this solution possible are:
Make inputStyle an option, move relevant logic into TextareaInput (CodeMirror 5.0.0, February 2015)
Add experimental spellcheck option (CodeMirror 5.18.0, August 2016)
This is a working version of hsk81's answer. It uses CodeMirror's overlay mode, and looks for any word inside quotes, html tags, etc. It has a sample typo.check that should be replaced with something like Typo.js. It underlines unknown words with a red squiggly line.
This was tested using an IPython's %%html cell.
<style>
.CodeMirror .cm-spell-error {
background: url("https://raw.githubusercontent.com/jwulf/typojs-project/master/public/images/red-wavy-underline.gif") bottom repeat-x;
}
</style>
<h2>Overlay Parser Demo</h2>
<form><textarea id="code" name="code">
</textarea></form>
<script>
var typo = { check: function(current) {
var dictionary = {"apple": 1, "banana":1, "can't":1, "this":1, "that":1, "the":1};
return current.toLowerCase() in dictionary;
}
}
CodeMirror.defineMode("spell-check", function(config, parserConfig) {
var rx_word = new RegExp("[^\!\"\#\$\%\&\(\)\*\+\,\-\.\/\:\;\<\=\>\?\#\[\\\]\^\_\`\{\|\}\~\ ]");
var spellOverlay = {
token: function (stream, state) {
var ch;
if (stream.match(rx_word)) {
while ((ch = stream.peek()) != null) {
if (!ch.match(rx_word)) {
break;
}
stream.next();
}
if (!typo.check(stream.current()))
return "spell-error";
return null;
}
while (stream.next() != null && !stream.match(rx_word, false)) {}
return null;
}
};
return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "text/html"), spellOverlay);
});
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "spell-check"});
</script>
CodeMirror is not based on an HTML textarea, so you can't use the built-in spell check
You could implement your own spell check for CodeMirror with something like typo.js
I don't believe anyone has done this yet.
I wrote a squiggly underline type spell checker a while ago. It needs a rewrite to be honest I was very new to JavaScript then. But the principles are all there.
https://github.com/jameswestgate/SpellAsYouType
I created a spellchecker with typos suggestions/corrections:
https://gist.github.com/kofifus/4b2f79cadc871a29439d919692099406
demo: https://plnkr.co/edit/0y1wCHXx3k3mZaHFOpHT
Below are relevant parts of the code:
First I make a promise to get load the dictionaries. I use typo.js for the dictionary, loading can take a while if they are not hosted locally, so it is better to start the loading as soon as your starts before login/CM initializing etc:
function loadTypo() {
// hosting the dicts on your local domain will give much faster results
const affDict='https://rawgit.com/ropensci/hunspell/master/inst/dict/en_US.aff';
const dicDict='https://rawgit.com/ropensci/hunspell/master/inst/dict/en_US.dic';
return new Promise(function(resolve, reject) {
var xhr_aff = new XMLHttpRequest();
xhr_aff.open('GET', affDict, true);
xhr_aff.onload = function() {
if (xhr_aff.readyState === 4 && xhr_aff.status === 200) {
//console.log('aff loaded');
var xhr_dic = new XMLHttpRequest();
xhr_dic.open('GET', dicDict, true);
xhr_dic.onload = function() {
if (xhr_dic.readyState === 4 && xhr_dic.status === 200) {
//console.log('dic loaded');
resolve(new Typo('en_US', xhr_aff.responseText, xhr_dic.responseText, { platform: 'any' }));
} else {
console.log('failed loading aff');
reject();
}
};
//console.log('loading dic');
xhr_dic.send(null);
} else {
console.log('failed loading aff');
reject();
}
};
//console.log('loading aff');
xhr_aff.send(null);
});
}
Second I add an overlay to detect and mark typos like this:
cm.spellcheckOverlay={
token: function(stream) {
var ch = stream.peek();
var word = "";
if (rx_word.includes(ch) || ch==='\uE000' || ch==='\uE001') {
stream.next();
return null;
}
while ((ch = stream.peek()) && !rx_word.includes(ch)) {
word += ch;
stream.next();
}
if (! /[a-z]/i.test(word)) return null; // no letters
if (startSpellCheck.ignoreDict[word]) return null;
if (!typo.check(word)) return "spell-error"; // CSS class: cm-spell-error
}
}
cm.addOverlay(cm.spellcheckOverlay);
Third I use a list box to show suggestions and fix typos:
function getSuggestionBox(typo) {
function sboxShow(cm, sbox, items, x, y) {
let selwidget=sbox.children[0];
let options='';
if (items==='hourglass') {
options='<option>⌛</option>'; // hourglass
} else {
items.forEach(s => options += '<option value="' + s + '">' + s + '</option>');
options+='<option value="##ignoreall##">ignore all</option>';
}
selwidget.innerHTML=options;
selwidget.disabled=(items==='hourglass');
selwidget.size = selwidget.length;
selwidget.value=-1;
// position widget inside cm
let cmrect=cm.getWrapperElement().getBoundingClientRect();
sbox.style.left=x+'px';
sbox.style.top=(y-sbox.offsetHeight/2)+'px';
let widgetRect = sbox.getBoundingClientRect();
if (widgetRect.top<cmrect.top) sbox.style.top=(cmrect.top+2)+'px';
if (widgetRect.right>cmrect.right) sbox.style.left=(cmrect.right-widgetRect.width-2)+'px';
if (widgetRect.bottom>cmrect.bottom) sbox.style.top=(cmrect.bottom-widgetRect.height-2)+'px';
}
function sboxHide(sbox) {
sbox.style.top=sbox.style.left='-1000px';
}
// create suggestions widget
let sbox=document.getElementById('suggestBox');
if (!sbox) {
sbox=document.createElement('div');
sbox.style.zIndex=100000;
sbox.id='suggestBox';
sbox.style.position='fixed';
sboxHide(sbox);
let selwidget=document.createElement('select');
selwidget.multiple='yes';
sbox.appendChild(selwidget);
sbox.suggest=((cm, e) => { // e is the event from cm contextmenu event
if (!e.target.classList.contains('cm-spell-error')) return false; // not on typo
let token=e.target.innerText;
if (!token) return false; // sanity
// save cm instance, token, token coordinates in sbox
sbox.codeMirror=cm;
sbox.token=token;
let tokenRect = e.target.getBoundingClientRect();
let start=cm.coordsChar({left: tokenRect.left+1, top: tokenRect.top+1});
let end=cm.coordsChar({left: tokenRect.right-1, top: tokenRect.top+1});
sbox.cmpos={ line: start.line, start: start.ch, end: end.ch};
// show hourglass
sboxShow(cm, sbox, 'hourglass', e.pageX, e.pageY);
// let the ui refresh with the hourglass & show suggestions
setTimeout(() => {
sboxShow(cm, sbox, typo.suggest(token), e.pageX, e.pageY); // typo.suggest takes a while
}, 100);
e.preventDefault();
return false;
});
sbox.onmouseleave=(e => {
sboxHide(sbox)
});
selwidget.onchange=(e => {
sboxHide(sbox)
let cm=sbox.codeMirror, correction=e.target.value;
if (correction=='##ignoreall##') {
startSpellCheck.ignoreDict[sbox.token]=true;
cm.setOption('maxHighlightLength', (--cm.options.maxHighlightLength) +1); // ugly hack to rerun overlays
} else {
cm.replaceRange(correction, { line: sbox.cmpos.line, ch: sbox.cmpos.start}, { line: sbox.cmpos.line, ch: sbox.cmpos.end});
cm.focus();
cm.setCursor({line: sbox.cmpos.line, ch: sbox.cmpos.start+correction.length});
}
});
document.body.appendChild(sbox);
}
return sbox;
}
Hope this helps !
So I have datatable setup using the YUI 2.0. And for one of my column definitions, I've set it up so when you click on a cell, a set of radio button options pops so you can modify that cell content.
I want whatever changes are made to be reflected in the database. So I subscribe to a radioClickEvent. Here is my code for that:
Ex.myDataTable.subscribe("radioClickEvent", function(oArgs){
// hold the change for now
YAHOO.util.Event.preventDefault(oArgs.event);
// block the user from doing anything
this.disable();
// Read all we need
var elCheckbox = oArgs.target,
newValue = elCheckbox.checked,
record = this.getRecord(elCheckbox),
column = this.getColumn(elCheckbox),
oldValue = record.getData(column.key),
recordIndex = this.getRecordIndex(record),
session_code = record.getData(\'session_code\');
alert(newValue);
// check against server
YAHOO.util.Connect.asyncRequest(
"GET",
"inlineeddit.php?sesscode=session_code&",
{
success:function(o) {
alert("good");
var r = YAHOO.lang.JSON.parse(o.responseText);
if (r.replyCode == 200) {
// If Ok, do the change
var data = record.getData();
data[column.key] = newValue;
this.updateRow(recordIndex,data);
} else {
alert(r.replyText);
}
// unblock the interface
this.undisable();
},
failure:function(o) {
alert("bad");
//alert(o.statusText);
this.undisable();
},
scope:this
}
);
});
Ex.myDataTable.subscribe("cellClickEvent", Ex.myDataTable.onEventShowCellEditor);
But when I run my code and I click on a cell and I click a radio button, nothing happens ever. I've been looking at this for some time and I have no idea what I'm doing wrong. I know you can also use asyncsubmitter within my column definition I believe and I tried that, but that also wasn't working for me.
Any ideas would be greatly appreciated.