We use Icecast streaming server to jPlayer on our website and also used on our mobile app. I was trying to add an <intro> to the Icecast config, but when I do, it presents an issue on mobile devices. Whenever the phone has an interruption causing a temporary disconnect, like a call that comes in, the stream repeats what you started listening to when you first connected to the stream, after the intro plays again of course. For instance, if I start the stream listening to one show or song, a call comes in and ends, the intro plays on the reconnect and the stream plays from where I initially started listening.
I have played with Icecast queue and burst settings up and down and none at all, and tried different formats, the same result. I've also had conversations on a couple of other streaming related posts and have been told it seems the issue is with the client buffer and player, which I did not set up. I took a look at our stream-player.js, it is jPlayer 2.9.2 with the following tacked on to the end at line 3507:
;(function() {
var DOMParser, find, parse;
DOMParser = (typeof window !== "undefined" && window !== null ? window.DOMParser : void 0) || (typeof require === "function" ? require('xmldom').DOMParser : void 0) || function() {};
find = function(node, list) {
var attributes, childNode, childNodeName, childNodes, i, match, x, _i, _j, _ref, _ref1;
if (node.hasChildNodes()) {
childNodes = node.childNodes;
for (i = _i = 0, _ref = childNodes.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
childNode = childNodes[i];
childNodeName = childNode.nodeName;
if (/REF/i.test(childNodeName)) {
attributes = childNode.attributes;
for (x = _j = 0, _ref1 = attributes.length; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; x = 0 <= _ref1 ? ++_j : --_j) {
match = attributes[x].nodeName.match(/HREF/i);
if (match) {
list.push({
file: childNode.getAttribute(match[0]).trim()
});
break;
}
}
} else if (childNodeName !== '#text') {
find(childNode, list);
}
}
}
return null;
};
parse = function(playlist) {
var doc, ret;
ret = [];
doc = (new DOMParser()).parseFromString(playlist, 'text/xml').documentElement;
if (!doc) {
return ret;
}
find(doc, ret);
return ret;
};
(typeof module !== "undefined" && module !== null ? module.exports : window).ASX = {
name: 'asx',
parse: parse
};
}).call(this);
(function() {
var COMMENT_RE, EXTENDED, comments, empty, extended, parse, simple;
EXTENDED = '#EXTM3U';
COMMENT_RE = /:(?:(-?\d+),(.+)\s*-\s*(.+)|(.+))\n(.+)/;
extended = function(line) {
var match;
match = line.match(COMMENT_RE);
if (match && match.length === 6) {
return {
length: match[1] || 0,
artist: match[2] || '',
title: match[4] || match[3],
file: match[5].trim()
};
}
};
simple = function(string) {
return {
file: string.trim()
};
};
empty = function(line) {
return !!line.trim().length;
};
comments = function(line) {
return line[0] !== '#';
};
parse = function(playlist) {
var firstNewline;
playlist = playlist.replace(/\r/g, '');
firstNewline = playlist.search('\n');
if (playlist.substr(0, firstNewline) === EXTENDED) {
return playlist.substr(firstNewline).split('\n#').filter(empty).map(extended);
} else {
return playlist.split('\n').filter(empty).filter(comments).map(simple);
}
};
(typeof module !== "undefined" && module !== null ? module.exports : window).M3U = {
name: 'm3u',
parse: parse
};
}).call(this);
(function() {
var LISTING_RE, parse;
LISTING_RE = /(file|title|length)(\d+)=(.+)\r?/i;
parse = function(playlist) {
var index, key, line, match, tracks, value, _, _i, _len, _ref;
tracks = [];
_ref = playlist.trim().split('\n');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
line = _ref[_i];
match = line.match(LISTING_RE);
if (match && match.length === 4) {
_ = match[0], key = match[1], index = match[2], value = match[3];
if (!tracks[index]) {
tracks[index] = {};
}
tracks[index][key.toLowerCase()] = value;
}
}
return tracks.filter(function(track) {
return track != null;
});
};
(typeof module !== "undefined" && module !== null ? module.exports : window).PLS = {
name: 'pls',
parse: parse
};
}).call(this);
;(function() {
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
window.PlayerUI = (function() {
function PlayerUI(container) {
var _this = this;
this.container = container;
this.onStateButtonClicked = __bind(this.onStateButtonClicked, this);
this.duration = null;
this.state = 'loading';
this.player = $('<div></div>');
this.container.append(this.player);
this.player.jPlayer({
ready: function() {
return _this.state = 'paused';
}
});
this.volume = this.container.find('.volume-slider input').rangeslider({
polyfill: false,
onSlide: function(position, value) {
return _this.player.jPlayer('volume', value / 100.0);
},
onSlideEnd: function(position, value) {
return _this.player.jPlayer('volume', value / 100.0);
}
});
this.hookEvents();
}
PlayerUI.prototype.hookEvents = function() {
var _this = this;
this.container.find('.state-button a').click(this.onStateButtonClicked);
this.player.on($.jPlayer.event.play, function() {
return _this.setState('playing');
});
this.player.on($.jPlayer.event.pause, function() {
return _this.setState('paused');
});
this.player.on($.jPlayer.event.durationchange, function(e) {
return _this.container.trigger('player.setProgressMax', {
maxValue: e.jPlayer.status.duration
});
});
this.player.on($.jPlayer.event.timeupdate, function(e) {
return _this.container.trigger('player.updateProgress', {
value: e.jPlayer.status.currentTime
});
});
return this.player.on($.jPlayer.event.ended, function(e) {
return _this.container.trigger('player.trackEnded');
});
};
PlayerUI.prototype.setState = function(state) {
this.state = state;
return this.container.find('.state-button a').removeClass().addClass("state-" + state);
};
PlayerUI.prototype.onStateButtonClicked = function(event) {
event.preventDefault();
switch (this.state) {
case 'playing':
return this.pause();
case 'paused':
return this.play();
default:
return this.noop();
}
};
PlayerUI.prototype.setMedia = function(media) {
this.pause();
return this.player.jPlayer('setMedia', media);
};
PlayerUI.prototype.setProgress = function(pct) {
return this.player.jPlayer('playHead', pct);
};
PlayerUI.prototype.play = function() {
this.setState('playing');
return this.player.jPlayer('play');
};
PlayerUI.prototype.pause = function() {
this.setState('paused');
return this.player.jPlayer('pause');
};
PlayerUI.prototype.noop = function() {
return null;
};
return PlayerUI;
})();
}).call(this);
;(function() {
window.PlaylistUI = (function() {
function PlaylistUI(container) {
var _this = this;
this.container = container;
this.container.hide();
$(window).on('playlistloader.finished', function(evt, data) {
return _this.setPlaylist(PlaylistLoader.coalescePlaylists(data.playlists));
});
}
PlaylistUI.prototype.loadM3UList = function(m3uList) {
return new PlaylistLoader(m3uList);
};
PlaylistUI.prototype.setPlaylist = function(playlistData) {
if (typeof playlistData.data !== 'undefined') {
this.name = playlistData.name;
playlistData = playlistData.data;
}
this.playlist = playlistData;
this.container.hide();
this.unhookEvents();
this.renderPlaylist();
this.container.show();
this.hookEvents();
return this.container.trigger('playlistui.ready', {
ui: this,
autoplay: false //this.getAutoplay()
});
};
PlaylistUI.prototype.unhookEvents = function() {
return this.container.find('.playlist-item').off('click.playlistUI', 'a');
};
PlaylistUI.prototype.hookEvents = function() {
var _this = this;
return this.container.find('.playlist-item').on('click.playlistUI', 'a', function(evt) {
var idx, item;
evt.preventDefault();
idx = $(evt.target).parent('.playlist-item').data('idx');
item = _this.getItemByIdx(idx);
return _this.select(item);
});
};
PlaylistUI.prototype.renderPlaylist = function() {
var idx, item, playlist, _i, _len, _ref, _results;
playlist = this.container.find('.playlist');
playlist.empty();
_ref = this.playlist;
_results = [];
for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
item = _ref[idx];
_results.push(playlist.append(this.rowTemplate(item, idx)));
}
return _results;
};
PlaylistUI.prototype.rowTemplate = function(item, idx) {
return $("<li class=\"playlist-item\" data-idx=\"" + idx + "\">" + item.title + "</li>");
};
PlaylistUI.prototype.getAutoplay = function() {
var item, _i, _len, _ref;
_ref = this.playlist;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
item = _ref[_i];
if (item.autoplay) {
return item;
}
}
return null;
};
PlaylistUI.prototype.getItemByIdx = function(idx) {
return this.playlist[idx];
};
PlaylistUI.prototype.getRowForItem = function(item) {
var compare, found, idx, _i, _len, _ref;
_ref = this.playlist;
for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
compare = _ref[idx];
if (compare === item) {
found = this.container.find(".playlist-item[data-idx=" + idx + "]");
return found;
}
}
return null;
};
PlaylistUI.prototype.getIndexForItem = function(item) {
var compare, idx, _i, _len, _ref;
_ref = this.playlist;
for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
compare = _ref[idx];
if (item === compare) {
return idx;
}
}
return null;
};
PlaylistUI.prototype.findNext = function() {
var currentIndex, nextIndex;
currentIndex = this.getIndexForItem(this.current);
if (currentIndex === null) {
return null;
}
nextIndex = currentIndex + 1;
if (nextIndex >= this.playlist.length) {
return null;
}
return this.playlist[nextIndex];
};
PlaylistUI.prototype.select = function(item) {
if (item) {
this.current = item;
this.getRowForItem(item).addClass('selected').siblings().removeClass('selected');
return this.container.trigger('playlistui.select', {
ui: this,
item: item
});
}
};
PlaylistUI.prototype.selectFirst = function() {
return this.select(this.playlist[0]);
};
PlaylistUI.prototype.selectNext = function() {
var nextItem;
nextItem = this.findNext();
if (nextItem === null) {
return false;
}
this.select(nextItem);
return true;
};
return PlaylistUI;
})();
}).call(this);
;(function() {
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
window.PlaylistLoader = (function() {
function PlaylistLoader(playlists) {
this.playlists = playlists;
this.loadedItem = __bind(this.loadedItem, this);
this.loadPlaylists();
}
PlaylistLoader.prototype.loadPlaylists = function() {
var idx, item, _i, _len, _ref, _results,
_this = this;
this.loadCount = 0;
this.data = new Array(this.playlists.length);
_ref = this.playlists;
_results = [];
for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
item = _ref[idx];
_results.push((function() {
var tmp;
tmp = idx;
return jQuery.ajax({
url: item
}).done(function(data) {
return _this.loadedItem(tmp, data);
});
})());
}
return _results;
};
PlaylistLoader.prototype.loadedItem = function(idx, data) {
var playlist;
playlist = M3U.parse(data);
this.data[idx] = playlist;
$(window).trigger('playlistloader.loadeditem', {
index: idx,
playlist: playlist
});
this.loadCount++;
if (this.loadCount === this.playlists.length) {
return this.finishedLoading();
}
};
PlaylistLoader.prototype.finishedLoading = function() {
return $(window).trigger('playlistloader.finished', {
playlists: this.data
});
};
PlaylistLoader.coalescePlaylists = function(playlistsLoaded) {
var fileEntry, output, playlist, _i, _j, _len, _len1;
output = [];
for (_i = 0, _len = playlistsLoaded.length; _i < _len; _i++) {
playlist = playlistsLoaded[_i];
for (_j = 0, _len1 = playlist.length; _j < _len1; _j++) {
fileEntry = playlist[_j];
output.push(fileEntry);
}
}
return output;
};
return PlaylistLoader;
})();
}).call(this);
;(function() {
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
window.StreamUI = (function() {
function StreamUI(selector, streamPlaylists) {
this.selector = selector;
this.streamPlaylists = streamPlaylists;
this.playlistSelect = __bind(this.playlistSelect, this);
this.playlistReady = __bind(this.playlistReady, this);
this.container = jQuery(this.selector);
this.playlist = new PlaylistUI(this.container.find('.playlist-ui'));
this.player = new PlayerUI(this.container.find('.player-ui'));
this.hookEvents();
this.playlist.loadM3UList(this.streamPlaylists);
}
StreamUI.prototype.hookEvents = function() {
var playlistUI;
playlistUI = this.container.find('.playlist-ui');
playlistUI.on('playlistui.ready', this.playlistReady);
return playlistUI.on('playlistui.select', this.playlistSelect);
};
StreamUI.prototype.playlistReady = function(evt, eventinfo) {
if (eventinfo.autoplay !== null) {
return eventinfo.ui.select(eventinfo.autoplay);
} else {
return eventinfo.ui.selectFirst();
}
};
StreamUI.prototype.playlistSelect = function(evt, eventinfo) {
this.player.setMedia({
mp3: eventinfo.item.file
});
return this.player.play();
};
return StreamUI;
})();
}).call(this);
Although I'm primarily a linux developer with most of my programming experience in Perl and PHP, and do know jQuery pretty well dealing with my web development, I'm surely a novice when it comes to jPlayer or even audio streaming. I was hoping someone could spot something in hte code above that could contribute to the issue we have when adding an intro to our Icecast 2.4.4 stream?
Our streams are available at the URL below, I have the intro on our HD4 stream at the moment.
streaming player
The issue is easily duplicated by starting the stream and listening a bit until the song changes, call the phone letting it interrupt the stream, then hang up. This will cause the first song listened to be playing again after the intro.
I believe the codec is a match, I did have an issue getting the intro to work until I formatted as MP3 128Kbps bit rate 44.1KHz sampling and 2 channel stereo. Here is the intro file info:
user#stream:~$ mediainfo /usr/share/icecast2/web/high_quality.mp3
General
Complete name : /usr/share/icecast2/web/high_quality.mp3
Format : MPEG Audio
File size : 138 KiB
Duration : 8s 777ms
Overall bit rate mode : Constant
Overall bit rate : 128 Kbps
Writing library : LAME3.99r
Audio
Format : MPEG Audio
Format version : Version 1
Format profile : Layer 3
Mode : Joint stereo
Mode extension : MS Stereo
Duration : 8s 803ms
Bit rate mode : Constant
Bit rate : 128 Kbps
Channel(s) : 2 channels
Sampling rate : 44.1 KHz
Compression mode : Lossy
Stream size : 137 KiB (100%)
Writing library : LAME3.99r
Encoding settings : -m j -V 4 -q 3 -lowpass 17 -b 128
Sounds like the underlying browser cache kicks in and forces replay of something held in memory. Browsers are 'awesome' like that under some circumstances and will then go out of their way to ignore no-cache directives and other things.
One way to make sure the browser doesn't try to play cache shenanigans is to add a "cache buster". Essentially a query string ( /stream?foo=bar ), which makes the browser engine think it's dynamically generated content and discard its cache; cf. https://www.keycdn.com/support/what-is-cache-busting .
At this time your Icecast server doesn't seem to answer any requests. So I can't look into the specifics on your side.
Related
I have an issue I have a "chat" and when I add a new text in my infinite scroll container,
I go back to the top of the page, i'm not stuck to the bottom.
How can I maintain the page in the bottom of my page while people chat
my service
tabs.factory('chat', function ($http, $timeout, $q) {
return {
default: {
delay: 100
},
data: [],
dataScroll: [],
init: function (data) {
if (this.data.length == 0) {
for (var i = 0; i < data.length; i++) {
this.data[i] = data[i]
}
} else {
var tailleDataSaved = this.data.length
var dataAAjouter = data.slice(tailleDataSaved)
for (var i = 0; i < dataAAjouter.length; i++) {
this.data.push(dataAAjouter[i])
}
}
},
request: function (showAll) {
var self = this;
var deferred = $q.defer();
var index = this.dataScroll.length
var ndDataVisible = 7
var start = index;
var end = index + ndDataVisible - 1;
$timeout(function () {
if (!showAll) {
var item = []
if (start < end) {
for (var i = start; i < end; i++) {
console.log(start)
console.log(end)
console.log(self.data[i])
if (item = self.data[i]) {
self.dataScroll.push(item);
}
}
}
} else {
self.dataScroll = self.data
}
deferred.resolve(self.dataScroll);
}, 0);
return deferred.promise;
}
}
})
my js file
$scope.listChat= function () {
$scope.isActionLoaded = false;
$http.get(apiurl).then(function (response) {
chat.init(response.data)
$scope.isActionLoaded = true
})
}
$scope.listChatInfiniteScroll = function () {
$scope.isScrollDataLoaded = false
ticketListeActionScroll.request(false).then(function (response) {
$scope.actionsInfiniteScroll = response
$scope.isScrollDataLoaded = true
})
}
html file
<div ng-if="isActionLoaded" infinite-scroll='listChatInfiniteScroll ()' infinite-scroll-distance='1'>
<div ng-repeat="chat in actionsInfiniteScroll">
{{chat.text}}
</div>
</div>
Each time I add a new message it calls listChat
The goal is to return a boolean value defining if a Job can be posted based on Earned staffing.
There are different pieces that come from three different sql tables. Rather than making a sql query that returns all of them in one result, i'm trying to understand how to use the $q.all function. The problem is that I am not getting a promise back from the service. The error is: TypeError: Cannot read property 'then' of undefined
There a few articles on this subject but most that I have found are old and still refer to using defer. I have tried the suggestions in the others and none of them have worked. They mentioned that the $q.all needs a return, and to return the $q.resolve and $q.reject for the return values.
Here is the code in my service:
function isMDOLevelAllowed(mdoLevel, finance) {
this.crData = "";
this.pData = "";
var mdoLevelToMatch = "", mdoMatrix = "", mdoOnRollsTotal = 0, mdoAuthTotal = 0;
var mdoVarianceTotal = 0, mdoPending = 0, mdoPendingThisLevel = 0;
return $q.all([
getCRO36ByFinance(finance),
epEarnedMDOSDOResource(finance),
getPendingByFinance(finance)
]).then(function (data) {
var crData = data[0];
var eData = data[1];
var pData = data[2];
var mdoData = crData.filter(function (m) { return m.jobType === "MDO"; });
mdoLevelToMatch = mdoData.filter(function (m) { return m.payGrade === mdoLevel; })[0];
mdoVarianceTotal = mdoData.reduce(function (a, b) { return a + b.variance; }, 0);
mdoMatrix = mdoData.map(function (m) { return { payGrade: m.payGrade, authorized: m.totalAuthorized }; });
mdoPending = pData.mdoTotalCount;
mdoPendingThisLevel = eval("pData.mdO" + mdoLevelToMatch.payGrade + "Count");
// Check if over Total Authorized
if (mdoVarianceTotal + mdoPending < 0) {
// Check if over Paylevel Authorized
if (mdoLevelToMatch.variance + mdoPendingThisLevel < 0) {
return $q.resolve();
}
else {
return $q.reject();
}
}
else {
return $q.reject();
}
}).$promise;
}
var service = {
getEarnedByFinance: getEarnedByFinance,
getCRO36ByFinance: getCRO36ByFinance,
getPendingByFinance: getPendingByFinance,
isMDOLevelAllowed: isMDOLevelAllowed,
isSDOAllowed: isSDOAllowed
};
return service;
How about trying:
function isMDOLevelAllowed(mdoLevel, finance) {
var defer = $q.defer();
this.crData = "";
this.pData = "";
var mdoLevelToMatch = "", mdoMatrix = "", mdoOnRollsTotal = 0, mdoAuthTotal = 0;
var mdoVarianceTotal = 0, mdoPending = 0, mdoPendingThisLevel = 0;
$q.all([
getCRO36ByFinance(finance),
epEarnedMDOSDOResource(finance),
getPendingByFinance(finance)
]).then(function (data) {
var crData = data[0];
var eData = data[1];
var pData = data[2];
var mdoData = crData.filter(function (m) { return m.jobType === "MDO"; });
mdoLevelToMatch = mdoData.filter(function (m) { return m.payGrade === mdoLevel; })[0];
mdoVarianceTotal = mdoData.reduce(function (a, b) { return a + b.variance; }, 0);
mdoMatrix = mdoData.map(function (m) { return { payGrade: m.payGrade, authorized: m.totalAuthorized }; });
mdoPending = pData.mdoTotalCount;
mdoPendingThisLevel = eval("pData.mdO" + mdoLevelToMatch.payGrade + "Count");
// Check if over Total Authorized
if (mdoVarianceTotal + mdoPending < 0) {
// Check if over Paylevel Authorized
if (mdoLevelToMatch.variance + mdoPendingThisLevel < 0) {
defer.resolve();
}
else {
defer.reject();
}
}
else {
defer.reject();
}
});
return defer.promise;
}
Take a note on how i defined var defer = $q.defer(); and then returned defer.promise just once. You dont need to return resolve and reject
Thank you for your help. I got it to work. I changed the $q.resolve, $q.reject to a return true or false, and removed the $promise at the end.
function isMDOLevelAllowed(mdoLevel, finance) {
this.crData = "";
this.pData = "";
var mdoLevelToMatch = "", mdoMatrix = "", mdoOnRollsTotal = 0, mdoAuthTotal = 0;
var mdoVarianceTotal = 0, mdoPending = 0, mdoPendingThisLevel = 0;
return $q.all([
getCRO36ByFinance(finance),
epEarnedMDOSDOResource(finance),
getPendingByFinance(finance)
]).then(function (data) {
var crData = data[0];
var eData = data[1];
var pData = data[2];
var mdoData = crData.filter(function (m) { return m.jobType === "MDO"; });
mdoLevelToMatch = mdoData.filter(function (m) { return m.payGrade === mdoLevel; })[0];
//mdoOnRollsTotal = mdoData.reduce(function (a, b) { return a + b.totalOnRolls; }, 0);
//mdoAuthTotal = mdoData.reduce(function (a, b) { return a + b.totalAuthorized; }, 0);
mdoVarianceTotal = mdoData.reduce(function (a, b) { return a + b.variance; }, 0);
mdoMatrix = mdoData.map(function (m) { return { payGrade: m.payGrade, authorized: m.totalAuthorized }; });
mdoPending = pData.mdoTotalCount;
mdoPendingThisLevel = eval("pData.mdO" + mdoLevelToMatch.payGrade + "Count");
// Check if over Total Authorized
if (mdoVarianceTotal + mdoPending < 0) {
// Check if over Paylevel Authorized
if (mdoLevelToMatch.variance + mdoPendingThisLevel < 0) {
return true;
}
else {
return false;
}
}
else {
return false;
}
});
}
I am trying to configure Vzaar video tracking in Tealium. There is very little documentation on how to go about.
here is a link to the documentation
https://community.tealiumiq.com/t5/iQ-Tag-Management/Vzaar-Video-Tracking/ta-p/5934
here is the custom javascript code provided by the documentation mentioned in the above link
var video_events = ["playState","progress","interaction"]; // Possible values are "playState", "progress" and/or "integration"
var milestone_percentages = ["10","20","30","40","90"]; // These must be rounded to the nearest 10
var player_element_id = "vzvd-1556961";
var player_type = "iframe"; // Possible values are "iframe" or "html"
var played = false;
var m1 = false;
var m2 = false;
var m3 = false;
var m4 = false;
// Call utag.link in Vzaar event listeners
window._tealium_VZ = {
name : "Vzaar",
init_tries : 0,
eventsAdded : false,
events : video_events,
milestone_percentages : milestone_percentages,
mediaEventHandler : function (pEvent) {
pos = _tealium_VZ.player_object.getTime();
dur = _tealium_VZ.player_object.getTotalTime();
if (pEvent=="mediaStarted" || pEvent=="started") {
alert('I am here');
played = true;
utag.DB("**** video started ****");
utag.link({event_type:"video",event_name:"play"})
////s.Media.open(video_name, video_duration, video_player);
////s.Media.play(video_name, 0);
//s.Media.track(video_name);
} else if(pEvent=="resume"){
//s.Media.play(video_name, 0);
//s.Media.track(video_name);
_tealium_VZ.pause = false;
utag.link({event_type:"video",event_name:"resume",video_position:pos,video_duration:dur})
utag.DB("**** video resumed ****");
}else if(pEvent=="pause"){
//s.Media.stop(video_name, video_position);
//s.Media.track(video_name);
_tealium_VZ.pause = true;
utag.link({event_type:"video",event_name:"pause",video_position:pos,video_duration:dur})
utag.DB("**** video paused****");
utag.DB("**** Position: " + pos);
utag.DB("**** Total Duration: " + dur);
}else if(pEvent=="mediaEnded"){
//s.Media.complete(video_name, video_position);
//s.Media.stop(video_name, video_position);
//s.Media.track(video_name);
played = false;
//_tealium_VZ.resetMilestones();
utag.link({event_type:"video",event_name:"complete",video_position:pos,video_duration:dur})
utag.DB("**** video complete****");
}else{
var ms = pEvent.replace(/[^0-9]/g, "")
for(var i=0;i<_tealium_VZ.milestone_percentages.length;i++){
if(ms==_tealium_VZ.milestone_percentages[i]){
var ms_num =(i+1);
utag.link({event_type:"video",event_name:"milestone",video_milestone:"M:"+ms_num+":"+_tealium_VZ.milestone_percentages[i],video_position:pos,video_duration:dur})
utag.DB("**** "+_tealium_VZ.milestone_percentages[i]+"% viwed****");
}
}
}
},
// Attaching Event Listeners for Begin, Play, Stop, and Video Completion
// Each Event Handler has a callback function attached to it (mediaEventHandler) which will be called when the event occurs
addEvents : function(a){
utag.DB("***** Adding Events ******");
if(a=="iframe"){
for(var i=0;i<video_events.length;i++){
_tealium_VZ.player_object.addEventListener(_tealium_VZ.events[i],_tealium_VZ.mediaEventHandler);
}
}else{
for(var i=0;i<video_events.length;i++){
_tealium_VZ.player_object.addEventListener(_tealium_VZ.events[i],"_tealium_VZ.mediaEventHandler");
}
}
},
init : function(){
// utag.DB("Connecting Tealium with Vzaar object");
if(typeof vzPlayer!="undefined"){
if(player_type=="iframe"){
vz_player = new vzPlayer(player_element_id)
vz_player.ready(function(e){
utag.DB("TEALIUM: Connecting Tealium with Oyala Player - SUCCESS");
_tealium_VZ.player_object = vz_player;
_tealium_VZ.addEvents(player_type)
utag.DB("****** Events Added ******");
_tealium_VZ.eventsAdded = true;
})
}else{
window.vzaarPlayerReady = function() {
utag.DB("*********** Video Ready **************");
utag.DB("TEALIUM: Connecting Tealium with Oyala Player - SUCCESS");
vzPlayer = document.getElementById(player_element_id);
_tealium_VZ.player_object = vzPlayer;
_tealium_VZ.addEvents(player_type)
utag.DB("****** Events Added ******");
_tealium_VZ.eventsAdded = true;
_tealium_VZ.readyFunction = true;
}
}
}else if(!_tealium_VZ.eventsAdded){
// If Vzaar object is not defined we will increment the number of tries by 1
_tealium_VZ.init_tries += 1;
//Stop trying to connect to the Video Player if tried 100 times
if(_tealium_VZ.init_tries>100){
utag.DB("TEALIUM: Cannot connect to Vzaar Video");
return;
}
// Calls init function repeatedly either 100 times or Vzaar Object is defined
setTimeout(function(){_tealium_VZ.init()}, 100);
}
}
}
if(typeof _tealium_VZ.videoPlayer == "undefined"){
_tealium_VZ.init();
}
I start this script off with this snippet I wrote to append to the iframe url, and I also set the custom javascript code extension to the preloader function
window.addEventListener('load', function(){
var ifrm = document.getElementsByTagName('iframe')[0];
ifrm.src += '&apiOn=true';
}, false);
I am still not seeing any events being fired when I press play on the video. What do I need to do to this script to start receiving tracking data? I am guessing I need to take out some comment blocks in the script provided by the Tealium Learning Community Docs however, I'm trying and still not seeing results. Any help would be appreciated!
There a couple of things you should check, and due to the lack of processing order context I will assume, so apologies if you already took that into consideration.
The pre loader extension
You are copy/pasting the extension content here, but did you consider the 3rd line where the id needs to be specified?
var player_element_id = "vzvd-1556961";
Im just mentioning because in your custom script you are fetching the element based on TAG type
var ifrm = document.getElementsByTagName('iframe')[0];
On the custom script it would be advisable to check if the query parameter is already there.
window.addEventListener('load', function(){
var ifrm = document.getElementsByTagName('iframe')[0];
if (ifrm.src.indexOf("apiOn=true") === -1)
if (ifrm.src.indexOf("?") === -1)
ifrm.src += '?apiOn=true';
else
ifrm.src += '&apiOn=true';
}, false);
Because with Tealium everything is async, there is always the possibility that the "pre-loader" extension runs the code before the iframe is ready on DOM. Therefore to make sure the extension code is only initialized when the document is ready, I would move the extension object initializing instruction to the body of the custom script.
Something like this:
window.addEventListener('load', function(){
var ifrm = document.getElementsByTagName('iframe')[0];
if (ifrm.src.indexOf("apiOn=true") === -1)
if (ifrm.src.indexOf("?") === -1)
ifrm.src += '?apiOn=true';
else
ifrm.src += '&apiOn=true';
if (typeof _tealium_VZ.videoPlayer == "undefined") {
_tealium_VZ.init();
}
}, false);
Finally make sure the vzaar client.js script is also loaded on the page as per your refered Tealium document
Here is a snippet to add it dinamically if the dev's didnt add it to the page. You can add it to the top of the "pre-loader" extension code
(function (a, b, c, d) {
a = '//player.vzaar.net/libs/flashtakt/client.js';
b = document;
c = 'script';
d = b.createElement(c);
d.src = a;
d.type = 'text/java' + c;
d.async = true;
a = b.getElementsByTagName(c)[0];
a.parentNode.insertBefore(d, a)
})();
So following your custom code logic, here is a full tracking code snippet for vzaar for a pre-loader extension in Tealium.
(function (a, b, c, d) {
a = '//player.vzaar.net/libs/flashtakt/client.js';
b = document;
c = 'script';
d = b.createElement(c);
d.src = a;
d.type = 'text/java' + c;
d.async = true;
a = b.getElementsByTagName(c)[0];
a.parentNode.insertBefore(d, a)
})();
var video_events = ["playState", "progress", "interaction"]; // Possible values are "playState", "progress" and/or "integration"
var milestone_percentages = ["10", "20", "30", "40", "90"]; // These must be rounded to the nearest 10
var player_element_id = "video";
var player_type = "iframe"; // Possible values are "iframe" or "html"
var fireEvent = true;
var played = false;
var m1 = false;
var m2 = false;
var m3 = false;
var m4 = false;
window._tealium_VZ = {
name: "Vzaar",
init_tries: 0,
eventsAdded: false,
events: video_events,
milestone_percentages: milestone_percentages,
mediaEventHandler: function (pEvent) {
pos = _tealium_VZ.player_object.getTime();
dur = _tealium_VZ.player_object.getTotalTime();
if (pEvent == "mediaStarted" || pEvent == "started") {
played = true;
var data = {event_type: "video", event_name: "play"};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
} else if (pEvent == "resume") {
_tealium_VZ.pause = false;
var data = {event_type: "video", event_name: "resume", video_position: pos, video_duration: dur};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
} else if (pEvent == "pause") {
_tealium_VZ.pause = true;
var data = {event_type: "video", event_name: "pause", video_position: pos, video_duration: dur};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
} else if (pEvent == "mediaEnded") {
played = false;
//_tealium_VZ.resetMilestones();
var data = {event_type: "video", event_name: "complete", video_position: pos, video_duration: dur};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
} else {
var ms = pEvent.replace(/[^0-9]/g, "")
for (var i = 0; i < _tealium_VZ.milestone_percentages.length; i++) {
if (ms == _tealium_VZ.milestone_percentages[i]) {
var ms_num = (i + 1);
var data = {event_type: "video", event_name: "milestone", video_milestone: "M:" + ms_num + ":" + _tealium_VZ.milestone_percentages[i], video_position: pos, video_duration: dur};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
}
}
}
},
addEvents: function (a) {
console.log("***** Adding Events ******");
if (a == "iframe") {
for (var i = 0; i < video_events.length; i++) {
_tealium_VZ.player_object.addEventListener(_tealium_VZ.events[i], _tealium_VZ.mediaEventHandler);
}
} else {
for (var i = 0; i < video_events.length; i++) {
_tealium_VZ.player_object.addEventListener(_tealium_VZ.events[i], "_tealium_VZ.mediaEventHandler");
}
}
},
init: function () {
if (typeof vzPlayer != "undefined") {
if (player_type == "iframe") {
vz_player = new vzPlayer(player_element_id)
vz_player.ready(function (e) {
console.log("TEALIUM: Connecting Tealium with Oyala Player - SUCCESS");
_tealium_VZ.player_object = vz_player;
_tealium_VZ.addEvents(player_type)
console.log("****** Events Added ******");
_tealium_VZ.eventsAdded = true;
})
} else {
window.vzaarPlayerReady = function () {
console.log("*********** Video Ready **************");
console.log("TEALIUM: Connecting Tealium with Oyala Player - SUCCESS");
vzPlayer = document.getElementById(player_element_id);
_tealium_VZ.player_object = vzPlayer;
_tealium_VZ.addEvents(player_type)
console.log("****** Events Added ******");
_tealium_VZ.eventsAdded = true;
_tealium_VZ.readyFunction = true;
}
}
} else if (!_tealium_VZ.eventsAdded) {
// If Vzaar object is not defined we will increment the number of tries by 1
_tealium_VZ.init_tries += 1;
//Stop trying to connect to the Video Player if tried 100 times
if (_tealium_VZ.init_tries > 100) {
console.log("TEALIUM: Cannot connect to Vzaar Video");
return;
}
// Calls init function repeatedly either 100 times or Vzaar Object is defined
setTimeout(function () {
_tealium_VZ.init()
}, 100);
}
}
}
window.addEventListener('load', function(){
var ifrm = document.getElementsByTagName('iframe')[0];
if (ifrm.src.indexOf("apiOn=true") === -1)
if (ifrm.src.indexOf("?") === -1)
ifrm.src += '?apiOn=true';
else
ifrm.src += '&apiOn=true';
player_element_id = ifrm.id;
if (typeof _tealium_VZ.videoPlayer == "undefined") {
_tealium_VZ.init();
}
}, false);
Here is a full working page working with the tracking code from refered Tealium documentation link:
<html>
<head>
<title>vzaar</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="http://player.vzaar.net/libs/flashtakt/client.js" type="text/javascript"></script>
<script>
var video_events = ["playState", "progress", "interaction"]; // Possible values are "playState", "progress" and/or "integration"
var milestone_percentages = ["10", "20", "30", "40", "90"]; // These must be rounded to the nearest 10
var player_element_id = "video";
var player_type = "iframe"; // Possible values are "iframe" or "html"
var fireEvent = false;
var played = false;
var m1 = false;
var m2 = false;
var m3 = false;
var m4 = false;
window._tealium_VZ = {
name: "Vzaar",
init_tries: 0,
eventsAdded: false,
events: video_events,
milestone_percentages: milestone_percentages,
mediaEventHandler: function (pEvent) {
pos = _tealium_VZ.player_object.getTime();
dur = _tealium_VZ.player_object.getTotalTime();
if (pEvent == "mediaStarted" || pEvent == "started") {
played = true;
var data = {event_type: "video", event_name: "play"};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
} else if (pEvent == "resume") {
_tealium_VZ.pause = false;
var data = {event_type: "video", event_name: "resume", video_position: pos, video_duration: dur};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
} else if (pEvent == "pause") {
_tealium_VZ.pause = true;
var data = {event_type: "video", event_name: "pause", video_position: pos, video_duration: dur};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
} else if (pEvent == "mediaEnded") {
played = false;
//_tealium_VZ.resetMilestones();
var data = {event_type: "video", event_name: "complete", video_position: pos, video_duration: dur};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
} else {
var ms = pEvent.replace(/[^0-9]/g, "")
for (var i = 0; i < _tealium_VZ.milestone_percentages.length; i++) {
if (ms == _tealium_VZ.milestone_percentages[i]) {
var ms_num = (i + 1);
var data = {event_type: "video", event_name: "milestone", video_milestone: "M:" + ms_num + ":" + _tealium_VZ.milestone_percentages[i], video_position: pos, video_duration: dur};
if (fireEvent === true) { utag.link(data) };
console.log(JSON.stringify(data));
}
}
}
},
addEvents: function (a) {
console.log("***** Adding Events ******");
if (a == "iframe") {
for (var i = 0; i < video_events.length; i++) {
_tealium_VZ.player_object.addEventListener(_tealium_VZ.events[i], _tealium_VZ.mediaEventHandler);
}
} else {
for (var i = 0; i < video_events.length; i++) {
_tealium_VZ.player_object.addEventListener(_tealium_VZ.events[i], "_tealium_VZ.mediaEventHandler");
}
}
},
init: function () {
if (typeof vzPlayer != "undefined") {
if (player_type == "iframe") {
vz_player = new vzPlayer(player_element_id)
vz_player.ready(function (e) {
console.log("TEALIUM: Connecting Tealium with Oyala Player - SUCCESS");
_tealium_VZ.player_object = vz_player;
_tealium_VZ.addEvents(player_type)
console.log("****** Events Added ******");
_tealium_VZ.eventsAdded = true;
})
} else {
window.vzaarPlayerReady = function () {
console.log("*********** Video Ready **************");
console.log("TEALIUM: Connecting Tealium with Oyala Player - SUCCESS");
vzPlayer = document.getElementById(player_element_id);
_tealium_VZ.player_object = vzPlayer;
_tealium_VZ.addEvents(player_type)
console.log("****** Events Added ******");
_tealium_VZ.eventsAdded = true;
_tealium_VZ.readyFunction = true;
}
}
} else if (!_tealium_VZ.eventsAdded) {
// If Vzaar object is not defined we will increment the number of tries by 1
_tealium_VZ.init_tries += 1;
//Stop trying to connect to the Video Player if tried 100 times
if (_tealium_VZ.init_tries > 100) {
console.log("TEALIUM: Cannot connect to Vzaar Video");
return;
}
// Calls init function repeatedly either 100 times or Vzaar Object is defined
setTimeout(function () {
_tealium_VZ.init()
}, 100);
}
}
}
window.addEventListener('load', function(){
var ifrm = document.getElementsByTagName('iframe')[0];
if (ifrm.src.indexOf("apiOn=true") === -1)
if (ifrm.src.indexOf("?") === -1)
ifrm.src += '?apiOn=true';
else
ifrm.src += '&apiOn=true';
player_element_id = ifrm.id;
if (typeof _tealium_VZ.videoPlayer == "undefined") {
_tealium_VZ.init();
}
}, false);
</script>
</head>
<body>
<iframe
id="video"
name="video"
title="vzaar video player"
class="vzaar-video-player"
type="text/html"
width="640"
height="480"
frameborder="0"
allowfullscreen=""
allowtransparency="true"
mozallowfullscreen=""
webkitallowfullscreen=""
src="//view.vzaar.com/9036822/player?apiOn=true">
</iframe>
</body>
</html>
Result from that example page:
I have been using ngMap with my angularjs code for displaying markers. However, with more than 100 markers I have noticed that there is a considerable decrease in performance mainly related to ng-repeat and two way binding. I would like to add markers with custom HTML elements similar to CustomMarker but using ordinary Markers and modified from controller when required.
Challenges faced :
I have SVG images which need to be dynamically coloured based on the conditions (These SVGs are not single path ones, hence doesn't seem to work well when I used it with Symbol)
These are vehicle markers and hence should support rotation
I have solved this by creating CustomMarker with Overlay and then adding the markers that are only present in the current map bounds to the map so that map doesn't lag.
Below is the code snippet with which I achieved it.
createCustomMarkerComponent();
/**
* options : [Object] : options to be passed on
* - position : [Object] : Google maps latLng object
* - map : [Object] : Google maps instance
* - markerId : [String] : Marker id
* - innerHTML : [String] : innerHTML string for the marker
**/
function CustomMarker(options) {
var self = this;
self.options = options || {};
self.el = document.createElement('div');
self.el.style.display = 'block';
self.el.style.visibility = 'hidden';
self.visible = true;
self.display = false;
for (var key in options) {
self[key] = options[key];
}
self.setContent();
google.maps.event.addListener(self.options.map, "idle", function (event) {
//This is the current user-viewable region of the map
var bounds = self.options.map.getBounds();
checkElementVisibility(self, bounds);
});
if (this.options.onClick) {
google.maps.event.addDomListener(this.el, "click", this.options.onClick);
}
}
function checkElementVisibility(item, bounds) {
//checks if marker is within viewport and displays the marker accordingly - triggered by google.maps.event "idle" on the map Object
if (bounds.contains(item.position)) {
//If the item isn't already being displayed
if (item.display != true) {
item.display = true;
item.setMap(item.options.map);
}
} else {
item.display = false;
item.setMap(null);
}
}
var supportedTransform = (function getSupportedTransform() {
var prefixes = 'transform WebkitTransform MozTransform OTransform msTransform'.split(' ');
var div = document.createElement('div');
for (var i = 0; i < prefixes.length; i++) {
if (div && div.style[prefixes[i]] !== undefined) {
return prefixes[i];
}
}
return false;
})();
function createCustomMarkerComponent() {
if (window.google) {
CustomMarker.prototype = new google.maps.OverlayView();
CustomMarker.prototype.setContent = function () {
this.el.innerHTML = this.innerHTML;
this.el.style.position = 'absolute';
this.el.style.cursor = 'pointer';
this.el.style.top = 0;
this.el.style.left = 0;
};
CustomMarker.prototype.getPosition = function () {
return this.position;
};
CustomMarker.prototype.getDraggable = function () {
return this.draggable;
};
CustomMarker.prototype.setDraggable = function (draggable) {
this.draggable = draggable;
};
CustomMarker.prototype.setPosition = function (position) {
var self = this;
return new Promise(function () {
position && (self.position = position); /* jshint ignore:line */
if (self.getProjection() && typeof self.position.lng == 'function') {
var setPosition = function () {
if (!self.getProjection()) {
return;
}
var posPixel = self.getProjection().fromLatLngToDivPixel(self.position);
var x = Math.round(posPixel.x - (self.el.offsetWidth / 2));
var y = Math.round(posPixel.y - self.el.offsetHeight + 10); // 10px for anchor; 18px for label if not position-absolute
if (supportedTransform) {
self.el.style[supportedTransform] = "translate(" + x + "px, " + y + "px)";
} else {
self.el.style.left = x + "px";
self.el.style.top = y + "px";
}
self.el.style.visibility = "visible";
};
if (self.el.offsetWidth && self.el.offsetHeight) {
setPosition();
} else {
//delayed left/top calculation when width/height are not set instantly
setTimeout(setPosition, 300);
}
}
});
};
CustomMarker.prototype.setZIndex = function (zIndex) {
if (zIndex === undefined) return;
(this.zIndex !== zIndex) && (this.zIndex = zIndex); /* jshint ignore:line */
(this.el.style.zIndex !== this.zIndex) && (this.el.style.zIndex = this.zIndex);
};
CustomMarker.prototype.getVisible = function () {
return this.visible;
};
CustomMarker.prototype.setVisible = function (visible) {
if (this.el.style.display === 'none' && visible) {
this.el.style.display = 'block';
} else if (this.el.style.display !== 'none' && !visible) {
this.el.style.display = 'none';
}
this.visible = visible;
};
CustomMarker.prototype.addClass = function (className) {
var classNames = this.el.className.trim().split(' ');
(classNames.indexOf(className) == -1) && classNames.push(className); /* jshint ignore:line */
this.el.className = classNames.join(' ');
};
CustomMarker.prototype.removeClass = function (className) {
var classNames = this.el.className.split(' ');
var index = classNames.indexOf(className);
(index > -1) && classNames.splice(index, 1); /* jshint ignore:line */
this.el.className = classNames.join(' ');
};
CustomMarker.prototype.onAdd = function () {
this.getPanes().overlayMouseTarget.appendChild(this.el);
// this.getPanes().markerLayer.appendChild(label-div); // ??
};
CustomMarker.prototype.draw = function () {
this.setPosition();
this.setZIndex(this.zIndex);
this.setVisible(this.visible);
};
CustomMarker.prototype.onRemove = function () {
this.el.parentNode.removeChild(this.el);
// this.el = null;
};
} else {
setTimeout(createCustomMarkerComponent, 200);
}
}
The checkElementVisibility function helps in identifying whether a marker should appear or not.
In case there are better solutions please add it here.Thanks!
I always manage pagination with angular
retrieve all the data from the server
and cache it client side (simply put it in a service)
now I have to cope with quite lot of data
ie 10000/100000.
I'm wondering if can get into trouble
using the same method.
Imo passing parameter to server like
page search it's very annoying for a good
user experience.
UPDATE (for the point in the comment)
So a possible way to go
could be get from the server
like 1000 items at once if the user go too close
to the offset (ie it's on the 800 items)
retrieve the next 1000 items from the server
merge cache and so on
it's quite strange not even ng-grid manage pagination
sending parameters to the server
UPDATE
I ended up like:
(function(window, angular, undefined) {
'use strict';
angular.module('my.modal.stream',[])
.provider('Stream', function() {
var apiBaseUrl = null;
this.setBaseUrl = function(url) {
apiBaseUrl = url;
};
this.$get = function($http,$q) {
return {
get: function(id) {
if(apiBaseUrl===null){
throw new Error('You should set a base api url');
}
if(typeof id !== 'number'){
throw new Error('Only integer is allowed');
}
if(id < 1){
throw new Error('Only integer greater than 1 is allowed');
}
var url = apiBaseUrl + '/' + id;
var deferred = $q.defer();
$http.get(url)
.success(function (response) {
deferred.resolve(response);
})
.error(function(data, status, headers, config) {
deferred.reject([]);
});
return deferred.promise;
}
};
};
});
})(window, angular);
(function(window, angular, undefined) {
'use strict';
angular.module('my.mod.pagination',['my.mod.stream'])
.factory('Paginator', function(Stream) {
return function(pageSize) {
var cache =[];
var staticCache =[];
var hasNext = false;
var currentOffset= 0;
var numOfItemsXpage = pageSize;
var numOfItems = 0;
var totPages = 0;
var currentPage = 1;
var end = 0;
var start = 0;
var chunk = 0;
var currentChunk = 1;
var offSetLimit = 0;
var load = function() {
Stream.get(currentChunk).then(function(response){
staticCache = _.union(staticCache,response.data);
cache = _.union(cache,response.data);
chunk = response.chunk;
loadFromCache();
});
};
var loadFromCache= function() {
numOfItems = cache.length;
offSetLimit = (currentPage*numOfItemsXpage)+numOfItemsXpage;
if(offSetLimit > numOfItems){
currentChunk++;
load();
}
hasNext = numOfItems > numOfItemsXpage;
totPages = Math.ceil(numOfItems/numOfItemsXpage);
paginator.items = cache.slice(currentOffset, numOfItemsXpage*currentPage);
start = totPages + 1;
end = totPages+1;
hasNext = numOfItems > (currentPage * numOfItemsXpage);
};
var paginator = {
items : [],
notFilterLabel: '',
hasNext: function() {
return hasNext;
},
hasPrevious: function() {
return currentOffset !== 0;
},
hasFirst: function() {
return currentPage !== 1;
},
hasLast: function() {
return totPages > 2 && currentPage!==totPages;
},
next: function() {
if (this.hasNext()) {
currentPage++;
currentOffset += numOfItemsXpage;
loadFromCache();
}
},
previous: function() {
if(this.hasPrevious()) {
currentPage--;
currentOffset -= numOfItemsXpage;
loadFromCache();
}
},
toPageId:function(num){
currentPage=num;
currentOffset= (num-1) * numOfItemsXpage;
loadFromCache();
},
first:function(){
this.toPageId(1);
},
last:function(){
this.toPageId(totPages);
},
getNumOfItems : function(){
return numOfItems;
},
getCurrentPage: function() {
return currentPage;
},
getEnd: function() {
return end;
},
getStart: function() {
return start;
},
getTotPages: function() {
return totPages;
},
getNumOfItemsXpage:function(){
return numOfItemsXpage;
},
search:function(str){
if(str===this.notFilterLabel){
if(angular.equals(staticCache, cache)){
return;
}
cache = staticCache;
}
else{
cache = staticCache;
cache = _.filter(cache, function(item){ return item.type == str; });
}
currentPage = 1;
currentOffset= 0;
loadFromCache();
}
};
load();
return paginator;
}
});
})(window, angular);
server side with laravel (All the items are cached)
public function tag($page)
{
$service = new ApiTagService(new ApiTagModel());
$items = $service->all();
$numOfItems = count($items);
if($numOfItems > 0){
$length = self::CHUNK;
if($length > $numOfItems){
$length = $numOfItems;
}
$numOfPages = ceil($numOfItems/$length);
if($page > $numOfPages){
$page = $numOfPages;
}
$offSet = ($page - 1) * $length;
$chunk = array_slice($items, $offSet, $length);
return Response::json(array(
'status'=>200,
'pages'=>$numOfPages,
'chunk'=>$length,
'data'=> $chunk
),200);
}
return Response::json(array(
'status'=>200,
'data'=> array()
),200);
}
The only trouble by now is managing filter
I've no idea how to treat filtering :(