I'm trying to best integrate annotorious with angular. The Annotorius library although described, it is particularly difficult to debug. (see what I mean at annotorious.debug.js )
So far, I created my own solution with angular-annotorious (free MIT open source project DISCLAIMER: i'm the maintainer of it.), that offers 2 directives for tag and attribute cases, also a service to access the anno object.
I'm facing problems with dynamic contents, like sliders.
(note that I do know that for ng-src images special treatment may be needed due to the img src cycle)
The annotated images with an unique URL, once recreated in the DOM tree, it does not seem to get annotated once again if we call
anno.makeAnnotatable(img)
To reproduce it, see the colorbox example
(colorbox is a free open source modal library)
Are there any interesting ideas how to integrate/annotate dynamic content that may reappear?
I found the solution, instead of doing a "shutting down" annotorious, we simply call the reset function in the onLoad and onCleanup events which is critical for dynamic content like colorbox or fancybox.
The code for dynamic integration with colorbox looks like this:
$scope.annotateColorbox03 = {
rel: 'img-group-01',
slideshow: false,
open: false,
onComplete: function () {
console.log('03-complete');
var photo = colorboxService.getCurrentPhoto();
if (photo.src) {
console.log('annotateColorbox03 ' + photo.src);
annotoriousService.makeAnnotatable(photo);
var annotations = annotoriousService.getAnnotations(photo.src);
console.log(annotations);
if (annotations && annotations.length > 0) {
annotoriousService.showAnnotations(photo.src);
colorboxService.resize();
}
}
},
onLoad: function () {
console.log('03-onLoad');
annotatableImage();
},
onCleanup: function () {
console.log('03-cleanup');
annotatableImage();
}
};
function annotatableImage() {
var photo = colorboxService.getCurrentPhoto();
if (photo && photo.src) {
//required
annotoriousService.reset(photo.src);
}
}
The working example is now at:
Annotorious in Colorbox Gallery
This example relies on angular-annotorious using the annotoriousService.
DISCLAIMER: i'm the maintainer of angular-annotorious which is a free MIT open source project.
Related
I have an AngularJS app using the Angular Material UI framework.
The app has different mechanisms showing dialogs (e.g error and loading spinner) and it would be preferable to only close one specifically chosen in certain scenarios, e.g. when an AJAX request is finished fetching data, I would like my loading spinner to close, but not any error dialog that may be the result of the fetching.
What I can find in documentation and code doesn't agree (though code should win the argument):
Documentation says only the latest can be closed, with an optional response
The code says the latest, a number of latest or all open can be closed, with an optional reason
Example in the documentation says a specific dialog can be closed, with a flag denoting how or why
I have made a demo of my intent, as MCV as possible – these are the highlights:
var dialog = {},
promise = {};
function showDialogs(sourceEvent) {
showDialog(sourceEvent, "one");
showDialog(sourceEvent, "two");
}
function showDialog(sourceEvent, id) {
dialog[id] = $mdDialog.alert({...});
promise[id] = $mdDialog.show(dialog[id]);
promise[id].finally(function() {
dialog[id] = undefined;
});
}
function closeDialogs() {
$mdDialog.hide("Closed all for a reason", {closeAll: true});
}
function closeDialogLatest() {
$mdDialog.hide("Closed from the outside");
}
function closeDialogReason() {
$mdDialog.hide("Closed with a reason");
}
function closeDialogSpecific(id) {
$mdDialog.hide(dialog[id], "finished");
}
EDIT:
I know the code always wins the argument about what happens, but I wasn't entirely sure it was the right code I was looking at.
I have updated the examples to better test and illustrate my point and problem. This shows things to work as the code said.
What I'm really looking for is whether it might still be possible to achieve my goal in some other way that I didn't think of yet.
Using $mdPanel instead of $mdDialog I was able to achieve the desired effect; I forked my demo to reflect the changes – these are the highlights:
var dialog = {};
function showDialogs() {
showDialog("one");
showDialog("two");
}
function showDialog(id) {
var config = {...};
$mdPanel.open(config)
.then(function(panelRef) {
dialog[id] = panelRef;
});
}
function closeDialogs() {
var id;
for(id in dialog) {
closeDialogSpecific(id, "Closed all for a reason");
}
}
function closeDialogSpecific(id, reason) {
var message = reason || "finished: " + id;
if(!dialog.hasOwnProperty(id) || !angular.isObject(dialog[id])) {
return;
}
if(dialog[id] && dialog[id].close) {
dialog[id].close()
.then(function() {
vm.feedback = message;
});
dialog[id] = undefined;
}
}
I would suggest having two or more dialogs up at the same time isn't ideal and probably not recommended by Google Material design.
To quote from the docs
Use dialogs sparingly because they are interruptive.
You say:
when an AJAX request is finished fetching data, I would like my
loading spinner to close, but not any error dialog that may be the
result of the fetching.
My solution here would be to have one dialog which initially shows the spinner. Once the request is finished replace the spinner with any messages.
I'm trying to migrate old jQuery code to angularjs.
The issue that I'm having is that I'm not sure on the best approach.
Bascially, depending on the selector a different type of 'event' needs to be pushed into a array called gt.
The purpose of the jQuery code is to provide detailed info of clients having issues while filling in a form. the gt array is picked up by third party software that helps the clients by asking if they want to chat.
Example of how the array is populated:
$('a').live('click', { element: this }, function (element) {
_clickedElement = this;
var linkUrl = element.currentTarget.hostname + element.currentTarget.pathname;
var querystring = window.location.search
var shortLocationUrl = window.location.href.replace(querystring, "").replace("http://", "").replace("https://", "");
if (element.currentTarget.hostname.length > 0 && element.currentTarget.target != "_blank" && linkUrl != shortLocationUrl) { //click on a link that opens in the current window and points to a page external to this part
_gt.push(['event', { eventName: 'Leave_Page_' + chat.name, name: chat.name, pageName: chat.pageName, locale: _locale, isClient: chat.isClient }]);
_pushLeavePageEvent = false;
}
else if (this.id == backButtonId) { //click "previous"
_gt.push(['event', { eventName: 'Go_Back_' + chat.name, name: chat.name, pageName: chat.pageName, locale: _locale, isClient: chat.isClient }]);
_pushLeavePageEvent = false;
}
return true;
});
So for all the a tags inside my page (or form) the above code needs to be executed.
What would be a good approach to have similar behaviour in Angularjs?
I was thinking of a directive but I'm not sure whether to make this a directive at the level of my form or make a directive that I then use throughout my page?
P.S.: similar behaviour is needed (pushing an event into the gt array) for all the input, textarea and select fields on the page as well as the errors on the page caused by the clients and when a client hovers over a tooltip.
I've a link like this:
<a ui-sref="someState(Param:'مثال')"> A localized param </a>
when compiling the Angular-ui-router, generates a href like this :
A localized param
how can I avoid this?
what i have tried:
creating a new type using $urlMatcherFactoryProvider
$urlMatcherFactoryProvider.type('decoded', {
encode: function (item) {
return decodeURIComponent(item) // i put this to decode personally
},
decode: function (item) {
return decodeURIComponent(item);
},
is: function (item) {
return true;
}
});
this actually happens inside 'angular' not 'ngRoute' , angular forces encoding all urls using encodeURICompenent,
so you need to change encodeUriQuery inside angular.js so it would pass arabic characters without encoding
function encodeUriQuery(val, pctEncodeSpaces) {
var r = /[\u0600-\u06ff]|[\u0750-\u077f]|[\ufb50-\ufc3f]|[\ufe70-\ufefc]/;
if(r.test(val)){
return val.replace(/\s/g,'-');
}else{
return encodeURIComponent(val).
replace(/%40/gi, '#').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%3B/gi, ';').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
}
if you're using minified version or don't want to bother looking into code here is a monkey patch you can copy and paste this anywhere in your code
window.encode = window.encodeURIComponent;
window.encodeURIComponent = function(val){return /[\u0600-\u06ff]|[\u0750-\u077f]|[\ufb50-\ufc3f]|[\ufe70-\ufefc]/.test(val) ? val.replace(/\s/g,'-') : window.encode(val)};
notice that part replace(/\s/g,'-') it replaces whitespaces with a dash because in angular it causes some issue to have whitespace in the url
the solution it's very simple actually
you can use javaScript decodeURIComponent() function on $scope.item.title for me it's worked like charm .
in controller use it like
$state.go("single-page", { contentType: "portfolio", date: "139411", title: decodeURIComponent("اولین-نمونه-کار-تست") });
in inspect you see something like :
its better for google seo ، google understood farsi better that way
but when you click on the link in url place you see this
I am trying to integrate Sencha 4.1 (ExtJS) with the Leaflet mapping library while using Sencha Architect.
When the page loads, the tiles are mixed up and appear offset. I need to drag the page up to be able to see the tiles.
The full project is available here: https://github.com/breizo/SenchaLeaflet.
Here is an excerpt of the custom component created (see full code here: https://github.com/breizo/SenchaLeaflet/blob/master/ux/LeafletMap.js).
constructor: function () {
this.callParent(arguments);
this.on({
resize: 'doResize',
scope: this
});
var ll = window.L;
if (!ll) {
this.setHtml('Leaflet library is required');
}
}
onRender: function() {
this.callParent(arguments);
var renderTo = arguments[0].dom.id;
debugger;
var me = this,
ll = window.L,
element = me.mapContainer,
mapOptions = me.getMapOptions(),
map,
tileLayer;
if (ll) {
// if no center property is given -> use default position
if (!mapOptions.hasOwnProperty('center') || !(mapOptions.center instanceof ll.LatLng)) {
mapOptions.center = new ll.LatLng(47.36865, 8.539183); // default: Zuerich
}
me.setTileLayer(new ll.TileLayer(me.getTileLayerUrl(), me.getTileLayerOptions()));
tileLayer = me.getTileLayer();
mapOptions.layers = [tileLayer];
me.setMap(new ll.Map(renderTo, mapOptions));
map = me.getMap();
// track map events
map.on('zoomend', me.onZoomEnd, me);
map.on('movestart', me.onMoveStart, me);
map.on('moveend', me.onMoveEnd, me);
me.fireEvent('maprender', me, map, tileLayer);
}
},
When debugging it appears that when onRender is called, the parent container of the map is not properly sized yet, in particular its height is only enough to contain the attrib text, about 16 pix. WHen the doResize is called, the container is properly sized, but it doesn't change the end result: the tiles are mixed up and offset.
I tried various changes to the container, but nothing worked...
1) Problem with mixed layers is caused by CSS. Your leaflet.css has wrong path in html, so it's not attached in the document. To fix mixing issue set correct path to css file, or attach it from CDN:
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.4/leaflet.css" />
2) Wrong map offset is caused by extjs generated div:
<div class="x-llmap x-fit-item x-llmap-default" ...></div>
It pushes map container to the bottom and wrong offset calculations are made. You can also fix this using inline style or CSS:
.leaflet-map-pane {
top: 0;
}
I've completely rewritten my question to hopefully better reflect what I am trying to do here. Thank you guys so much for your help so far.
I have a file called en.js, which holds this code:
Ext.apply(Ext.locale || {}, {
variable: 'great success!'
});
Here's my index.js setup code:
Ext.setup({
tabletStartupScreen: 'tablet_startup.png',
phoneStartupScreen: 'phone_startup.png',
icon: 'icon.png',
glossOnIcon: false,
onReady: function() {
Ext.locale = {};
var headID = document.getElementsByTagName("head")[0],
newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'en.js';
headID.appendChild(newScript);
loginPanel = new login.Panel();
}
});
login.Panel is an extension of the Sencha panel class using Ext.extend.
The 'en.js' script is added to the header correctly. I don't have it in the index.html file because once this problem is solved there will be several files that could be loaded, depending on the output of a function. That's why I need to add the script to the header in the onReady function, and not in the index.html file itself.
Once the script has been added it loads "variable: 'great success'" into Ext.locale,
Yet my problem currently lies within login.Panel(), which is an extension of the Sencha panel class using Ext.extend.
Currently, there is a button in the panel.
When I put this in the button's handler:
console.log(Ext.locale.variable)
it returns the string "great success",
yet when I try to set the button's text like this:
text:Ext.locale.variable,
I get the error
Uncaught TypeError: Cannot read property 'variable' of undefined
I'm guessing I have a scope issue here, since console.log() and alert() can both access Ext.locale, but trying to use it to construct the form gives me the undefined error.
Any help would be greatly appreciated.
Thank you!
it sounds like you are defining Ext.locale from your script early on... then later in onReady you are overwriting it as Ext.locale = {}
onReady will run after all your other scripts have been loaded.
Why not move your initialisation code for locale into onReady insted of your = {} line
This will add three properties and their values to the receiving object.
Ext.apply(receivingObject, {
property1: 'value1',
property2: 'value2',
property3: 'value3'
});
Here also is the Sencha documentation on the Ext.apply method:
http://dev.sencha.com/deploy/touch/docs/source/Ext.html#method-Ext-apply
As for accessing the isReady property, you could do something like if(someExtObj.isReady), but you may be more interested in using the onReady method...
Ext.setup({
onReady: function() {
// your setup code
}
});