Related
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 am using bootstrap-wysiwyg rich text editor in my application and i am not able to open the hyerlink created in a new window.
i am using the bootstrap-wysiwyg.js file which is below. i am not able to figure out how to make the hyperlink created, to open in a new tab.
(function ($) {
'use strict';
/** underscoreThrottle()
* From underscore http://underscorejs.org/docs/underscore.html
*/
var underscoreThrottle = function(func, wait) {
var context, args, timeout, result;
var previous = 0;
var later = function() {
previous = new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
}
var readFileIntoDataUrl = function (fileInfo) {
var loader = $.Deferred(),
fReader = new FileReader();
fReader.onload = function (e) {
loader.resolve(e.target.result);
};
fReader.onerror = loader.reject;
fReader.onprogress = loader.notify;
fReader.readAsDataURL(fileInfo);
return loader.promise();
};
$.fn.cleanHtml = function (o) {
if ( $(this).data("wysiwyg-html-mode") === true ) {
$(this).html($(this).text());
$(this).attr('contenteditable',true);
$(this).data('wysiwyg-html-mode',false);
}
// Strip the images with src="data:image/.." out;
if ( o === true && $(this).parent().is("form") ) {
var gGal = $(this).html;
if ( $(gGal).has( "img" ).length ) {
var gImages = $( "img", $(gGal));
var gResults = [];
var gEditor = $(this).parent();
$.each(gImages, function(i,v) {
if ( $(v).attr('src').match(/^data:image\/.*$/) ) {
gResults.push(gImages[i]);
$(gEditor).prepend("<input value='"+$(v).attr('src')+"' type='hidden' name='postedimage/"+i+"' />");
$(v).attr('src', 'postedimage/'+i);
}});
}
}
var html = $(this).html();
return html && html.replace(/(<br>|\s|<div><br><\/div>| )*$/, '');
};
$.fn.wysiwyg = function (userOptions) {
var editor = this,
wrapper = $(editor).parent(),
selectedRange,
options,
toolbarBtnSelector,
updateToolbar = function () {
if (options.activeToolbarClass) {
$(options.toolbarSelector,wrapper).find(toolbarBtnSelector).each(underscoreThrottle(function () {
var commandArr = $(this).data(options.commandRole).split(' '),
command = commandArr[0];
// If the command has an argument and its value matches this button. == used for string/number comparison
if (commandArr.length > 1 && document.queryCommandEnabled(command) && document.queryCommandValue(command) == commandArr[1]) {
$(this).addClass(options.activeToolbarClass);
// Else if the command has no arguments and it is active
} else if (commandArr.length === 1 && document.queryCommandEnabled(command) && document.queryCommandState(command)) {
$(this).addClass(options.activeToolbarClass);
// Else the command is not active
} else {
$(this).removeClass(options.activeToolbarClass);
}
}, options.keypressTimeout));
}
},
execCommand = function (commandWithArgs, valueArg) {
var commandArr = commandWithArgs.split(' '),
command = commandArr.shift(),
args = commandArr.join(' ') + (valueArg || '');
var parts = commandWithArgs.split('-');
if ( parts.length == 1 ) {
document.execCommand(command, 0, args);
}
else if ( parts[0] == 'format' && parts.length == 2) {
document.execCommand('formatBlock', false, parts[1] );
}
editor.trigger('change');
updateToolbar();
},
bindHotkeys = function (hotKeys) {
$.each(hotKeys, function (hotkey, command) {
editor.keydown(hotkey, function (e) {
if (editor.attr('contenteditable') && editor.is(':visible')) {
e.preventDefault();
e.stopPropagation();
execCommand(command);
}
}).keyup(hotkey, function (e) {
if (editor.attr('contenteditable') && editor.is(':visible')) {
e.preventDefault();
e.stopPropagation();
}
});
});
editor.keyup(function(){ editor.trigger('change'); });
},
getCurrentRange = function () {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
}
} else if (document.selection) {
range = document.selection.createRange();
} return range;
},
saveSelection = function () {
selectedRange = getCurrentRange();
},
restoreSelection = function () {
var selection;
if (window.getSelection || document.createRange) {
selection = window.getSelection();
if (selectedRange) {
try {
selection.removeAllRanges();
} catch (ex) {
document.body.createTextRange().select();
document.selection.empty();
}
selection.addRange(selectedRange);
}
}
else if (document.selection && selectedRange) {
selectedRange.select()
}
},
// Adding Toggle HTML based on the work by #jd0000, but cleaned up a little to work in this context.
toggleHtmlEdit = function(a) {
if ( $(editor).data("wysiwyg-html-mode") !== true ) {
var oContent = $(editor).html();
var editorPre = $( "<pre />" )
$(editorPre).append( document.createTextNode( oContent ) );
$(editorPre).attr('contenteditable',true);
$(editor).html(' ');
$(editor).append($(editorPre));
$(editor).attr('contenteditable', false);
$(editor).data("wysiwyg-html-mode", true);
$(editorPre).focus();
}
else {
$(editor).html($(editor).text());
$(editor).attr('contenteditable',true);
$(editor).data('wysiwyg-html-mode',false);
$(editor).focus();
}
},
insertFiles = function (files) {
editor.focus();
$.each(files, function (idx, fileInfo) {
if (/^image\//.test(fileInfo.type)) {
$.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
execCommand('insertimage', dataUrl);
editor.trigger('image-inserted');
}).fail(function (e) {
options.fileUploadError("file-reader", e);
});
} else {
options.fileUploadError("unsupported-file-type", fileInfo.type);
}
});
},
markSelection = function (input, color) {
restoreSelection();
if (document.queryCommandSupported('hiliteColor')) {
document.execCommand('hiliteColor', 0, color || 'transparent');
}
saveSelection();
input.data(options.selectionMarker, color);
},
bindToolbar = function (toolbar, options) {
toolbar.find(toolbarBtnSelector, wrapper).click(function () {
restoreSelection();
editor.focus();
if ($(this).data(options.commandRole) === 'html') {
toggleHtmlEdit();
}
else {
execCommand($(this).data(options.commandRole));
}
saveSelection();
});
toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
this.value = '';
restoreSelection();
if (newValue) {
editor.focus();
execCommand($(this).data(options.commandRole), newValue);
}
saveSelection();
}).on('focus', function () {
var input = $(this);
if (!input.data(options.selectionMarker)) {
markSelection(input, options.selectionColor);
input.focus();
}
}).on('blur', function () {
var input = $(this);
if (input.data(options.selectionMarker)) {
markSelection(input, false);
}
});
toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
restoreSelection();
if (this.type === 'file' && this.files && this.files.length > 0) {
insertFiles(this.files);
}
saveSelection();
this.value = '';
});
},
initFileDrops = function () {
editor.on('dragenter dragover', false)
.on('drop', function (e) {
var dataTransfer = e.originalEvent.dataTransfer;
e.stopPropagation();
e.preventDefault();
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
insertFiles(dataTransfer.files);
}
});
};
options = $.extend(true, {}, $.fn.wysiwyg.defaults, $.fn.wysiwyg.defaults1, userOptions);
toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']';
bindHotkeys(options.hotKeys);
// Support placeholder attribute on the DIV
if ($(this).attr('placeholder') != '') {
$(this).addClass('placeholderText');
$(this).html($(this).attr('placeholder'));
$(this).bind('focus',function(e) {
if ( $(this).attr('placeholder') != '' && $(this).text() == $(this).attr('placeholder') ) {
$(this).removeClass('placeholderText');
$(this).html('');
}
});
$(this).bind('blur',function(e) {
if ( $(this).attr('placeholder') != '' && $(this).text() == '' ) {
$(this).addClass('placeholderText');
$(this).html($(this).attr('placeholder'));
}
})
}
if (options.dragAndDropImages) {
initFileDrops();
}
bindToolbar($(options.toolbarSelector), options);
editor.attr('contenteditable', true)
.on('mouseup keyup mouseout', function () {
saveSelection();
updateToolbar();
});
$(window).bind('touchend', function (e) {
var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
currentRange = getCurrentRange(),
clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
if (!clear || isInside) {
saveSelection();
updateToolbar();
}
});
return this;
};
$.fn.wysiwyg.defaults = {
hotKeys: {
'Ctrl+b meta+b': 'bold',
'Ctrl+i meta+i': 'italic',
'Ctrl+u meta+u': 'underline',
'Ctrl+z': 'undo',
'Ctrl+y meta+y meta+shift+z': 'redo',
'Ctrl+l meta+l': 'justifyleft',
'Ctrl+r meta+r': 'justifyright',
'Ctrl+e meta+e': 'justifycenter',
'Ctrl+j meta+j': 'justifyfull',
'Shift+tab': 'outdent',
'tab': 'indent'
},
toolbarSelector: '[data-role=editor-toolbar]',
commandRole: 'edit',
activeToolbarClass: 'btn-info',
selectionMarker: 'edit-focus-marker',
selectionColor: 'darkgrey',
dragAndDropImages: true,
keypressTimeout: 200,
fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
};
$.fn.wysiwyg.defaults1 = {
hotKeys: {
'Ctrl+b meta+b': 'bold',
'Ctrl+i meta+i': 'italic',
'Ctrl+u meta+u': 'underline',
'Ctrl+z': 'undo',
'Ctrl+y meta+y meta+shift+z': 'redo',
'Ctrl+l meta+l': 'justifyleft',
'Ctrl+r meta+r': 'justifyright',
'Ctrl+e meta+e': 'justifycenter',
'Ctrl+j meta+j': 'justifyfull',
'Shift+tab': 'outdent',
'tab': 'indent'
},
toolbarSelector: '[data-role=editor1-toolbar]',
commandRole: 'edit',
activeToolbarClass: 'btn-info',
selectionMarker: 'edit-focus-marker',
selectionColor: 'darkgrey',
dragAndDropImages: true,
keypressTimeout: 200,
fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
};
}(window.jQuery));
#
i am saving the editor contents and then i am retrieving it in a different page as below.
<p class="textAlignLeft" ng-bind-html="editorContent | unsafe"></p>
"editorContent" will have the contents entered in the richtext editor, and the hyper link in it has to open in a new window.
in the browser console i am getting the following output.
<p class="textAlignLeft ng-binding" ng-bind-html="editorContent | unsafe">ajslkjsak sdsad</p>
One way of doing that would be to bind to click event of anchor tag and open URL in new tab using JavaScript.
Say, the ID of your editor is "editor", the following code would work with it
$("a", "#editor").click(function(e) {
window.open($(this).attr('href'), '_blank')
});
this will bind click event on all the a tags inside editor div and when the user clicks any of it, the url will be opened in new window.
Update:
using jQuery, it is very easy. First assign an id to your <p> tag like
<p id="myCustomContent" class="textAlignLeft" ng-bind-html="newsContent | unsafe"></p>
Where myCustomContent is the id
now use the following code
$("a", "#myCustomContent").each(function() {
$(this).attr('target', '_blank');
});
this will loop once on all the anchor tags and make them open in new tab when user clicks on them.
Is it possible to leverage FuelUX spinbox to cycle/scroll through array of custom strings?
This is an example of what I mean, but it's a jQueryUI implementation:
Links to jsfiddle.net must be accompanied by code.
Please indent all code by 4 spaces using the code
toolbar button or the CTRL+K keyboard shortcut.
For more editing help, click the [?] toolbar icon.
http://jsfiddle.net/MartynDavis/gzmvc2ds/
Not out of the box, no.
You'd have to modify this portion of spinbox to make newVal be set by accessing your desired array instead of doing maths:
step: function step(isIncrease) {
//refresh value from display before trying to increment in case they have just been typing before clicking the nubbins
this.setValue(this.getDisplayValue());
var newVal;
if (isIncrease) {
newVal = this.options.value + this.options.step;
} else {
newVal = this.options.value - this.options.step;
}
newVal = newVal.toFixed(5);
this.setValue(newVal + this.unit);
},
Something quick and dirty based on FuelUX spinbox:
/*
* Fuel UX SpinStrings
* https://github.com/ExactTarget/fuelux
*
* Copyright (c) 2014 ExactTarget
* Licensed under the BSD New license.
*/
// -- BEGIN UMD WRAPPER PREFACE --
// For more information on UMD visit:
// https://github.com/umdjs/umd/blob/master/jqueryPlugin.js
(function (factory) {
if (typeof define === 'function' && define.amd) {
// if AMD loader is available, register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// OR use browser globals if AMD is not present
factory(jQuery);
}
}(function ($) {
// -- END UMD WRAPPER PREFACE --
// -- BEGIN MODULE CODE HERE --
var old = $.fn.spinstrings;
// SPINSTRINGS CONSTRUCTOR AND PROTOTYPE
var SpinStrings = function SpinStrings(element, options) {
this.$element = $(element);
this.$element.find('.btn').on('click', function (e) {
//keep spinstrings from submitting if they forgot to say type="button" on their spinner buttons
e.preventDefault();
});
if ($.isPlainObject(options) && 'options' in options) {
if (!$.isArray(options.options)) {
delete options.options;
} else {
options.min = 0;
options.max = options.options.length - 1;
if (options.value && ((idx = options.options.indexOf(options.value)) > -1)) {
options.index = idx;
} else {
options.index = 0;
}
}
}
this.options = $.extend({}, $.fn.spinstrings.defaults, options);
if (this.options.index < this.options.min) {
this.options.index = this.options.min;
} else if (this.options.max < this.options.index) {
this.options.index = this.options.max;
}
this.$input = this.$element.find('.spinstrings-input');
this.$input.on('focusout.fu.spinstrings', this.$input, $.proxy(this.change, this));
this.$element.on('keydown.fu.spinstrings', this.$input, $.proxy(this.keydown, this));
this.$element.on('keyup.fu.spinstrings', this.$input, $.proxy(this.keyup, this));
this.bindMousewheelListeners();
this.mousewheelTimeout = {};
this.$element.on('click.fu.spinstrings', '.spinstrings-up', $.proxy(function () {
this.step(true);
}, this));
this.$element.on('click.fu.spinstrings', '.spinstrings-down', $.proxy(function () {
this.step(false);
}, this));
this.lastValue = this.options.value;
this.render();
if (this.options.disabled) {
this.disable();
}
};
// Truly private methods
var _applyLimits = function(value) {
// if unreadable
if (isNaN(parseFloat(value))) {
return value;
}
// if not within range return the limit
if (value > this.options.max) {
if (this.options.cycle) {
value = this.options.min;
} else {
value = this.options.max;
}
} else if (value < this.options.min) {
if (this.options.cycle) {
value = this.options.max;
} else {
value = this.options.min;
}
}
return value;
};
SpinStrings.prototype = {
constructor: SpinStrings,
destroy: function destroy() {
this.$element.remove();
// any external bindings
// [none]
// set input value attrbute
this.$element.find('input').each(function () {
$(this).attr('value', $(this).val());
});
// empty elements to return to original markup
// [none]
// returns string of markup
return this.$element[0].outerHTML;
},
render: function render() {
this.setValue(this.getDisplayValue());
},
change: function change() {
this.setValue(this.getDisplayValue());
this.triggerChangedEvent();
},
triggerChangedEvent: function triggerChangedEvent() {
var currentValue = this.getValue();
if (currentValue === this.lastValue) return;
this.lastValue = currentValue;
// Primary changed event
this.$element.trigger('changed.fu.spinstrings', currentValue);
},
step: function step(isIncrease) {
//refresh value from display before trying to increment in case they have just been typing before clicking the nubbins
this.setValue(this.getDisplayValue());
var newVal;
if (isIncrease) {
newVal = this.options.index + this.options.step;
} else {
newVal = this.options.index - this.options.step;
}
newVal = _applyLimits.call(this, newVal);
newVal = this.getOptionByIndex(newVal);
this.setValue(newVal);
},
getDisplayValue: function getDisplayValue() {
var inputValue = this.$input.val();
var value = (!!inputValue) ? inputValue : this.options.value;
return value;
},
/**
* #param string value
*/
setDisplayValue: function setDisplayValue(value) {
this.$input.val(value);
},
/**
* #return string
*/
getValue: function getValue() {
return this.options.value;
},
/**
* #param string val
*/
setValue: function setValue(val) {
var intVal = this.getIndexByOption(val);
//cache the pure int value
this.options.value = val;
this.options.index = intVal;
//display number
this.setDisplayValue(val);
return this;
},
value: function value(val) {
if (val || val === 0) {
return this.setValue(val);
} else {
return this.getValue();
}
},
/**
* Get string's position in array of options.
*
* #param string value
* #return integer
*/
getIndexByOption: function(value) {
ret = null;
if (this.options.options) {
ret = this.options.options.indexOf(value);
}
return ret;
},
/**
* Get option string by index.
*
* #param integer index
* #return string
*/
getOptionByIndex: function(value) {
ret = null;
if (this.options.options[value]) {
ret = this.options.options[value];
}
return ret;
},
disable: function disable() {
this.options.disabled = true;
this.$element.addClass('disabled');
this.$input.attr('disabled', '');
this.$element.find('button').addClass('disabled');
},
enable: function enable() {
this.options.disabled = false;
this.$element.removeClass('disabled');
this.$input.removeAttr('disabled');
this.$element.find('button').removeClass('disabled');
},
keydown: function keydown(event) {
var keyCode = event.keyCode;
if (keyCode === 38) {
this.step(true);
} else if (keyCode === 40) {
this.step(false);
} else if (keyCode === 13) {
this.change();
}
},
keyup: function keyup(event) {
var keyCode = event.keyCode;
if (keyCode === 38 || keyCode === 40) {
this.triggerChangedEvent();
}
},
bindMousewheelListeners: function bindMousewheelListeners() {
var inputEl = this.$input.get(0);
if (inputEl.addEventListener) {
//IE 9, Chrome, Safari, Opera
inputEl.addEventListener('mousewheel', $.proxy(this.mousewheelHandler, this), false);
// Firefox
inputEl.addEventListener('DOMMouseScroll', $.proxy(this.mousewheelHandler, this), false);
} else {
// IE <9
inputEl.attachEvent('onmousewheel', $.proxy(this.mousewheelHandler, this));
}
},
mousewheelHandler: function mousewheelHandler(event) {
if (!this.options.disabled) {
var e = window.event || event;// old IE support
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
var self = this;
clearTimeout(this.mousewheelTimeout);
this.mousewheelTimeout = setTimeout(function () {
self.triggerChangedEvent();
}, 300);
if (delta > 0) {//ACE
this.step(true);
} else {
this.step(false);
}
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
return false;
}
}
};
// SPINSTRINGS PLUGIN DEFINITION
$.fn.spinstrings = function spinstrings(option) {
var args = Array.prototype.slice.call(arguments, 1);
var methodReturn;
var $set = this.each(function () {
var $this = $(this);
var data = $this.data('fu.spinstrings');
var options = typeof option === 'object' && option;
if (!data) {
$this.data('fu.spinstrings', (data = new SpinStrings(this, options)));
}
if (typeof option === 'string') {
methodReturn = data[option].apply(data, args);
}
});
return (methodReturn === undefined) ? $set : methodReturn;
};
// value needs to be 0 for this.render();
$.fn.spinstrings.defaults = {
value: null,
index: 0,
min: 0,
max: 0,
step: 1,
disabled: false,
cycle: true
};
$.fn.spinstrings.Constructor = SpinStrings;
$.fn.spinstrings.noConflict = function noConflict() {
$.fn.spinstrings = old;
return this;
};
// DATA-API
$(document).on('mousedown.fu.spinstrings.data-api', '[data-initialize=spinstrings]', function (e) {
var $control = $(e.target).closest('.spinstrings');
if (!$control.data('fu.spinstrings')) {
$control.spinstrings($control.data());
}
});
// Must be domReady for AMD compatibility
$(function () {
$('[data-initialize=spinstrings]').each(function () {
var $this = $(this);
if (!$this.data('fu.spinstrings')) {
$this.spinstrings($this.data());
}
});
});
// -- BEGIN UMD WRAPPER AFTERWORD --
}));
// -- END UMD WRAPPER AFTERWORD --
Mark-up & style:
<style>
.spinstrings {position:relative;display:inline-block;margin-left:-2px}
.spinstrings input {height:26px;color:#444444;font-size:12px;padding:0 5px;margin:0 !important;width:100px}
.spinstrings input[readonly] {background-color:#ffffff !important}
.spinstrings .spinstrings-arrows {position:absolute;right:5px;height:22px;width:12px;background:transparent;top:1px}
.spinstrings .spinstrings-arrows i.fa {position:absolute;display:block;font-size:16px;line-height:10px;width:12px;cursor:pointer}
.spinstrings .spinstrings-arrows i.fa:hover {color:#307ecc}
.spinstrings .spinstrings-arrows i.fa.spinstrings-up {top:0}
.spinstrings .spinstrings-arrows i.fa.spinstrings-down {bottom:0}
</style>
<div class="spinstrings" id="mySpinStrings">
<input type="text" class="spinstrings-input" readonly="readonly">
<div class="spinstrings-arrows"><i class="fa fa-caret-up spinstrings-up"></i><i class="fa fa-caret-down spinstrings-down"></i></div>
</div>
Init as:
$('#mySpinStrings', expiryControlsW).spinstrings({
options: ['day', 'week', 'month'],
value: 'week'
});
That's it, folks!
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 :(