How to detect a slide change in Slidy? - knitr

I use rmarkdown to produce a Slidy slide show using slidy_presentation. I would like to execute some Javascript when a slide is shown. In ioslides_presentation there's a slideenter event triggered when a page is shown (see Event when slide is displayed in ioslides?), but I don't see anything like that in Slidy.
I can detect whether a particular slide is being shown (by looking at the class of its div), but I don't know how to trigger the code to do that.
Is there some way to detect that a slide change is happening?

I researched a little and found an easy hack on how to detect which slide is currently shown:
Using a MutationObserver
In the following script we use a MutationObserver (see here and here) to observe changes in the content of the title element. Having the title we can extract the integer part of it, which represents a slide number.
In this example we just show an alert containing the text "Current Slide: " followed by the number we extracted.
Here ist the JavaScript snippet:
<script>
var target = document.querySelector('head > title');
var observer = new window.WebKitMutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
var title = mutation.target.textContent;
var current = +title.toString().match(/\d+/g);
alert('Current Slide: ' + current);
});
});
observer.observe(target, { subtree: true, characterData: true, childList: true });
</script>
Since we don't want the snippet to be interpreted as the content of a slide itself, we include it via the YAML header. Check the reproducible example below, where header.html contains only the the javascript snippet above:
MRE:
---
title: "Untitled"
author: "Martin Schmelzer"
date: "18 3 2017"
output:
slidy_presentation:
includes:
in_header: header.html
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
## R Markdown
Slide 2 (Title slide is #1!)
## Slide with Bullets
This is slide 3!
## Slide with R Output
...and number 4!
EDIT: Modified script for ioslides
If somehow needed in ioslides, you can modify the JavaScript snippet the following way:
<script>
$(document).ready(function() {
var target = document.querySelector('slides');
var observer = new window.WebKitMutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.target.getAttribute('class').indexOf('current') != -1 ) {
alert(mutation.target.getAttribute('data-slide-num'));
}
});
});
observer.observe(target, { subtree: true, attributes: true});
});
</script>
In slidy each slide consists of its own div container. The approach in ioslides is different. Here we only have a couple of <slide> elements for the past, current, next and far-next slides. The current slide element is the one which has the class current. So we just search for that class inside the forEach loop and grab it's data-slide-num attribute.

Related

How can I rename the "Font Family" dropdown in TinyMCE?

I'd like it to simply be labeled "Font" as this is more useful to my non technical users. I can't seem to find a way to do this. Using version 4.x
you can change it in the tinymce.min.js file. just search for the text "Font Family" and you will see where you can change it.
You can do this by changing the default value in tinymce.min.js file. Just search for the code part beginning with:
{type:"listbox",text:"Font Family",tooltip:"Font Family",values:i,
...
Then put your desired label (for example "Font") inside the text:"". After change it should be look like :
{type:"listbox",text:"Font",tooltip:"Font Family",values:i,.
Result:
If you want to do this programmatically, for example create a button that will change label on click you can add this code when initializing tinyMCE:
<script type="text/javascript">
tinymce.init({
selector: 'textarea',
toolbar1: 'fontselect fontsizeselect mybutton',
//...
// extra coding can go here
//...
setup: function(editor) {
editor.addButton('mybutton', {
text: 'My button',
icon: false,
onclick: function() {
var fontlabel = "Font";
document.getElementsByTagName("button")[4].firstChild.innerText = fontlabel;
// getElementsByTagName is an array, so the index changes belonging to
// position of your "FontFamily" dropdown. In my example it' 6th.
}
});
}
});
</script>
Before:
After:

Class prefix as selector for each function

I am able to do this using an ID prefix as the selector, but I need to be able to do it with classes instead. It's an each function for opening up different modal windows on the same page. I need to avoid using ID names because I have some modal windows that will have multiple links on the same page, and when using IDs, only the first link will work.
So here's the function as it works with IDs:
$('div[id^=ssfamodal-help-]').each(function() {
var sfx = this.id,
mdl = $(this),
lnk = $('.link-' + sfx),
cls = $('.ssfamodal-close'),
con = $('.ssfamodal-content');
lnk.click(function(){
mdl.show();
});
cls.click(function(){
mdl.hide();
});
mdl.click(function() {
mdl.hide();
});
con.click(function() {
return false;
});
});
and I'm trying to change it to classes instead, like:
$('div[class^=ssfamodal-help-]').each(function() {
var sfx = this.attr('class'),
etc.
But I cannot get it to work without using IDs. Is it possible?
EDIT Fixed error with semi-colon at end of Vars, and updated Fiddle with the fix. Still not working though.
Here's a Fiddle
** UPDATE **
To be clearer, I need to be able to refer to the same modal more than once on the same page. E.g.:
MODAL 1
MODAL 2
MODAL 3
MODAL 4
LINK TO MODAL 1
LINK TO MODAL 2
LINK TO MODAL 3
LINK TO MODAL 4
OTHER STUFF
LINK TO MODAL 1
LINK TO MODAL 4
LINK TO MODAL 3
OTHER STUFF
LINK TO MODAL 2
ETC.
When using classes get rid of the ID habit :
className1, className2, className3 ... etc
simply use
className
HTML:
<div class="ssfamodal-help-base ssfamodal-backdrop">
<div id="help-content" class="ssfamodal-content">
<span class="ssfamodal-close">[x]</span>
Howdy
</div>
</div>
<div class="ssfamodal-help-base ssfamodal-backdrop">
<div id="help-content" class="ssfamodal-content">
<span class="ssfamodal-close">[x]</span>
Howdy Ho
</div>
</div>
<span class="link-ssfamodal-help-base">One</span>
<span class="link-ssfamodal-help-base">Two</span>
LIVE DEMO
var $btn = $('.link-ssfamodal-help-base'),
$mod = $('.ssfamodal-help-base'),
$X = $('.ssfamodal-close');
$btn.click(function(i) {
var i = $('[class^="link-"]').index(this); // all .link-** but get the index of this!
// Why that?! cause if you only do:
// var i = $('.link-ssfamodal-help-base').index();
// you'll get // 2
// cause that element, inside a parent is the 3rd element
// but retargeting it's index using $('className').index(this);
// you'll get the correct index for that class name!
$('.ssfamodal-help-base').eq(i).show() // Show the referenced element by .eq()
.siblings('.ssfamodal-help-base').hide(); // hide all other elements (with same class)
});
$X.click(function(){
$(this).closest('.ssfamodal-help-base').hide();
});
From the DOCS:
http://api.jquery.com/eq/
http://api.jquery.com/index/
http://api.jquery.com/closest/
Here I created a quite basic example on how you can create a jQuery plugin of your own to handle modals: http://jsbin.com/ulUPIje/1/edit
feel free to use and abuse.
The problem is that class attributes can consist of many classes, rather than IDs which only have one value. One solution, which isn't exactly clean, but seems to work is the following.
$('div').filter(function () {
var classes = $(this).attr('class').split(/\s+/);
for (var i = 0; i < classes.length; i++)
if (classes[i].indexOf('ssfamodal-help-') == 0)
return true;
return false;
}).each(function() {
// code
});
jsFiddle
Or, equivalently
$('div').filter(function () {
return $(this).attr('class').split(/\s+/).some(function (e) {
return e.indexOf('ssfamodal-help-') == 0;
});
}).each(function() {
// code
});
jsFiddle
If there is one-to-one relationship between the modal helps and the modal links which it appears there is...can simplfy needing to match class values by using indexing.
For this reason you don't need unique class names, rather they just overcomplicate things. Following assumes classes stay unique however
var $helps=$('div[id^=ssfamodal-help-]');
var $help_links=$('div[id^=link-ssfamodal-help-]');
$help_links.click(function(){
var linkIndex= $help_links.index(this);
$helps.hide().eq( linkIndex ).show();
});
/* not sure if this is what's wanted, but appeared original code had it*/
$helps.click(function(){
$(this).hide()
})
/* close buttons using traverse*/
$('.ssfamodal-close').click(function(){
$(this).closest('div[id^=ssfamodal-help-]' ).hide();
});
Also believe that this code is a little more readable than original apporach
DEMO
Can you try this,
$('div[class^=ssfamodal-help-]').each(function() {
var sfx = $(this).attr('class');
console.log(sfx);
/*console log:
ssfamodal-help-base ssfamodal-backdrop
ssfamodal-help-base2 ssfamodal-backdrop
*/
});
Demo: http://jsfiddle.net/xAssR/51/
why don't you write like
$('div.classname').each(function() {
// you can write your desired code here
var sfx = this.attr('class');
var aa= this.attr('id');
});
or
$('.classname').each(function() {
// you can write your desired code here
var sfx = this.attr('class');
var aa= this.attr('id');
});
where classname is the name of the class used for the div in html
Thanks.

Avoid Ext.form validation scrolling to top

I have a Ext.form.Panel inside Ext.window. Form height is more than window height so I have vertical scroll on window.
On form fields validation (on validitychange event) scroll jumps to the top.
How to avoid this behaviour?
I tried to figure out, why one of my forms did scroll up and other did not. Turned out, that I have forgot to explicitly specify layout manager and that default layout manager (anchor) scrolled to top on validity change, while vbox layout did not. While everything looked exactly the same (vbox with align: 'stretch'), it behaved differently when the error was either shown or hidden.
I have the same problem :(
I made a creepy workaround (it works to 80%) Sometimes it still jumps to the top.
You should know, that I have a window with a layout of 'form'. If you have a window with (for example) a layout of 'fit' with an xtype of 'form' - you may have to change the code a little bit.
For example the line el.child(".x-window-body", fasle) wouldn't work.
init: function() {
this.control({
...
/** My Ext.window.Window is called reservationwindow **/
'reservationwindow': {
afterrender: function(comp) {
// comp is this Ext.Component == wrapper
var el = comp.getEl();
//extjs adds the scrollbar to the body element...
var elForm = el.child(".x-window-body", false);
// or el.child(".x-panel-body", false);
//we are listinig to the scroll-event now
this.myFormEl = elForm;
this.safeScroll = {top:0, left:0};
elForm.on('scroll', function() {
console.log("save");
this.safeScroll = this.myFormEl.getScroll();
}, this);
elForm.on('click', function() {
var resWin = this.getResWin();
resWin.scrollBy(0,this.safeScroll.top,false);
console.log("reset");
}, this);
elForm.on('keyup', function() {
var resWin = this.getResWin();
resWin.scrollBy(0, this.safeScroll.top, false);
console.log("reset");
}, this);
}
As you can see, I am listening to the scroll-event and safe and reset the scroll bar. Sometimes (especially if you are writing very quickly in a textbox) the events come in a different order and the page will still jump to the top. Sometimes you also see it flickering around (if it needs too long to set it back to the original position).
So.... As I said, its a creepy workaround.
If you find a better solution, please let me know.
EDIT
I also figured out, that the grow option on a textareafield was one of the troublemakers.
{
id: this.id + '-details',
xtype: 'textareafield',
// grow: true, now it isn't jumping
name: 'message',
fieldLabel: 'Zusätzliche Informationen',
labelAlign: 'top',
renderder: 'htmlEncode',
disabled: isDisabled,
anchor: '100%'
}

sort multiple items at once with jquery.ui.sortable

did somebody manage to sort multiple items at once with jquery.ui.sortable?
we are working on a photo managing app.
select multiple items
drag them to a new location.
thanx
I had a similar requirement, but the solution in the accepted answer has a bug. It says something like "insertBefore of null", because it removes the nodes.
And also i tried jQuery multisortable, it stacks the selected items on top of each other when dragging, which is not what i want.
So I rolled out my own implementation and hope it will save others some time.
Fiddle Link.
Source code:
$( "#sortable" ).sortable({
// force the cursor position, or the offset might be wrong
cursorAt: {
left: 50,
top: 45
},
helper: function (event, item) {
// make sure at least one item is selected.
if (!item.hasClass("ui-state-active")) {
item.addClass("ui-state-active").siblings().removeClass("ui-state-active");
}
var $helper = $("<li><ul></ul></li>");
var $selected = item.parent().children(".ui-state-active");
var $cloned = $selected.clone();
$helper.find("ul").append($cloned);
// hide it, don't remove!
$selected.hide();
// save the selected items
item.data("multi-sortable", $cloned);
return $helper;
},
stop: function (event, ui) {
// add the cloned ones
var $cloned = ui.item.data("multi-sortable");
ui.item.removeData("multi-sortable");
// append it
ui.item.after($cloned);
// remove the hidden ones
ui.item.siblings(":hidden").remove();
// remove self, it's duplicated
ui.item.remove();
}
});
There's a jQuery UI plugin for that: https://github.com/shvetsgroup/jquery.multisortable
jsFiddle: http://jsfiddle.net/neochief/KWeMM/
$('ul.sortable').multisortable();
... or just define a 'items' option to your multisortable that way (for example) :
$('table tbody').multisortable({
items: 'tr'
});
you can use shvetsgroup/jquery.multisortable
but it will create problem.. because, that js is designed only for tags...
but customize it to use it, its very simple i'll tell you how????
at first download that .js and use it in your program...
step 1. open the js file...now edit the following lines...
$.fn.multiselectable.defaults = {
click: function(event, elem) {},
mousedown: function(event, elem) {},
selectedClass: 'selected',
items: 'li'
};
the above are lines from 107 to 112....
there you can see "items: 'li'
in that use your tag which you are used to enclose those image like if you are using, or or anything you are using like this
$.fn.multiselectable.defaults = {
click: function(event, elem) {},
mousedown: function(event, elem) {},
selectedClass: 'selected',
items: 'div' // or any tag you want...
};
and 249 to 254
selectedClass: 'selected',
placeholder: 'placeholder',
items: 'li'
};
}(jQuery);
change the line " item:'li' " with your tag like this
selectedClass: 'selected',
placeholder: 'placeholder',
items: 'div' // or anything else
};
}(jQuery);
if you are working on textboxes inside those envelopes.. you have to get rid of these lines too
// If no previous selection found, start selecting from first selected item.
prev = prev.length ? prev : $(parent.find('.' + options.selectedClass)[0]).addClass('multiselectable-previous');
var prevIndex = prev.index();
after that comment line...
add a line code that search textbox or check box or any interaction element inside it...
like this..
// If no previous selection found, start selecting from first selected item.
item.children("input").focus(); // customize this code to get your element focus...
prev = prev.length ? prev : $(parent.find('.' + options.selectedClass)[0]).addClass('multiselectable-previous');
var prevIndex = prev.index();
and also to indicate selected tags or elements... use styles like this
div { margin: 2px 0; cursor: pointer; }
div.selected { background-color: orange }
div.child { margin-left: 20px; }
actually i used div.. instead of that you can use any tag you wish...
hope will help u.... if it is not... read again.. and ask again....
wishes

Drag from Tree to div

I am trying to implement a drag and drop senario from an extJs TreePanel into a div in the body of the page. I have been following an example by Saki here.
So far I have the below code:
var contentAreas = new Array();
var tree = new Ext.tree.TreePanel({
title : 'Widgets',
useArrows: true,
autoScroll: true,
animate: true,
enableDrag: true,
border: false,
layout:'fit',
ddGroup:'t2div',
loader:new Ext.tree.TreeLoader(),
root:new Ext.tree.AsyncTreeNode({
expanded:true,
leaf:false,
text:'Tree Root',
children:children
}),
listeners:{
startdrag:function() {
$('.content-area').css("outline", "5px solid #FFE767");
},
enddrag:function() {
$('.content-area').css("outline", "0");
}
}
});
var areaDivs = Ext.select('.content-area', true);
Ext.each(areaDivs, function(el) {
var dd = new Ext.dd.DropTarget(el, {
ddGroup:'t2div',
notifyDrop:function(ddt, e, node) {
alert('Drop');
return true;
}
});
contentAreas[contentAreas.length] = dd;
});
The drag begins and the div highlights but when I get over the div it does not show as a valid drop target and the drop fails.
This is my first foray into extJS. I'm JQuery through and through and I am struggling at the moment.
Any help would be appreciated.
Ian
Edit
Furthermore if I create a panel with a drop target in it, this works fine. What is the difference between creating an element and selecting an existing element from the dom. This is obviously where I am going wrong but I'm none the wiser. I have to be able to select existing dom elements and make them into drop targets so the code below is not an option.
Here is the drop target that works
var target = new Ext.Panel({
renderTo: document.body
,layout:'fit'
,id:'target'
,bodyStyle:'font-size:13px'
,title:'Drop Target'
,html:'<div class="drop-target" '
+'style="border:1px silver solid;margin:20px;padding:8px;height:140px">'
+'Drop a node here. I\'m the DropTarget.</div>'
// setup drop target after we're rendered
,afterRender:function() {
Ext.Panel.prototype.afterRender.apply(this, arguments);
this.dropTarget = this.body.child('div.drop-target');
var dd = new Ext.dd.DropTarget(this.dropTarget, {
// must be same as for tree
ddGroup:'t2div'
// what to do when user drops a node here
,notifyDrop:function(dd, e, node) {
alert('drop');
return true;
} // eo function notifyDrop
});
}
});
See if adding true as the second param here makes any difference:
var areaDivs = Ext.select('.content-area', true);
As a cosmetic note, the param name e conventionally indicates an event object (as in the second arg of notifyDrop). For an element, el is more typical. Doesn't matter functionally, but looks weird to someone used to Ext code to see e passed into the DropTarget constructor.
If you are having problem duplicating a working example such as that, copy the entire thing, then modify it to your needs line-by-line - you can't go wrong.
As i know you can't set DropZone to any Ext element, just to Ext component. So this might be you problem. Try to use DropTarget instead of DropZone.