Windows Phone App in JavaScript with AngularJS - javascript

I am developing a Windows Phone Application in JavaScript. I am using the AngularJS library. The problem is that I cannot add a dynamic content because of security reasons.
The error I get: HTML1701: Unable to add dynamic content '<div id="view_login" class="view"> <div id="view_login_container"> <img class="logo" src="http://oi60.tinypic.com/okwifa.jpg"> <input type="text" placeholder="Username" ng-model="loginUsername"> <input type="password" placeholder="******" ng-model="loginPassword"> <button ng-click="doLogin()">Login</button> <button ng-click="changeView('/signup')" class="link">... or sign up now</button> </div> </div>'. A script attempted to inject dynamic content, or elements previously modified dynamically, that might be unsafe. For example, using the innerHTML property to add script or malformed HTML will generate this exception. Use the toStaticHTML method to filter dynamic content, or explicitly create elements and attributes with a method such as createElement. For more information, see http://go.microsoft.com/fwlink/?LinkID=247104.
I changed one line in AngularJS library which should fix the problem:
append:function(a,c){
**MSApp.execUnsafeLocalFunction(function () {**
r(new N(c),function(c){
1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)
})
});
}
Unfortunately it did not work.
I spent several hours trying to find a solution, but I did not manage it. I would appreciate any suggestions how to make working the Windows Phone App written in JavaScript with AngularJS.

Microsoft Open Technologies recently released a shim which will prevent this exact problem for Windows Store apps using AngularJS, as well as many other popular JavaScript libraries.
Simply download the JavaScript Dynamic Content shim off of GitHub, then reference the file towards the beginning of your app before any other scripts are run. You should no longer see a dynamic content error.
Let me know if this solves your problem!

I encountered this issue when using Angular in a Windows Store App. The solution I came up with was to monkey patch the DOM manipulation functions that were unsafe, rather than having to hack up Angular or jQuery because I still wanted to be able to update using bower.
var patch = {
methods: [
'appendNode',
'cloneNode',
'insertBefore',
'removeChild',
'replaceChild'
],
properties: [
'innerHTML',
'outerHTML'
]
};
patch.methods.forEach(function (name) {
proxyUnsafeMethod(HTMLElement.prototype, name);
});
patch.properties.forEach(function (name) {
proxyUnsafeProperty(HTMLElement.prototype, name);
});
function proxyUnsafeMethod(object, name) {
var _unsafe = object[name];
object[name] = function () {
var context = this;
var args = arguments;
return MSApp.execUnsafeLocalFunction(function () {
return _unsafe.apply(context, args);
});
};
}
function proxyUnsafeProperty(object, prop) {
var descriptor = Object.getOwnPropertyDescriptor(object, prop);
proxyUnsafeMethod(descriptor, 'set');
Object.defineProperty(object, prop, descriptor);
}

Angular dynamically puts HTML comment tags <!-- --> for ng-repeat and other directives. Unfortunately, Microsoft considers these to be unsafe when put in from javascript using element.innerHTML, and thus is not allowed.
The workaround is to modify the actual angular.js file and wrap all element.innerHTML calls in MSApp.execUnsafeLocalFunction();
In the version of Angular that I'm using, this is line 2539 and line 2162
Line 2539:
MSApp.execUnsafeLocalFunction(function() { element.innerHTML = value; });
Line 2162:
MSApp.execUnsafeLocalFunction(function() { div.innerHTML = '<div> </div>' + element });
The best method would be to search the angular.js file for all instances of innerHTML and wrap it.

In a lot of cases where you run into issues with dynamic content Winstore-jsContrib might help. Simply add the .js file at the beginning of your app and you're good to go.

Related

Running <script> tags within <template> on vue js

I've run into a problem whilst adding a Iframe that a service I am using has given me. They come in tags. I am using Vue 3 as a framework so when I insert them into the I get an error.
I've tried searching for a solution but the only thing that I've been able to find is this, Ads with script tags in template [Vue.js]. However it is from 2017 and even the solutions posted on there are outdated and do not work.
<script src="//widget.xxxxxx.it/v2/widget/widget.js"></script>
<script>var widget = new SimplybookWidget({"widget_type":"iframe","url":"https:\/\/xxxxxxx.simplybook.it","theme":"dainty","theme_settings":{"timeline_show_end_time":"1","timeline_hide_unavailable":"1","hide_past_days":"0","sb_base_color":"#13445b","secondary_color":"#e4ebf5","sb_text_color":"#13445b","display_item_mode":"block","body_bg_color":"#fefbe9","sb_background_image":"","sb_review_image":"","dark_font_color":"#13445b","light_font_color":"#ffffff","sb_company_label_color":"#ffffff","sb_cancellation_color":"#ff7a93","hide_img_mode":"0"},"timeline":"modern","datepicker":"top_calendar","is_rtl":false,"app_config":{"allow_switch_to_ada":0,"predefined":[]}});</script>
The error...
VueCompilerError: Tags with side effect (<script> and <style>) are
ignored in client component templates.
In Vue it generally is not possible to use script-tags within the template-tags.
Instead of using script-tags in HTML, use mounted() to add your JavaScript code to Vue's JavaScript section.
You can find extensive examples here.
If there is no html elements accompanied you can move this to your own script section e.g.
This may not be required and you can do it another way, e.g. just download it and load it yourself.
npm install require-from-url
var requireFromUrl = require('require-from-url/sync');
export default {
data() {
return {
widget: null,
};
},
mounted() {
var SimplybookWidget = requireFromUrl("//widget.xxxxxx.it/v2/widget/widget.js");
this.widget = new SimplybookWidget({"widget_type":"iframe","url":"https:\/\/xxxxxxx.simplybook.it","theme":"dainty","theme_settings":{"timeline_show_end_time":"1","timeline_hide_unavailable":"1","hide_past_days":"0","sb_base_color":"#13445b","secondary_color":"#e4ebf5","sb_text_color":"#13445b","display_item_mode":"block","body_bg_color":"#fefbe9","sb_background_image":"","sb_review_image":"","dark_font_color":"#13445b","light_font_color":"#ffffff","sb_company_label_color":"#ffffff","sb_cancellation_color":"#ff7a93","hide_img_mode":"0"},"timeline":"modern","datepicker":"top_calendar","is_rtl":false,"app_config":{"allow_switch_to_ada":0,"predefined":[]}});
},
destroyed() {
this.widget = null;
},
}

Check if AngularJS module is bootstrapped

I have an iframe with ASP.NET application, that contains UpdatePanel. I started using Angular inside the application, but things didn't work because of the .NET postbacks.
To solve this, I used this solution:
with (Sys.WebForms.PageRequestManager.getInstance()) {
add_endRequest(onEndRequest); // regester to the end Request
}
function onEndRequest(sender, args) {
angular.bootstrap($('#mainDiv'), ['defaultApp']);
var rootscope = angular.element('#mainDiv').scope();
if (rootscope) {
rootscope.$apply();
}
}
And it works great.
The problem is that when I dynamically load a different user control in the ASP.NET page, with another ng-controller, Angular throws an error saying the app is already loaded:
App Already Bootstrapped with this Element
So the question is: How can I check if the app is already bootstrapped? Can I reload this module? Can I remove it from the element and than bootstrap it again?
Thanks.
It's not good practice to access scope from outside the app, so it's not enabled in well-built production applications. If you need to access/apply scope then there's something strange/unsupported about your use case.
However, the right way to check whether an element has been bootstrapped is the way the Angular library does it which is to load up the element and check for an injector. So you'd want angular.element(document.querySelector('#mainDiv')).injector(); which makes your code:
function onEndRequest(sender, args) {
var element = angular.element(document.querySelector('#mainDiv'));
//This will be truthy if initialized and falsey otherwise.
var isInitialized = element.injector();
if (!isInitialized) {
angular.bootstrap(element, ['defaultApp']);
}
// Can't get at scope, and you shouldn't be doing so anyway
}
Can you tell us why you need to apply the scope?
You could simply check for the scope of mainDiv, if angular.element(document.querySelector('#mainDiv')).scope() is not undefined then that means angular has been not initialized yet.
You code will be like below.
CODE
function onEndRequest(sender, args) {
//below flag will be undefined if app has not bootsrap by angular.
var doesAppInitialized = angular.element(document.querySelector('#mainDiv')).scope();
if (angular.isUndefined(doesAppInitialized)) //if it is not
angular.bootstrap($('#mainDiv'), ['defaultApp']);
var rootscope = angular.element('#mainDiv').scope();
if (rootscope) {
rootscope.$apply(); //I don't know why you are applying a scope.this may cause an issue
}
}
Update
After angular 1.3+ release in later Aug 2015, there it added performance related improvement by disabling debugging information by disabling debug info. So normally we should enable debuginfo option to false to have good performance improvement on Production environment. I don't wanted to write too much about it as its already covered by #AdamMcCormick answer, which is really cool.

How use getSelection?

How use getSelection?
It does not return the selected text:
function pageContextMenu (event) {
var window = require("sdk/window/utils").getMostRecentBrowserWindow();
var stringSelection = window.getSelection();
console.log(stringSelection.toString());
}
window.document.getElementById("contentAreaContextMenu").addEventListener("popupshowing", pageContextMenu);
You're mixing up content script code and backend/add-on code. Your main.js (backend/add-on) file has access to the SDK modules, your content scripts have access to the DOM (web page/document). If you want to use the DOM api (as you're doing with getSelection and getElementById) you must do so from the content script side. See this part of the guide to understand the distinction conceptually. Read these two tutorials to implement.
If you just want to access the selection from main.js and don't need any other DOM functions, then you'll have to do as #ZER0 suggested and use the sdk/selection module

JavaScript template library that doesn't use eval/new Function

Google Chrome extensions using manifest_version: 2 are restricted from using eval or new Function. All of the JavaScript templating libraries I checked (mustachejs, underscorejs, jQuery template, hoganjs, etc) use new Function. Are there any that are fairly mature and supported that don't use either?
Info about the security restrictions.
It turns out that mustachejs added new Function recently and using tag 0.4.2 doesn't have it. It the API is slightly different with Mustache.to_html instead of Mustache.render and there are likely some performance reduction.
I opened an issue to potentially get new Function removed in a future release.
It doesn't appear that Pure uses either eval or new Function.
The answers here are outdated so I post an update.
Since September, Google changed their policy and allowed unsafe-eval in manifest 2 extensions. See this thread and this page.
So libraries using eval(), new Function() etc. can be used if unsafe-eval is turned on for your extensions.
Closure Templates is a templating library that does not use eval. Templates are compiled to JavaScript ahead of time, so that what gets included in your app is a plain .js file that should not run into CSP issues.
Distal template doesn't use eval.
It really depends on what you mean by "template library". If you just want string interpolation, there's no need for eval or new Function, when you start needing embedded looping structures, things get more complicated.
A few months ago I wrote a String.prototype.tmpl.js script that I've used a couple times here and there in places where I don't mind overriding String.prototype. As a static function, you can use:
tmpl.js:
function tmpl(tmpl, o) {
return tmpl.replace(/<%=(?:"([^"]*)"|(.*?))%>/g, function (item, qparam, param) {
return o[qparam] || o[param];
});
}
An example template:
<div id="bar"></div>
<script type="text/x-tmpl" id="foo">
<h1><%=title%></h1>
<p><%=body%></p>
</script>
<script>
(function () {
var foo,
bar;
foo = document.getElementById('foo');
bar = document.getElementById('bar');
bar.innerHTML = tmpl(foo.innerHTML, {
title: 'foo bar baz',
body: 'lorem ipsum dolor sit amet'
});
}());
</script>
The base tmpl script can of course be modified to take advantage of document fragments to actually build out DOM elements, but as-is I'm not sure whether it counts as a "template library".
The best solution to this problem is to pre-compile your templates before you deploy your extension. Both handlebarsjs and eco offer pre-compilation as a feature. I actually wrote a blog post that goes into more depth.
Maybe you can write a function eval1:
function eval1(blah) {
var s = document.createElement("script");
s.src = blah;
document.head.appendChild(s);
document.head.removeChild(s);
}
and do a find/replace in the library you want, but that'd be cheating, right?
I recently run into the same problem. After updating manifest version my extension stopped working. I tried Mustache but it unable to render index of the array and names of the object properties. So I had to create my own simple but effective templating library Ashe which is free of eval and new Function. Hope it will help someone.
https://developer.chrome.com/extensions/sandboxingEval
Not sure when it was added, but you can do Firefox style sandboxing in Chrome now. I'm porting my Firefox extension, so I need this (since I don't have evalInSandbox :P)

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

Categories

Resources