Related
I have drift's async script code in the index.html file of the react app.
<script>
"use strict";
!function () {
var t = window.driftt = window.drift = window.driftt || [];
if (!t.init) {
if (t.invoked) return void (window.console && console.error && console.error("Drift snippet included twice."));
t.invoked = !0, t.methods = ["identify", "config", "track", "reset", "debug", "show", "ping", "page", "hide", "off", "on"],
t.factory = function (e) {
return function () {
var n = Array.prototype.slice.call(arguments);
return n.unshift(e), t.push(n), t;
};
}, t.methods.forEach(function (e) {
t[e] = t.factory(e);
}), t.load = function (t) {
var e = 3e5, n = Math.ceil(new Date() / e) * e, o = document.createElement("script");
o.type = "text/javascript", o.async = !0, o.crossorigin = "anonymous", o.src = "https://js.driftt.com/include/" + n + "/" + t + ".js";
var i = document.getElementsByTagName("script")[0];
i.parentNode.insertBefore(o, i);
};
}
}();
drift.SNIPPET_VERSION = '0.3.1';
drift.load('----api----');
drift.on('ready', api => {
api.widget.hide();
})
</script>
The issue is, it is getting popped up in every page of the app whereas I want it only when I click a button(onClick)
The function to trigger onClick :
openDriftChat = () =>{
const { setDriftState } = this.props;
if (window.drift.api) {
//this needs to happen only once but currently happening on every page load
if (!this.props.driftInit) {
if (localStorage.token) {
var tokenBase64 = localStorage.token.split(".")[1];
var tokenBase64_1 = tokenBase64.replace("-", "+").replace("_", "/");
var token = JSON.parse(window.atob(tokenBase64_1));
window.drift.identify(token.email, {
email: token.email,
nickname: token.name
});
setDriftState(true);
}
}
window.drift.api.openChat();
}
}
I basically want it pop up only when I call the function.
Hello I had the same issue:
To hide the welcome message use the following css code
iframe#drift-widget.drift-widget-welcome-expanded-online {
display: none !important;
}
iframe#drift-widget.drift-widget-welcome-expanded-away {
display: none !important;
}
The welcome message will only be shown when your drift button. Some extra info:
To hide the drift button icon use the following js code
drift.on('ready', function (api) {
api.widget.hide()
drift.on('message', function (e) {
if (!e.data.sidebarOpen) {
api.widget.show()
}
})
drift.on('sidebarClose', function (e) {
if (e.data.widgetVisible) {
api.widget.hide()
}
})
})
To call for the sidebar from a specific button use the following
Javascript
(function () {
var DRIFT_CHAT_SELECTOR = '.drift-open-chat'
function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', fn);
} else {
document.attachEvent('onreadystatechange', function () {
if (document.readyState != 'loading')
fn();
});
}
}
function forEachElement(selector, fn) {
var elements = document.querySelectorAll(selector);
for (var i = 0; i < elements.length; i++)
fn(elements[i], i);
}
function openSidebar(driftApi, event) {
event.preventDefault();
driftApi.sidebar.open();
return false;
}
ready(function () {
drift.on('ready', function (api) {
var handleClick = openSidebar.bind(this, api)
forEachElement(DRIFT_CHAT_SELECTOR, function (el) {
el.addEventListener('click', handleClick);
});
});
});
})();
HTML
<a class="drift-open-chat">Open Chat</a>
I hope this helps someone out there.
PS: The above javascript code must be included after you have initialized your drift widget.
You need to disable that through the application: turn off the Playbooks.
Here is the link to do so: https://app.drift.com/playbooks
Hope it helps.
I would like to know if it was possible to update several pages in a single controller?
I need to refresh my home page after adding data to my list.
For now I use a pull to refresh in my homepage, but I would like it to update after an addition in my list.
my controller :
.controller('addCRCtrl', function ($scope, $state, $stateParams, $ionicPopup, $ionicLoading, $timeout, AppService) {
let loadData = () => {
$ionicLoading.show();
$scope.loading=true;
$scope.form = {};
$scope.formProjet = {};
$scope.form.patient = idjeune;
$scope.form.dateVisite = new Date();
AppService.patient(idjeune).then(function (response) {
$scope.patient = response;
console.log(response);
});
AppService.user().then(function (response) {
$scope.bene1 = response;
console.log(response);
});
AppService.hopitaux().then(function (response) {
$scope.hopitaux = response;
});
AppService.lieuxlist().then(function (response) {
$scope.lieux = response;
});
AppService.userByAntenneAddCR().then(function (response) {
console.log(response);
$scope.listeBene = JSON.parse(JSON.stringify(response));
});
AppService.typeVisite().then(function (response) {
$scope.listeVisite = JSON.parse(JSON.stringify(response));
});
AppService.projet(idjeune).then(function (response) {
$scope.projets = response;
});
AppService.getCategorie().then(function (response) {
$scope.categorieprojet = response;
console.log(response);
if (update) {
$scope.updating = update;
$scope.form.dateVisite = new Date(cr.date);
for (let i in $scope.lieux) {
if ($scope.lieux[i].id == cr.idLieu) {
$scope.lieu = $scope.lieux[i];
break;
}
}
$scope.form.lieudetail = cr.lieudetail;
$scope.bene1 = {
id: cr.bene1,
nom: cr.benevole1
}
$scope.bene2 = {
id: cr.bene2,
nom: cr.benevole2
}
$scope.bene3 = {
id: cr.bene3,
nom: cr.benevole3
}
console.log("Benevole 1 : ", $scope.bene1);
console.log("Benevole 2 : ", $scope.bene2);
console.log("Benevole 3 : ", $scope.bene3);
$scope.form.description = cr.compteRenduVisite;
$scope.form.todo = cr.todo;
$scope.form.etatpatient = cr.etatPatient;
for (let i in $scope.listeVisite) {
if ($scope.listeVisite[i].id == cr.typeVisite) {
$scope.typeVisite = $scope.listeVisite[i];
break;
}
}
for (let i in $scope.projets) {
if ($scope.projets[i].id == cr.idProjet) {
$scope.projet = $scope.projets[i];
break;
}
}
$timeout(() => { $ionicLoading.hide(); $scope.loading=false;;}, 6000);
}
else
$timeout(() => { $ionicLoading.hide(); $scope.loading=false;}, 2000);
});
}
$scope.$on('$ionicView.beforeEnter', function (event, viewData) {
loadData();
});
$scope.addCr = function () {
$ionicLoading.show();
let isValid = $scope.form.dateVisite && $scope.patient && $scope.bene1 && $scope.form.description && $scope.form.todo;
isValid = isValid && $scope.form.etatpatient && $scope.typeVisite;
let formInvalid = () => {
$ionicLoading.hide();
var alertPopup = $ionicPopup.alert({
template: '<p style="text-align: center;">Veuillez renseigner tous les champs</p>'
});
}
if (!isValid) {
formInvalid();
return;
}
$scope.form.lieu = ($scope.lieu)? $scope.lieu.id:0;
$scope.form.bene1 = $scope.bene1.id;
$scope.form.bene2 = ($scope.bene2) ? $scope.bene2.id : null;
$scope.form.bene3 = ($scope.bene3) ? $scope.bene3.id : null;
$scope.form.lieudetail = ($scope.form.lieudetail) ? $scope.form.lieudetail : "-";
let temp = $scope.form.dateVisite;
//$scope.form.dateVisite = $scope.form.dateVisite.toISOString().slice(0, 10);
let newProject = false;
$scope.form.typeVisite = $scope.typeVisite.id;
switch ($scope.typeVisite.id) {
case 3:
newProject = true;
isValid = isValid && $scope.cprojet && $scope.formProjet.description && $scope.formProjet.titre;
break;
case 4:
case 5:
if ($scope.projet) $scope.form.projet = $scope.projet.id;
isValid = isValid && $scope.projet;
break;
default:
$scope.form.projet = 0;
break;
}
for (var i in $scope.hopitaux) {
if ($scope.hopitaux[i].nom === $scope.patient.hopital) {
$scope.form.hopital = $scope.hopitaux[i].id;
}
}
for (var j in $scope.form) {
if ($scope.form[j] == null) {
if (j == 'bene2' || j == 'bene3')
$scope.form[j] = 0;
else
delete $scope.form[j];
}
}
if (!isValid) {
formInvalid();
return;
}
let callService = () => {
var myform = angular.copy($scope.form);
if (update)
myform.dateVisite = new Date($scope.form.dateVisite.getTime() + 86400000).toISOString().slice(0, 10);
else
myform.dateVisite = $scope.form.dateVisite.toISOString().slice(0, 10);
console.log(myform);
let idtogo = (update) ? cr.id : null;
AppService.addCR(myform, idtogo).then(function (response) {
update = false;
cr = null;
var alertPopup = $ionicPopup.alert({
title: 'Ajout Réussie !',
template: '<p style="text-align: center;">Merci d\'avoir renseigné les informations relatives à la visite !</p>'
});
$scope.form.dateVisite = temp;
console.log(response);
$ionicLoading.hide();
$state.reload('menu.ficheJeune');
$state.go('menu.ficheJeune');
});
}
if (!newProject || update)
callService();
else {
$scope.formProjet.categorie = $scope.cprojet.id;
$scope.formProjet.patient = idjeune;
var myprojectform = angular.copy($scope.formProjet);
console.log(myprojectform);
AppService.addProject(myprojectform).then((response) => {
console.log(response);
if (response) {
AppService.projet(idjeune).then(function (response) {
for (let i in response) {
console.log("Response :", response);
let temp = false;
for (let j in $scope.projets) {
if (response[i].id == $scope.projets[j].id) {
temp = true;
}
}
if (!temp) {
$scope.form.projet = response[i].id;
callService();
break;
}
}
});
}
});
}
};
AppService.refreshToken().then(function (response) {
if ($scope.token) {
console.info('Refresh');
}
});
})
For now, to update my first page I do:
$state.reload('menu.ficheJeune');
$state.go('menu.ficheJeune');
But I would also like to update a second page called 'menu.home'.
I thank you in advance
You can use this in your homeCtrl :
$scope.$on('$ionicView.beforeEnter', function() {
console.log('beforeEnter');
// your code
});
I'm making an ionic application. I have a function called scope.win() that's supposed to fire when a quiz is finished, and then update the JSON file to add that quiz to the number of finished quizzes but it doesn't run properly.It doesn't give off any error. The function just stops running at a certain point and the app keeps going and nothing is happening.
This is the controller's file
angular.module('controllers', ['services'])
.controller('MainCtrl', function ($scope, $state, data) {
$scope.$on('$ionicView.enter', function () {
data.create();
});
$scope.chapter = data.chapterProgress();
})
.controller("BtnClick", function ($scope, lives, data, $cordovaFile, $ionicScrollDelegate) {
var live = 3;
var clickedOn = [];
var numQuestions;
$scope.part2Cred = false;
$scope.part3Cred = false;
$scope.part4Cred = false;
$scope.part5Cred = false;
$scope.part6Cred = false;
$scope.part7Cred = false;
$scope.part8Cred = false;
$scope.part9Cred = false;
$scope.part10Cred = false;
$scope.partQCred = false;
$scope.setNumQuestions = function(num){
numQuestions = num;
}
$scope.updatelives = function (){
//grabs the element that is called liv then updates it
var livesOnPage = document.getElementById('liv');
livesOnPage.innerHTML = live;
}
$scope.wrong = function (event){
var selec = document.getElementById(event.target.id);
if(clickedOn.includes(selec)){}
else{
selec.style.color = "grey";
clickedOn.push(selec);
live = live - 1;
if(live == 0){
$scope.gameover();
}
else{
$scope.updatelives();
}
}
}
$scope.right = function (event,chapter, section){
var selec = document.getElementById(event.target.id);
if(clickedOn.includes(selec)){}
else{
selec.style.color = "green";
clickedOn.push(selec);
numQuestions = numQuestions - 1;
if(numQuestions === 0){
$scope.win(chapter, section);
}
}
}
$scope.gameover = function(){
alert("game over please try again");
live = 3;
$ionicScrollDelegate.scrollTop();
$scope.partQCred = false;
$scope.part1Cred = !$scope.part1Cred;
for(i = 0; i< clickedOn.length;i++){
clickedOn[i].style.color = "rgb(68,68,68)";
}
}
$scope.win = function (chapter, section) {
alert("Well Done");
var data = data.chapterProgress(); // It is at this point that the //function stops running without giving off any error.
alert("Good Job");
var sectionsComplete = data[chapter].sectionsCompleted;
var totalsection = data[chapter].totalSections;
if (section === totalSection) {
window.location.href = "#/chapter1sections";
return;
}
if (section > sectionsComplete) {
data[chapter].sectionsCompleted += 1;
var url = "";
if (ionic.Platform.isAndroid()) {
url = "/android_asset/www/";
}
document.addEventListener('deviceready', function () {
$cordovaFile.writeFile(url + "js/", "chapters.json", data, true)
.then(function (success) {
// success
alert("Json file updated")
window.location.href = "#/chapter1sections";
}, function (error) {
// error
});
});
}
}
});
Here is the Services file. It seems to run fine but it is referenced a lot in the problematic sections of code in the controllers so I figured it was necessary to put it here.
angular.module('services', [])
.service('lives', function () {
var live = 3;
return {
getlives: function () {
return live;
},
setlives: function (value) {
live = value;
}
};
})
.service('data', function ($cordovaFile, $http) {
var url = "";
if (ionic.Platform.isAndroid()) {
url = "/android_asset/www/";
}
return {
//Run this function on startup to check if the chapters.json file exists, if not, then it will be created
create: function () {
var init = {
"one": {
"sectionsCompleted": 0,
"totalSections": 4
},
"two": {
"sectionsCompleted": 0,
"totalSections": 1
}
};
$cordovaFile.writeFile(url + "js/", "chapters.json", init, false)
.then(function (success) {
// success
}, function (error) {
// error
});
},
chapterProgress: function () {
return $http.get(url + "js/chapters.json").success(function (response) {
var json = JSON.parse(response);
return json;
}, function (error) {
alert(error);
});
}
}
});
Thank You so much for the help and time.
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.
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 :(