i have phpbb 3.1 forum.
i have a- jquery.collapse.js.
i want that the defult will be collapsed insted of open.
wheat parameter do i need to change to do it?
and can o make it that in default only 1 is open and not close?
the default will be the 1st category to be open, and when i open the next one, the open one will close automatically.
the code-
(function($, exports) {
// Constructor
function Collapse (el, options) {
options = options || {};
var _this = this,
query = options.query || "> :even";
$.extend(_this, {
$el: el,
options : options,
sections: [],
isAccordion : options.accordion || false,
db : options.persist ? jQueryCollapseStorage(el.get(0).id) : false
});
// Figure out what sections are open if storage is used
_this.states = _this.db ? _this.db.read() : [];
// For every pair of elements in given
// element, create a section
_this.$el.find(query).each(function() {
new jQueryCollapseSection($(this), _this);
});
// Capute ALL the clicks!
(function(scope) {
_this.$el.on("click", "[data-collapse-summary] " + (scope.options.clickQuery || ""),
$.proxy(_this.handleClick, scope));
_this.$el.bind("toggle close open",
$.proxy(_this.handleEvent, scope));
}(_this));
}
Collapse.prototype = {
handleClick: function(e, state) {
e.preventDefault();
state = state || "toggle";
var sections = this.sections,
l = sections.length;
while(l--) {
if($.contains(sections[l].$summary[0], e.target)) {
sections[l][state]();
break;
}
}
},
handleEvent: function(e) {
if(e.target == this.$el.get(0)) return this[e.type]();
this.handleClick(e, e.type);
},
open: function(eq) {
this._change("open", eq);
},
close: function(eq) {
this._change("close", eq);
},
toggle: function(eq) {
this._change("toggle", eq);
},
_change: function(action, eq) {
if(isFinite(eq)) return this.sections[eq][action]();
$.each(this.sections, function(i, section) {
section[action]();
});
}
};
// Section constructor
function Section($el, parent) {
if(!parent.options.clickQuery) $el.wrapInner('<a href="#"/>');
$.extend(this, {
isOpen : false,
$summary : $el.attr("data-collapse-summary",""),
$details : $el.next(),
options: parent.options,
parent: parent
});
parent.sections.push(this);
// Check current state of section
var state = parent.states[this._index()];
if(state === 0) {
this.close(true);
}
else if(this.$summary.is(".open") || state === 1) {
this.open(true);
} else {
this.close(true);
}
}
Section.prototype = {
toggle : function() {
this.isOpen ? this.close() : this.open();
},
close: function(bypass) {
this._changeState("close", bypass);
},
open: function(bypass) {
var _this = this;
if(_this.options.accordion && !bypass) {
$.each(_this.parent.sections, function(i, section) {
section.close();
});
}
_this._changeState("open", bypass);
},
_index: function() {
return $.inArray(this, this.parent.sections);
},
_changeState: function(state, bypass) {
var _this = this;
_this.isOpen = state == "open";
if($.isFunction(_this.options[state]) && !bypass) {
_this.options[state].apply(_this.$details);
} else {
_this.$details[_this.isOpen ? "show" : "hide"]();
}
_this.$summary.toggleClass("open", state !== "close");
_this.$details.attr("aria-hidden", state === "close");
_this.$summary.attr("aria-expanded", state === "open");
_this.$summary.trigger(state === "open" ? "opened" : "closed", _this);
if(_this.parent.db) {
_this.parent.db.write(_this._index(), _this.isOpen);
}
}
};
// Expose in jQuery API
$.fn.extend({
collapse: function(options, scan) {
var nodes = (scan) ? $("body").find("[data-collapse]") : $(this);
return nodes.each(function() {
var settings = (scan) ? {} : options,
values = $(this).attr("data-collapse") || "";
$.each(values.split(" "), function(i,v) {
if(v) settings[v] = true;
});
new Collapse($(this), settings);
});
}
});
//jQuery DOM Ready
$(function() {
$.fn.collapse(false, true);
});
// Expose constructor to
// global namespace
exports.jQueryCollapse = Collapse;
exports.jQueryCollapseSection = Section;
})(window.jQuery, window);
the forum- limodim.com/1
thank you.
Related
I'm having issues with the new CAF receiver not registering the YouTube iframe player as a player, and that it is playing. After 5 minutes connected to the receiver, the connection is broken because it thinks that the player is idle.
This is the sender-code
var metadata = new chrome.cast.media.GenericMediaMetadata();
metadata.title = "Foo - Bar";
metadata.image = 'https://img.youtube.com/vi/IXNrHusLXoM/mqdefault.jpg';
metadata.images = ['https://img.youtube.com/vi/IXNrHusLXoM/mqdefault.jpg'];
var mediaInfo = new chrome.cast.media.MediaInfo();
mediaInfo.contentType = "video/*";
mediaInfo.contentId ="IXNrHusLXoM";
mediaInfo.duration = 300;
var request = new chrome.cast.media.LoadRequest();
request.media = mediaInfo;
request.customData = customData;
request.metadata = metadata;
castSession.loadMedia(request).then(
function() {
console.log('Load succeed');
},
function(errorCode) {
console.log('Error code: ' + errorCode);
});
The receiver code can be found here: https://github.com/zoff-music/zoff-chromecast-receiver/blob/feature/v3/receiver.js
Is there any way of having the new CAF receiver hook into the YouTube iframe player, or "manually" dispatch LOADED, BUFFERING, PLAYING, PAUSED, STOPPED events so that the receiver doesn't disconnect from the sender?
Edit: with the above code, the PlayerState gets to the BUFFERING stage, but stops there. The promise with the log "Load succeed" is never triggered.
I managed to trick the receiver with fake mediaEelement. You can see the code in pastebin
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
var yt_events = {};
var pause_request = false;
var yt_player;
var yt_video_fake = {
removeAttribute: function(attr) {
},
setAttribute: function(attr, val) {
},
getCurrentTimeSec: function() { return yt_player && yt_player.getCurrentTime ? yt_player.getCurrentTime() : 0; },
getDurationSec: function() { return yt_player ? yt_player.getDuration() : 0; },
getVolume: function() {
if(!yt_player || !yt_player.getVolume) {
return 0;
}
var volume = new cast.framework.messages.Volume();
volume.level = yt_player.getVolume() / 100;
volume.muted = yt_player.isMuted() ? true : false;
return volume;
},
setVolume: function(vol) { yt_player && yt_player.setVolume(vol.level * 100); },
getState: function() {
if(!yt_player || !yt_player.getPlayerState) {
return 'IDLE';
}
var state = yt_player.getPlayerState();
var _state;
if(pause_request) {
pause_request = false;
state = YT.PlayerState.PAUSED;
}
switch(state) {
default: case YT.PlayerState.UNSTARTED:
_state = 'IDLE';
break;
case YT.PlayerState.PLAYING:
_state = 'PLAYING';
break;
case YT.PlayerState.PAUSED:
_state = 'PAUSED';
break;
case YT.PlayerState.BUFFERING:
_state = 'BUFFERING';
break;
case YT.PlayerState.ENDED:
_state = 'ENDED';
break;
}
return _state;
},
addEventListener: function(e, func) { },
load: function() {},
play: function() { yt_player && yt_player.playVideo(); },
pause: function() { if(yt_player && yt_player.pauseVideo) {pause_request = true; yt_player.pauseVideo(); }},
seek: function(timeTo) { yt_player && yt_player.seekTo(timeTo, true);},
reset: function() {
if(yt_player) {
try { yt_player.destroy && yt_player.destroy(); } catch(e) {
//console.trace(e);
};
delete yt_player;
}
},
registerErrorCallback: function(func) { yt_events['error'] = func; },
registerEndedCallback: function(func) { yt_events['ended'] = func; },
registerLoadCallback: function(func) { yt_events['load'] = func; },
unregisterErrorCallback: function () { delete yt_events['error'] },
unregisterEndedCallback: function () { delete yt_events['ended']},
unregisterLoadCallback: function () { delete yt_events['load']}
};
Object.defineProperty(yt_video_fake, 'currentTime', {
val1: null,
get: function() { return yt_player && yt_player.getCurrentTime ? yt_player.getCurrentTime() : this.val1; },
set: function(newValue) {
yt_player && yt_player.seekTo(newValue, true);
this.val1 = newValue;
},
enumerable : true,
configurable : true
});
Object.defineProperty(yt_video_fake, 'volume', {
val1: null,
get: function() { var vol = this.getVolume(); if(vol) return vol['level']; return 1; },
set: function(newValue) {
yt_player && yt_player.setVolume && yt_player.setVolume(newValue * 100);
this.val1 = newValue;
},
enumerable : true,
configurable : true
});
Object.defineProperty(yt_video_fake, 'duration', {
val1: null,
get: function() { return this.getDurationSec(); },
set: function() {},
enumerable : true,
configurable : true
});
function YoutubePlayMedia(videoid) {
var yt_container = $('#yt_container');
if(!yt_container.length) {
yt_container = $('<div id="yt_container" style="position:absolute;top:0;left:0;width:100%;height:100%;"></div>');
$('body').append(yt_container);
}
yt_container.html('<iframe id="youtube_container" style="width:100%;height:100%;" frameborder="0" allowfullscreen="1" allow="autoplay; encrypted-media" title="YouTube video player" src="//www.youtube.com/embed/' + videoid +'?autoplay=1&enablejsapi=1&modestbranding=1&controls=0&fs=0&iv_load_policy=3&rel=0&cc_load_policy=1&cc_lang_pref=bg"></iframe>');
yt_player = new YT.Player('youtube_container', {
events: {
'onReady': function(e) {
yt_player.is_loaded = true;
yt_player.playVideo();
},
'onStateChange': function(e) {
switch(e.data) {
case YT.PlayerState.PLAYING:
if(yt_player.is_loaded) {
if(yt_events['load']) {
yt_events['load']();
}
}
break;
case YT.PlayerState.ENDED:
//yt_events['ended'] && yt_events['ended'](e);
break;
}
},
'onError': function(e) {
//yt_events['error'] && yt_events['error'](e);
}
}
});
}
function YoutubeLoadMedia(url) {
current_media_type = 'Youtube';
window.onYouTubeIframeAPIReady = function() {
window.youtube_loaded = true;
YoutubePlayMedia(url);
}
if(!window.youtube_script) {
window.youtube_script = document.createElement('script');
window.youtube_script.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(window.youtube_script, firstScriptTag);
} else {
// Вече имаме api направо действаме
YoutubePlayMedia(url);
}
}
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
loadRequestData => {
if (loadRequestData.media && loadRequestData.media.contentId) {
YoutubeLoadMedia(loadRequestData.media.contentId);
playerManager.setMediaElement(yt_video_fake);
return false;
}
return loadRequestData;
}
);
const options = new cast.framework.CastReceiverOptions();
options.disableIdleTimeout = true;
context.start(options);
The YouTube iframe player is not designed to be a player for a Cast receiver.
As Javascript newbie I'm not able to store locally a variable and reuse from Google cache.
I have a counter that starts onClick from zero then increments until a specific number then goes to a new page.
In the new page I need to continue the counter from the previous number.
I can't use cookies since this is for a Chrome extension, so I used storage.local and I set my manifest as follow:
...
"permissions": ["storage", "https://example.com/*", "unlimitedStorage"],
"content_scripts": [
{
"matches": ["https://example.com/*"],
"js": ["js/jquery-2.1.4.min.js", "js/example.js"],
"css": ["css/example.css"]
}
],
...
So basically when I click my button it starts the follow function and it increments this.count
function Button(t, e, i) {
var n = this;
n.$el = t,
n.count = (Record.get(function() {tmf.followBtn.counter()}) > 1 ? Record.get(function() {tmf.followBtn.counter()}) : 0),
n.$title = t.find(".tmf-btn__title"),
n.$subtitle = t.find(".tmf-btn__subtitle"),
n.action = new BulkAction(e, i), n.$el.on("click", function() {
n.$el.addClass("tmf-btn--active"),
n.$title.text(n.count),
n.action.paused ? (n.action.proceed(), n.$subtitle.text("Click to pause")) : (n.action.pause(), n.$subtitle.text("Click to continue"))
})
}
function Profile(t) {
this.$el = $(".ProfileCard:eq(" + t + ")"),
this.isLoaded() && (this.$btn = this.$el.find(".user-actions-follow-button"),
this.id = this.$el.data("user-id") + "-")
}
function BulkAction(t, e) {
this.callback = t,
this.interval = e,
this.paused = !0
}
function gotoPage() {
window.location.replace("https://example.com/newpage/");
}
$.fn.isPresent = function() {
return this.length > 0
};
var USER_ID = $("#user-dropdown").find("[data-user-id]").data("user-id"),
Record = {
storage: chrome.storage.local,
key: USER_ID.toString(),
get: function(t) {
var e = this;
this.storage.get(this.key, function(i) {
e.result = i, t.call()
})
},
set: function() {
this.storage.set(this.result)
},
add: function(t) {
this.result[this.key] ? this.result[this.key] += t : this.result[this.key] = t, this.set()
},
includes: function(t) {
return this.result[this.key] ? this.result[this.key].includes(t) : !1
}
},
tmf = {
init: function() {
var t = this;
t.$el = $("<div>").addClass("tmf animated").appendTo("body")
},
toggle: function() {
var t = this;
this.$el.removeClass("flipInY flipOutY tmf--active"), $(".ProfileCard").length > 10 ? t.$el.load(chrome.extension.getURL("html/actions.html"), {}, function() {
$(".tmf-btn").on("click", function() {
t.$el.addClass("tmf--active")
}), t.followBtn = new Button($(".tmf-btn--follow"), function() {
this.follow()
}, 100), t.unfollowBtn = new Button($(".tmf-btn--unfollow"), function() {
this.unfollow()
}, 100), t.$el.addClass("flipInY");
var e = new MutationObserver(function(e) {
e.forEach(function(e) {
var i = $("#message-drawer").html();
i.includes("66885") && t.followBtn.setIdle()
})
});
e.observe(document.getElementById("message-drawer"), {
subtree: !0,
characterData: !0,
childList: !0
}), $("#tmf_without_exception").on("change", function() {
t.withoutException = this.checked,
this.checked ? (t.followBtn.$subtitle.text("without exception"),t.unfollowBtn.$subtitle.text("without exception")) : (t.followBtn.$subtitle.text("who have never been unfollowed"), t.unfollowBtn.$subtitle.text("who do not follow you"))
})
}) : this.$el.addClass("flipOutY")
},
withoutException: !1
};
$.extend(Button.prototype, {
incrementCount: function() {
this.count++,
this.$title.text(this.count)
},
counter: function() {
return this.count
},
setIdle: function() {
var t = this;
if (!t.action.paused) {
t.action.pause();
}
}
}),
$.extend(Profile.prototype, {
isLoaded: function() {
return this.$el.isPresent()
},
isFollowing: function() {
return this.$el.find(".FollowStatus").isPresent()
},
isProtected: function() {
return this.$el.find(".Icon--protected").isPresent()
},
isNotFollowing: function() {
return !this.isFollowing()
},
isFollowable: function() {
return this.$btn.children(".follow-text").is(":visible")
},
isFollowed: function() {
return this.$btn.children(".following-text").is(":visible")
},
follow: function() {
if (tmf.followBtn.counter() > 1) {
tmf.followBtn.action.pause();
Record.set(function() {tmf.followBtn.counter()});
gotoPage();
};
this.isFollowable() && !this.isProtected() && tmf.followBtn.counter() < 6 && (tmf.withoutException || !Record.includes(this.id) ? (this.click(), tmf.followBtn.incrementCount()) : this.log("warn", "Already unfollowed once"))
},
unfollow: function() {
this.isFollowed() && (tmf.withoutException || this.isNotFollowing()) && (this.click(), tmf.unfollowBtn.incrementCount(), Record.add(this.id))
},
click: function() {
this.log("success", 'Clicked on "' + this.$btn.children(":visible").text().trim() + '"'), this.$btn.click(), this.clicked = !0
},
log: function(t, e) {
$("<div>").addClass("tmf-log").addClass("tmf-log--" + t).text(e).insertBefore(this.$el.find(".ProfileCard-bio"))
}
}),
$.extend(BulkAction.prototype, {
nth: 0,
loadAttempts: 0,
_loadProfiles: function() {
this.loadAttempts < 10 && (this.last.$btn.focus(), this.loadAttempts++, this._sleep(700))
},
_sleep: function(t) {
var e = this;
setTimeout(function() {
e.run()
}, t)
},
run: function() {
if (!this.paused) {
var t = new Profile(this.nth);
t.isLoaded() ? (this.loadAttempts = 0, this.callback.call(t), this.last = t, this.nth++, t.clicked ? this._sleep(this.interval) : this.run()) : this._loadProfiles()
}
},
pause: function() {
this.paused = !0
},
proceed: function() {
this.paused = !1, this.run()
}
}),
Record.get(function() {
tmf.init(), tmf.toggle()
});
var observer = new MutationObserver(function(t) {
t.forEach(function(t) {
tmf.toggle()
})
});
$(".tmf-btn--close").click(function(){
$(".flipInY").hide();
});
observer.observe(document.querySelector("head > title"), {
subtree: !0,
characterData: !0,
childList: !0
});
I need to store locally my counter, so as you can see I tried to set it as:
Record.set(function() {tmf.followBtn.counter()}); inside my follow function.
Then I tried to get it from my Button function as:
n.count = (Record.get(function() {tmf.followBtn.counter()}) > 1 ? Record.get(function() {tmf.followBtn.counter()}) : 0),
Why all the time I reload the page, the counter starts from zero?
I want to run this code without having to click on the chrome.browserAction.onClicked.addListener. How do I do this?
This is the code:
var ToggleJSApplication = {
triggerWin: null,
urlPattern: 'http://*',
init: function() {
var self = this;
self.updateIcon(function() {
chrome.browserAction.onClicked.addListener(self.toggleState.bind(self));
chrome.windows.onFocusChanged.addListener(self.onWinFocusChanged.bind(self));
});
},
getState: function(incognito, callback) {
var self = this,
data = {
'primaryUrl': self.urlPattern,
'incognito': incognito || false
};
chrome.contentSettings.javascript.get(data, function(state) {
state.enabled = (state.setting === 'allow');
if (typeof callback === 'function') callback(state);
});
},
setState: function(incognito, enabled, callback) {
var self = this,
data = {
'primaryPattern': '<all_urls>',
'setting': (enabled) ? 'allow' : 'block',
'scope': (incognito === true) ? 'incognito_session_only' : 'regular'
};
chrome.contentSettings.javascript.set(data, function() {
self.updateIcon();
if (typeof callback === 'function') callback();
});
},
toggleState: function() {
var self = this;
chrome.windows.getCurrent(function(win) {
self.triggerWin = win;
self.getState(win.incognito, function(state) {
self.setState(win.incognito, !state.enabled, function() {
self.reloadCurrentTab();
});
});
});
},
onWinFocusChanged: function() {
var self = this;
chrome.windows.getCurrent(function(win) {
self.triggerWin = win;
self.updateIcon();
});
},
reloadCurrentTab: function() {
chrome.tabs.query({currentWindow: true, active: true}, function(tabs) {
var tab = tabs[0];
chrome.tabs.duplicate(tab.id);
chrome.tabs.remove(tab.id);
});
},
updateIcon: function(callback) {
var self = this,
incognito = (self.triggerWin && self.triggerWin.incognito) || false;
self.getState(incognito, function(state) {
if (state.enabled) {
chrome.browserAction.setIcon({path: 'icons/38-on.png'});
chrome.browserAction.setTitle({title: 'JavaScript is enabled'});
}
else {
chrome.browserAction.setIcon({path: 'icons/38-off.png'});
chrome.browserAction.setTitle({title: 'JavaScript is disabled'});
}
if (typeof callback === 'function') callback();
});
}
};
ToggleJSApplication.init();
I already tried using context menu's but I failed. Is there any workaround?
You mentioned that you want to use a button click in the existing webpage to trigger your script. In order to interact with the webpage, you need to use a content script, which must interact with the background script via message passing. The content script can be as simple as:
document.getElementById('buttonId').addEventListener('click',sendMessage);
// or $('#buttonId').click(sendMessage);
function sendMessage() {
chrome.runtime.sendMessage({'message':'buttonClicked'});
}
Then in your background script, you can add:
chrome.runtime.onMessage.addListener(messageListener);
function messageListener(request) {
if ( request.message === 'buttonClicked' ) {
ToggleJSApplication.toggleState();
}
}
I need to make a Rickshaw legend where I can make groups of time series.
e.g. I have 20 time series displayed in a graph and I want them to be grouped in 4 groups, named series1, series2, series3, series4. Series1 would contain the first 5 time series in the graph, series2 would contain the 6th to the 10th and so on.
Before I try to add a custom object in my .js to solve my problem someone knows if there is a built in functionality that I couldn't find?
I didn't find anything helpful so I created a personalized version of a multiple legend that works along with the traditional legend.
Unfortunately to update the multiple legend when the standard legend is modified I had to create a myLegend object instead of the default one.
You can use this version in this way:
//you must have a traditional Rickshaw seriesData
var graph = new Rickshaw.Graph( {
element: document.getElementById("chart"),
width: 960,
height: 500,
renderer: 'line',
series: seriesData
} );
graph.render();
var legend = new Rickshaw.Graph.Legend( {
graph: graph,
element: document.getElementById('legend')
} );
var mLegend = new multiLegend( {
graph: graph,
element: document.getElementById('multi_legend'),
multiDivision: [
{
name: "Africa",
indexes: [ 0, 1, 2, 3, 4, 5, 6, 7 ]
}
{
name: "Asia",
indexes: [ 8, 9, 10, 11, 12 ]
},
{
name: "Europe",
indexes: [ 13, 14, 15, 16, 17]
},
{
name: "North America",
indexes: [ 18, 19, 20 ]
}
]
} );
new myToggle( {
graph: graph,
legend: legend,
multiLegend: mLegend
} );
new multiToggle( {
graph: graph,
multiLegend: mLegend,
legend: legend
});
Here's the code:
multiLegend = Rickshaw.Class.create( {
className: 'rickshaw_legend',
initialize: function(args) {
this.element = args.element;
this.graph = args.graph;
this.naturalOrder = args.naturalOrder;
this.seriesGroups = args.multiDivision;
this.element.classList.add(this.className);
this.list = document.createElement('ul');
this.element.appendChild(this.list);
this.render();
// we could bind this.render.bind(this) here
// but triggering the re-render would lose the added
// behavior of the series toggle
this.graph.onUpdate( function() {} );
},
render: function() {
var self = this;
while ( this.list.firstChild ) {
this.list.removeChild( this.list.firstChild );
}
this.lines = [];
var allSeries = this.graph.series;
self.seriesGroups.forEach( function(s) {
var series = allSeries.filter( function(value, index) {
return (s.indexes.indexOf(index)!=-1) ? true : false;
});
series = series.reverse();
self.addLine(s.name, series);
} );
},
addLine: function (name, series) {
var line = document.createElement('li');
line.className = 'line';
if (series.disabled) {
line.className += ' disabled';
}
if (series.className) {
d3.select(line).classed(series.className, true);
}
var swatch = document.createElement('div');
swatch.className = 'swatch';
swatch.style.backgroundColor = "#0000FF";
line.appendChild(swatch);
var label = document.createElement('span');
label.className = 'label';
label.innerHTML = name;
line.appendChild(label);
this.list.appendChild(line);
line.series = series;
var _line = { element: line, series: series, disabled: false};
if (this.shelving) {
this.shelving.addAnchor(_line);
this.shelving.updateBehaviour();
}
if (this.highlighter) {
this.highlighter.addHighlightEvents(_line);
}
this.lines.push(_line);
return line;
}
} );
multiToggle = function(args) {
this.graph = args.graph;
this.multiLegend = args.multiLegend;
this.legend = args.legend;
var self = this;
this.addAnchor = function(line) {
var anchor = document.createElement('a');
anchor.innerHTML = '✔';
anchor.classList.add('action');
line.element.insertBefore(anchor, line.element.firstChild);
anchor.onclick = function(e) {
if (line.disabled) {
line.series.forEach( function(serie) {
serie.enable();
});
line.element.classList.remove('disabled');
line.disabled = false;
self.legend.lines
.filter(function(value) {
return (line.series.indexOf(value.series)!=-1) ? true : false;
})
.forEach( function(l) {
l.element.classList.remove('disabled');
l.disabled = false;
});
} else {
if (this.graph.series.filter(function(s) { return !s.disabled }).length <= 1) return;
line.series.forEach( function(serie) {
serie.disable();
});
line.element.classList.add('disabled');
line.disabled = true;
self.legend.lines
.filter(function(value) {
return (line.series.indexOf(value.series)!=-1) ? true : false;
})
.forEach( function(l) {
l.element.classList.add('disabled');
l.disabled = true;
});
}
self.graph.update();
}.bind(this);
var label = line.element.getElementsByTagName('span')[0];
label.onclick = function(e){
var disableAllOtherLines = line.disabled;
if ( ! disableAllOtherLines ) {
for ( var i = 0; i < self.multiLegend.lines.length; i++ ) {
var l = self.multiLegend.lines[i];
if ( line.series === l.series ) {
// noop
} else if ( l.series.disabled ) {
// noop
} else {
disableAllOtherLines = true;
break;
}
}
}
// show all or none
if ( disableAllOtherLines ) {
// these must happen first or else we try ( and probably fail ) to make a no line graph
line.series.forEach( function(serie) {
serie.enable();
});
line.element.classList.remove('disabled');
line.disabled = false;
self.legend.lines
.filter(function(value) {
return (line.series.indexOf(value.series)!=-1) ? true : false;
})
.forEach( function(l) {
l.element.classList.remove('disabled');
l.disabled = false;
});
self.multiLegend.lines.forEach(function(l){
if ( line.series === l.series ) {
// noop
} else {
l.series.forEach( function(serie) {
serie.disable();
});
l.element.classList.add('disabled');
l.disabled = true;
self.legend.lines
.filter(function(value) {
return (l.series.indexOf(value.series)!=-1) ? true : false;
})
.forEach( function(l2) {
l2.element.classList.add('disabled');
l2.disabled = true;
});
}
});
} else {
self.multiLegend.lines.forEach(function(l){
l.series.forEach( function(serie) {
serie.enable();
});
l.element.classList.remove('disabled');
l.disabled = false;
self.legend.lines
.filter(function(value) {
return (l.series.indexOf(value.series)!=-1) ? true : false;
})
.forEach( function(l2) {
l2.element.classList.remove('disabled');
l2.disabled = false;
});
});
}
self.graph.update();
};
};
if (this.multiLegend) {
var $ = jQuery;
if (typeof $ != 'undefined' && $(this.multiLegend.list).sortable) {
$(this.multiLegend.list).sortable( {
start: function(event, ui) {
ui.item.bind('no.onclick',
function(event) {
event.preventDefault();
}
);
},
stop: function(event, ui) {
setTimeout(function(){
ui.item.unbind('no.onclick');
}, 250);
}
});
}
this.multiLegend.lines.forEach( function(l) {
self.addAnchor(l);
} );
}
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;
};
s.enable = function() {
s.disabled = false;
};
} );
};
this._addBehavior();
this.updateBehaviour = function () { this._addBehavior() };
};
myToggle = function(args) {
this.graph = args.graph;
this.legend = args.legend;
this.multiLegend = args.multiLegend;
var self = this;
this.addAnchor = function(line) {
var anchor = document.createElement('a');
anchor.innerHTML = '✔';
anchor.classList.add('action');
line.element.insertBefore(anchor, line.element.firstChild);
anchor.onclick = function(e) {
if (line.series.disabled) {
line.series.enable();
line.element.classList.remove('disabled');
} else {
if (this.graph.series.filter(function(s) { return !s.disabled }).length <= 1) return;
line.series.disable();
line.element.classList.add('disabled');
self.multiLegend.lines.forEach( function(l) {
if(l.series.indexOf(line.series)!=-1) {
l.element.classList.add('disabled');
l.disabled = true;
}
});
}
self.graph.update();
}.bind(this);
var label = line.element.getElementsByTagName('span')[0];
label.onclick = function(e){
var disableAllOtherLines = line.series.disabled;
if ( ! disableAllOtherLines ) {
for ( var i = 0; i < self.legend.lines.length; i++ ) {
var l = self.legend.lines[i];
if ( line.series === l.series ) {
// noop
} else if ( l.series.disabled ) {
// noop
} else {
disableAllOtherLines = true;
break;
}
}
}
// show all or none
if ( disableAllOtherLines ) {
// these must happen first or else we try ( and probably fail ) to make a no line graph
line.series.enable();
line.element.classList.remove('disabled');
self.legend.lines.forEach(function(l){
if ( line.series === l.series ) {
// noop
} else {
l.series.disable();
l.element.classList.add('disabled');
}
});
self.multiLegend.lines.forEach( function(l) {
l.element.classList.add('disabled');
l.disabled = true;
});
} else {
self.legend.lines.forEach(function(l){
l.series.enable();
l.element.classList.remove('disabled');
});
}
self.graph.update();
};
};
if (this.legend) {
var $ = jQuery;
if (typeof $ != 'undefined' && $(this.legend.list).sortable) {
$(this.legend.list).sortable( {
start: function(event, ui) {
ui.item.bind('no.onclick',
function(event) {
event.preventDefault();
}
);
},
stop: function(event, ui) {
setTimeout(function(){
ui.item.unbind('no.onclick');
}, 250);
}
});
}
this.legend.lines.forEach( function(l) {
self.addAnchor(l);
} );
}
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;
};
s.enable = function() {
s.disabled = false;
};
} );
};
this._addBehavior();
this.updateBehaviour = function () { this._addBehavior() };
};
In SAPUI5 I have a Model ("sModel") filled with metadata.
In this model I have a property "/aSelectedNumbers".
I also have a panel, of which I want to change the visibility depending on the content of the "/aSelectedNumbers" property.
update
first controller:
var oModelMeta = cv.model.recycleModel("oModelZAPRegistratieMeta", that);
//the cv.model.recycleModel function sets the model to the component
//if that hasn't been done so already, and returns that model.
//All of my views are added to a sap.m.App, which is returned in the
//first view of this component.
var aSelectedRegistratieType = [];
var aSelectedDagdelen = ["O", "M"];
oModelMeta.setProperty("/aSelectedRegistratieType", aSelectedRegistratieType);
oModelMeta.setProperty("/aSelectedDagdelen", aSelectedDagdelen);
First Panel (Which has checkboxes controlling the array in question):
sap.ui.jsfragment("fragments.data.ZAPRegistratie.Filters.RegistratieTypeFilter", {
createContent: function(oInitData) {
var oController = oInitData.oController;
var fnCallback = oInitData.fnCallback;
var oModel = cv.model.recycleModel("oModelZAPRegistratieMeta", oController);
var oPanel = new sap.m.Panel( {
content: new sap.m.Label( {
text: "Registratietype",
width: "120px"
})
});
function addCheckBox(sName, sId) {
var oCheckBox = new sap.m.CheckBox( {
text: sName,
selected: {
path: "oModelZAPRegistratieMeta>/aSelectedRegistratieType",
formatter: function(oFC) {
if (!oFC) { return false; }
console.log(oFC);
return oFC.indexOf(sId) !== -1;
}
},
select: function(oEvent) {
var aSelectedRegistratieType = oModel.getProperty("/aSelectedRegistratieType");
var iIndex = aSelectedRegistratieType.indexOf(sId);
if (oEvent.getParameters().selected) {
if (iIndex === -1) {
aSelectedRegistratieType.push(sId);
oModel.setProperty("/aSelectedRegistratieType", aSelectedRegistratieType);
}
} else {
if (iIndex !== -1) {
aSelectedRegistratieType.splice(iIndex, 1);
oModel.setProperty("/aSelectedRegistratieType", aSelectedRegistratieType);
}
}
// arrays update niet live aan properties
oModel.updateBindings(true); //******** <<===== SEE HERE
if (fnCallback) {
fnCallback(oController);
}
},
width: "120px",
enabled: {
path: "oModelZAPRegistratieMeta>/bChanged",
formatter: function(oFC) {
return oFC !== true;
}
}
});
oPanel.addContent(oCheckBox);
}
addCheckBox("Presentielijst (dag)", "1");
addCheckBox("Presentielijst (dagdelen)", "2");
addCheckBox("Uren (dagdelen)", "3");
addCheckBox("Tijd (dagdelen)", "4");
return oPanel;
}
});
Here is the panel of which the visibility is referred to in the question. Note that it DOES work after oModel.updateBindings(true) (see comment in code above), but otherwise it does not update accordingly.
sap.ui.jsfragment("fragments.data.ZAPRegistratie.Filters.DagdeelFilter", {
createContent: function(oInitData) {
var oController = oInitData.oController;
var fnCallback = oInitData.fnCallback;
var oModel = cv.model.recycleModel("oModelZAPRegistratieMeta", oController);
var oPanel = new sap.m.Panel( {
content: new sap.m.Label( {
text: "Dagdeel",
width: "120px"
}),
visible: {
path: "oModelZAPRegistratieMeta>/aSelectedRegistratieType",
formatter: function(oFC) {
console.log("visibility");
console.log(oFC);
if (!oFC) { return true; }
if (oFC.length === 0) { return true; }
return oFC.indexOf("2") !== -1;
}
}
});
console.log(oPanel);
function addCheckBox(sName, sId) {
var oCheckBox = new sap.m.CheckBox( {
text: sName,
selected: {
path: "oModelZAPRegistratieMeta>/aSelectedDagdelen",
formatter: function(oFC) {
if (!oFC) { return false; }
console.log(oFC);
return oFC.indexOf(sId) !== -1;
}
},
select: function(oEvent) {
var aSelectedDagdelen = oModel.getProperty("/aSelectedDagdelen");
var iIndex = aSelectedDagdelen.indexOf(sId);
if (oEvent.getParameters().selected) {
if (iIndex === -1) {
aSelectedDagdelen.push(sId);
oModel.setProperty("/aSelectedDagdelen", aSelectedDagdelen);
}
} else {
if (iIndex !== -1) {
aSelectedDagdelen.splice(iIndex, 1);
oModel.setProperty("/aSelectedDagdelen", aSelectedDagdelen);
}
}
if (fnCallback) {
fnCallback(oController);
}
},
enabled: {
path: "oModelZAPRegistratieMeta>/bChanged",
formatter: function(oFC) {
return oFC !== true;
}
},
width: "120px"
});
oPanel.addContent(oCheckBox);
}
addCheckBox("Ochtend", "O", true);
addCheckBox("Middag", "M", true);
addCheckBox("Avond", "A");
addCheckBox("Nacht", "N");
return oPanel;
}
});
The reason that the model doesn´t trigger a change event is that the reference to the Array does not change.
A possible way to change the value is to create a new Array everytime you read it from the model:
var newArray = oModel.getProperty("/aSelectedNumbers").slice();
// do your changes to the array
// ...
oModel.setProperty("/aSelectedNumbers", newArray);
This JSBin illustrates the issue.