I have a static html5 and css3 website, which I want to translate. Primary language is German, and I want to translate it to English and Russian.
I found a jQuery plugin (translate.js), which does exactly what I need but I have one issue with this plugin.
Everything works perfectly, when I use for example
HTML
<p class="trn">Herzlich Willkommen</p>
jQuery:
$(function() {
var t = {
"Herzlich Willkommen": {
en: "Welcome",
ru: "Добро пожаловать"
}
};
var _t = $('body').translate({
lang: "de",
t: t
});
var str = _t.g("translate");
console.log(str);
$(".lang_selector").click(function(ev) {
var lang = $(this).attr("data-value");
_t.lang(lang);
console.log(lang);
ev.preventDefault();
});
});
but when I want to use nested trn class inside parent trn class, unfortunately I can not translate it.
For example:
<h2 class="trn">Ihr <span class="trn">Bauunternehmen</span> in <span class="trn">Wien</span></h2>
Can you help me please?
Plugin documentation
jQuery.translate.js
(function($) {
$.fn.translate = function(options) {
var that = this; //a reference to ourselves
var settings = {
css: "trn",
lang: "en"
/*,
t: {
"translate": {
pt: "tradução",
br: "tradução"
}
}
*/
};
settings = $.extend(settings, options || {});
if (settings.css.lastIndexOf(".", 0) !== 0) //doesn't start with '.'
settings.css = "." + settings.css;
var t = settings.t;
//public methods
this.lang = function(l) {
if (l) {
settings.lang = l;
this.translate(settings); //translate everything
}
return settings.lang;
};
this.get = function(index) {
var res = index;
try {
res = t[index][settings.lang];
} catch (err) {
//not found, return index
return index;
}
if (res)
return res;
else
return index;
};
this.g = this.get;
//main
this.find(settings.css).each(function(i) {
var $this = $(this);
var trn_key = $this.attr("data-trn-key");
if (!trn_key) {
trn_key = $this.html();
$this.attr("data-trn-key", trn_key); //store key for next time
}
$this.html(that.get(trn_key));
});
return this;
};
})(jQuery);
Related
I'm trying to convert es5 to es6 using lebab.
lebab es5.js -o es6.js --transform class
if I convert simple code everything is fine. For example:
var className = function(values) { this.__values = values; };
className.func = function() { return "test"; };
className.func2 = function() { return "test2"; };
className.prototype = {
_values: null,
put: function() { return 0; },
get_length: function() { return this._values.length; }
};
To
class com_cie_json_JsonArray {
constructor(values) {
this._values = values;
}
static func1() {
return "test1";
}
static func2() {
return "test2";
}
put() {
return 0;
}
get_length() {
return this._values.length;
}
}
But if I insert more fields then the Lebab either partially or not translates these pieces at all. This example not translated code.
var className = function(values) {
this.__values = values;
};
className.func = function() {
return "test";
};
className.func2 = function() {
return "test2";
};
$classes[2] = $classes["name.to.className"] = className;
className.__name__ = ["name","to","className"];
className.prototype = {
_values: null,
put: function(value) {
return 0;
},
get_length: function() {
return this._values.length;
},
__class__: className,
__properties__: {get_length:"get_length"}
};
The question is how this type of code will look in ec6. And if it’s possible how to tell the Lebab to convert it.
var className = function() {
};
$classes[2] = $classes["name.to.className"] = className;
className.__name__ = ["name","to","className"];
className.prototype = {
_values: null,
__class__: className,
__properties__: {get_length:"get_length"}
};
$classes[2] = $classes["name.to.className"] = className;
className.__name__ = ["name","to","className"];
So I bought a template and hosting from ghost.org. Unfortunately, it uses handlebars, a language I'm not very familiar with. I figured it out, but then when I implemented this jquery file for adding related posts to the bottom of the page using tags created in handlebars, it did not work. When I looked over the file it seemed to be fine. So I'm wondering if any of you can find a bug or error with the prototype based programming. Credits to dane cando at github for the file, but it isnt working. Below are the javascript for the related posts function and the ul class from the handlebars file.
JavScript File
(function($) {
defaults = {
feed: '/rss',
titleClass: '.post-title',
tagsClass: '.post-meta',
limit: 5,
debug: true,
template: '<li>{title}</li>',
messages: {
noRelated: 'No related posts were found.'
}
};
function RelatedPosts(element, options) {
this.element = element;
this.options = $.extend({}, defaults, options);
this.parseRss();
};
RelatedPosts.prototype.displayRelated = function(posts) {
var self = this, count = 0;
this._currentPostTags = this.getCurrentPostTags(this.options.tagsClass);
var related = this.matchByTag(this._currentPostTags, posts), options = this.options;
related.forEach(function(post) {
var template = options.template.replace(/{[^{}]+}/g, function(key) {
return post[key.replace(/[{}]+/g, '')] || '';
});
if (count < self.options.limit) {
$(self.element).append($(template));
}
count++;
});
if (count == 0) {
$(this.element).append($('<li>' + this.messages.noRelated + '</li>'));
}
};
RelatedPosts.prototype.parseRss = function(pageNum, prevId, feeds) {
var page = pageNum || 1, prevId = prevId || '', feeds = feeds || [], self = this;
$.ajax({
url: this.options.feed + '/' + page,
type: 'GET'
})
.done(function(data, textStatus, xhr) {
var curId = $(data).find('item > guid').text();
if (curId != prevId) {
feeds.push(data);
self.parseRss(page + 1, curId, feeds);
}
else {
var posts = self.getPosts(feeds);
self.displayRelated(posts);
}
})
.fail(function(e) {
self.reportError(e);
});
};
RelatedPosts.prototype.getCurrentPostTitle = function(titleClass) {
if (titleClass[0] != '.') {
titleClass = '.' + titleClass;
}
var postTitle = $(titleClass).text();
if (postTitle.length < 1) {
this.reportError("Couldn't find the post title with class: " + titleClass);
}
return postTitle;
};
RelatedPosts.prototype.getCurrentPostTags = function(tagsClass) {
if (tagsClass[0] != '.') {
tagsClass = '.' + tagsClass;
}
var tags = [];
$(tagsClass + ' a').each(function() {
tags.push($(this).text());
});
if (tags.length < 1) {
this.reportError("Couldn't find any tags in this post");
}
return tags;
};
RelatedPosts.prototype.getPosts = function(feeds) {
var posts = [], items = [];
feeds.forEach(function(feed) {
items = $.merge(items, $(feed).find('item'));
});
for (var i = 0; i < items.length; i++) {
var item = $(items[i]);
if (item.find('title').text() !== this.getCurrentPostTitle(this.options.titleClass)) {
posts.push({
title: item.find('title').text(),
url: item.find('link').text(),
content: item.find('description').text(),
tags: $.map(item.find('category'), function(elem) {
return $(elem).text();
})
});
}
}
if (posts.length < 1) {
this.reportError("Couldn't find any posts in feed: " + feed);
}
return posts;
};
RelatedPosts.prototype.reportError = function(error) {
if (this.options.debug) {
$(this.element).append($('<li>' + error + '</li>'));
}
};
RelatedPosts.prototype.matchByTag = function(postTags, posts) {
var matches = [];
posts.forEach(function(post) {
var beenAdded = false;
post.tags.forEach(function(tag) {
postTags.forEach(function(postTag) {
if (postTag.toLowerCase() === tag.toLowerCase() && !beenAdded) {
matches.push(post);
beenAdded = true;
}
});
});
});
if (matches.length < 1) {
this.reportError("There are no closely related posts");
}
return matches;
};
$.fn.ghostRelated = function(options) {
return this.each(function() {
new RelatedPosts(this, options);
});
};
})(jQuery);
$('.related-posts').ghostRelated();
HTML List
<ul class="related-posts">
</ul>
I have a sealed object with an array member on which I want to prevent direct pushes.
var myModule = (function () {
"use strict";
var a = (function () {
var _b = {},
_c = _c = "",
_d = [];
Object.defineProperty(_b, "c", {
get: function () { return _c; }
});
Object.defineProperty(_b, "d", {
get { return _d; }
});
_b.addD = function (newD) {
_d.push(newD);
};
Object.seal(_b);
return _b;
}());
var _something = { B: _b };
return {
Something: _something,
AddD: _b.addD
};
}());
myModule.Something.c = "blah"; // doesn't update = WIN!!
myModule.AddD({}); // pushed = WIN!
myModule.Something.d.push({}); // pushed = sadness
How can I prevent the push?
UPDATE:
Thanks for all the thoughts. I eventually need the JSON to send to the server. It looks like I might need to use an object for the array then figure out a way to generate and return the JSON needed, or change _something to use .slice(). Will play and report.
you could override the push method:
var _d = [];
_d.__proto__.push = function() { return this.length; }
and when you need to use it in your module, call Array.prototype.push:
_b.addD = function (newD) {
Array.prototype.push.call(_d, newD);
};
I haven't done any performance tests on this, but this certainly helps to protect your array.
(function(undefined) {
var protectedArrays = [];
protectArray = function protectArray(arr) {
protectedArrays.push(arr);
return getPrivateUpdater(arr);
}
var isProtected = function(arr) {
return protectedArrays.indexOf(arr)>-1;
}
var getPrivateUpdater = function(arr) {
var ret = {};
Object.keys(funcBackups).forEach(function(funcName) {
ret[funcName] = funcBackups[funcName].bind(arr);
});
return ret;
}
var returnsNewArray = ['Array.prototype.splice'];
var returnsOriginalArray = ['Array.prototype.fill','Array.prototype.reverse','Array.prototype.copyWithin','Array.prototype.sort'];
var returnsLength = ['Array.prototype.push','Array.prototype.unshift'];
var returnsValue = ['Array.prototype.shift','Array.prototype.pop'];
var funcBackups = {};
overwriteFuncs(returnsNewArray, function() { return []; });
overwriteFuncs(returnsOriginalArray, function() { return this; });
overwriteFuncs(returnsLength, function() { return this.length; });
overwriteFuncs(returnsValue, function() { return undefined; });
function overwriteFuncs(funcs, ret) {
for(var i=0,c=funcs.length;i<c;i++)
{
var func = funcs[i];
var funcParts = func.split('.');
var obj = window;
for(var j=0,l=funcParts.length;j<l;j++)
{
(function() {
var part = funcParts[j];
if(j!=l-1) obj = obj[part];
else if(typeof obj[part] === "function")
{
var funcBk = obj[part];
funcBackups[funcBk.name] = funcBk;
obj[part] = renameFunction(funcBk.name, function() {
if(isProtected(this)) return ret.apply(this, arguments);
else return funcBk.apply(this,arguments);
});
}
})();
}
}
}
function renameFunction(name, fn) {
return (new Function("return function (call) { return function " + name +
" () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
};
})();
You would use it like so:
var myArr = [];
var myArrInterface = protectArray(myArr);
myArr.push(5); //Doesn't work, but returns length as expected
myArrInterface.push(5); //Works as normal
This way, you can internally keep a copy of the interface that isn't made global to allow your helper funcs to modify the array as normal, but any attempt to use .push .splice etc will fail, either directly, or using the .bind(myArr,arg) method.
It's still not completely watertight, but a pretty good protector. You could potentially use the Object.defineProperty method to generate protected properties for the first 900 indexes, but I'm not sure of the implications of this. There is also the method Object.preventExtensions() but I'm unaware of a way to undo this effect when you need to change it yourself
Thank you, dandavis!
I used the slice method:
var myModule = (function () {
"use strict";
var a = (function () {
var _b = {},
_c = _c = "",
_d = [];
Object.defineProperty(_b, "c", {
get: function () { return _c; }
});
Object.defineProperty(_b, "d", {
get { return _d.slice(); } // UPDATED
});
_b.updateC = function (newValue) {
_c = newValue;
};
_b.addD = function (newD) {
_d.push(newD);
};
Object.seal(_b);
return _b;
}());
var _something = { B: _b };
return {
Something: _something,
AddD: _b.addD
};
}());
myModule.Something.c = "blah"; // doesn't update = WIN!!
myModule.AddD({}); // pushed = WIN!
myModule.Something.d.push({}); // no more update = happiness
This allows me to protect from direct push calls enforcing some logic.
Begin with a fresh plain HTML document, and only use HTML and Javascript.
Place on it the hyperlinked word ''food''
Upon clicking ''food'', it should be replaced with ''meat and vegetables''
Upon clicking ''meat'', it should be replaced with ''pork with bacon''
Upon clicking ''vegetables'', it should be replaced with ''carrots plus peas''
Upon clicking ''pork'', it should be replaced with ''tough and chewy''
Upon clicking ''tough'', it should be replaced with ''burnt and salty''
(And so on)
I've been trying to do this as far as I can, but I'm having escapecode problems.
Here is my code:
<span id="food">food</span>
Here it is in action: http://jsfiddle.net/jshflynn/L6r5rrfx/
I'm sorry it's not spaced, but that threw up errors.
Notice that ''alert(2)'' has no delimiting characters around it, I don't know how to make it say alert(''Hello'').
I feel there must be some recursive way to do this, but I'm not sure.
Thanks in advance. Especially so if you can do the full problem.
Here you go, you get the idea: http://jsfiddle.net/8bhd8njh/
function bind(obj, evt, fnc) {
// W3C model
if (obj.addEventListener) {
obj.addEventListener(evt, fnc, !1);
return !0;
}
// Microsoft model
else if (obj.attachEvent) {
return obj.attachEvent('on' + evt, fnc);
}
// Browser don't support W3C or MSFT model, go on with traditional
else {
evt = 'on'+evt;
if(typeof obj[evt] === 'function'){
// Object already has a function on traditional
// Let's wrap it with our own function inside another function
fnc = (function(f1,f2){
return function(){
f1.apply(this,arguments);
f2.apply(this,arguments);
}
})(obj[evt], fnc);
}
obj[evt] = fnc;
return !0;
}
}
String.prototype.supplant = function (a, b) {
return this.replace(/{([^{}]*)}/g, function (c, d) {
return void 0!=a[d]?a[d]:b?'':c
})
};
var data = {
food : '{meat} and {vegetables}',
meat : '{pork} and {beef}',
pork : '{tough} and {chewy}',
tough : '{burnt} and {salty}',
vegetables : '{carrots} and {peas}'
};
var classname = 'game-clickable';
var init = function(obj, data) {
var template = '<span class="{classname}">{text}</span>';
obj.innerHTML = obj.innerHTML.replace(/{([^{}]*)}/g, function(a,b) {
return template.supplant({
classname : data[b] ? classname : '',
text : b
}, !0)
});
var objects = document.getElementsByClassName('game-clickable');
for (var i = 0; i < objects.length; i++) {
bind(objects[i], 'click', (function(o) {
return function() {
if (!data[o.innerHTML]) {
return;
}
var parent = o.parentNode;
var span = document.createElement('SPAN');
span.innerHTML = data[o.innerHTML];
parent.insertBefore(span, o);
parent.removeChild(o);
init(parent, data);
}
})(objects[i]));
}
};
init(document.getElementById('word-game'), data);
.game-clickable {
cursor: pointer;
text-decoration: underline;
}
<div id="word-game">
{food}
</div>
I think you are looking for something like the following:
var replacements = {
"food" : "meat and vegetables",
"meat" : "pork with bacon",
"vegetables" : "carrots plus peas",
"pork" : "tough and chewy",
"tough" : "burnt and salty"
};
function replaceAnchor(a) {
var elementType = "";
var lastElementType = "";
var target = a.innerHTML;
var replacement = replacements[target];
var words = replacement.split(' ');
var newElement = {};
for(var i = 0; i < words.length; i++) {
var word = words[i];
if (replacements[word]) {
elementType = "a";
} else {
elementType = "span";
}
if (elementType === "a" || elementType != lastElementType) {
newElement = document.createElement(elementType);
if (elementType === "a") {
newElement.onclick = function(e) {
replaceAnchor(this);
e.preventDefault();
};
}
a.parentNode.insertBefore(newElement, a);
}
if (elementType == "span") {
newElement.innerHTML = newElement.innerHTML + " " + word + " ";
} else {
newElement.innerHTML += word;
}
lastElementType = elementType;
}
a.parentElement.removeChild(a);
return false;
}
a {
text-decoration : underline;
color : blue;
cursor: pointer;
}
<a onclick="return replaceAnchor(this);">food</a>
How can I get the latest page data (HTML & Javascript varaibles) from PhantomJS
e.g page.refresh() or something?
I have an Interval, than checks a variable (on the page) every 200ms. However, this variable and the page content, isn't shown to have changed over time. (even though I know it has)
So I need an efficient way to check the value of a JS variable every 200ms or so,
then once I've discovered that variable has changed value, I want to request the latest page HTML.
How can I do this?
var Error = function (description) {
this.description = description;
return this;
};
var DTO = function (status, content, error) {
this.status = status;
this.content = content;
this.error = error;
return this;
};
function outputAndExit(dto) {
console.log(JSON.stringify(dto));
phantom.exit();
}
//For any uncaught exception, just log it out for .NET to capture
window.onerror = function (errorMsg, url, lineNumber) {
var description = 'window.onerror caught an error: ' +
'errorMsg: ' + errorMsg +
'url: ' + url +
'lineNumber: ' + lineNumber;
outputAndExit(new DTO(false, null, new Error(description)));
};
var GetDynamicPageResult__ = function () {
var obj = new GetDynamicPageResult();
obj.initialize();
return obj;
};
var GetDynamicPageResult = function () {
var self = this;
this.initialize = function () {
this.error = null;
this.isContentReadyForCrawler = false;
this.ticker = null;
this.tickerInterval = 150;
this.tickerElapsed = 0;
this.url = '';
this.loadDependencies();
this.processArgs();
this.openPage();
};
this.loadDependencies = function () {
this.system = require('system'),
this.page = require('webpage').create(),
this.page.injectJs('jquery-1.10.2.min');
this.fs = require('fs');
};
this.processArgs = function () {
if (this.system.args.length == 0) {
outputAndExit(new DTO(false, null, new Error('No arguments given')));
}
//system.args[0] Was the name of this script
this.url = this.system.args[1];
};
this.updateIsContentReadyForCrawler = function () {
var updateIsContentReadyForCrawler = self.page.evaluate(function () {
self.isContentReadyForCrawler = window.isContentReadyForCrawler;
});
};
this.openPage = function () {
self.page.open(this.url, function (status) { //NB: status = 'success' || 'fail'
if (status !== 'success') {
outputAndExit(new DTO(false, null, new Error('page.open received a non-success status')));
}
self.initTicker();
});
};
this.initTicker = function () {
this.ticker = setInterval(self.handleTick, self.tickerInterval);
};
this.handleTick = function () {
self.tickerElapsed += self.tickerInterval;
self.updateIsContentReadyForCrawler();
if (self.isContentReadyForCrawler) {
clearInterval(self.ticker);
var content = self.page.content;
self.finish(true, content, null);
} else {
var tooMuchTimeElapsed = self.tickerElapsed > 7000;
if (tooMuchTimeElapsed) {
clearInterval(self.ticker);
self.finish(false, null, new Error('Too much time elapsed'));
}
}
};
this.finish = function (status, content, error) {
content = content || '';
error = error || {};
outputAndExit(new DTO(status, content, error));
};
};
/**********************************************************************************/
/***************************** Helpers *****************************/
/**********************************************************************************/
var Utility__ = function () {
var obj = new Utility();
obj.initialize();
return obj;
};
var Utility = function () {
var self = this;
this.initialize = function () {
};
this.isEmpty = function (obj) {
var isEmpty = false;
(obj == undefined || obj == null) && (isEmpty = true);
return isEmpty;
};
this.isStringEmpty = function (str) {
var isEmpty = false;
isEmpty(str) && (isEmpty = true);
(isEmpty == false && $.trim(str) == '') && (isEmpty = true);
return isEmpty;
};
};
var getDynamicPageResult = new GetDynamicPageResult__();
I think you are almost there: you need to be using page.evaluate(), but currently only use it to get window.isContentReadyForCrawler. You need to use page.evaluate() to grab the latest HTML too.
I'm going to shamelessly paste in code from another answer (https://stackoverflow.com/a/12044474/841830):
var html = page.evaluate(function () {
var root = document.getElementsByTagName("html")[0];
var html = root ? root.outerHTML : document.body.innerHTML;
return html;
});