Translate JQuery Mobile widgets - javascript

Using i18next, how can I translate JQuery Mobile widgets? Specifically, I'd like to know how to do that without resorting to using data-i18n-target to modify generated inner elements, because that is brittle since future widget versions may change the generated code.
Is there a specific page lifecycle event I can subscribe to in order to be able to have i18next modify the DOM before the widget transformation happens?
In this example (see jsfiddle), some markup is correctly translated, but homeBtn and submitBtn are not:
HTML:
<div id="home_page" data-role="page">
<div data-role="header">
<a id="homeBtn" href="/" data-icon="home" data-i18n>app.home</a>
<h2 data-i18n>app.title</h2>
</div>
<div data-role="content">
<div data-role="fieldcontain">
<label for="textinput1" data-i18n>app.label</label>
<input type="text" name="textinput1" id="textinput1" value=""></input>
</div>
<form action="">
<input id="submitBtn" type="submit" data-i18n="[value]app.button" />
</form>
</div>
<div data-role="footer">
<center>
<p data-i18n>app.footer</p>
</center>
</div>
</div>
JavaScript:
var i18nOpts = {
resStore: {
dev: {
translation: {
app: {
button: 'Button',
home: 'Home',
label: 'Label',
footer: 'Footer',
title: 'i18n Test'
}
}
},
}
};
i18n.init(i18nOpts).done(function() {
$("html").i18n();
});

jQuery Mobile widgets are auto-initialized (Markup enhancement) on two events, pagebeforecreate and pagecreate. The majority of widgets are initialized once pagecreate occurs.
All you need is to wrap i18n code in pagebeforecreate.
$(document).on("pagebeforecreate", function () {
// code
});
Demo

Related

Colorbox breaking <form>

I'm using Colorbox.js for the first time and am having issues with placing a newsletter signup inside of the popup. I am assigning colorbox to my element .newsletter-popup and it is working correctly, however as mentioned the <form> within does not work. I have tested the form and it works correctly when not loading via colorbox?
Also, in the source code of my page I can see some automatically generated colorbox code #cboxOverlay and #colorbox, why do these appear when I've already assigned colorbox to my own element? I'm also not able to set options for colorbox on my element, as they don't work. Have a got this setup incorrectly?
Working Example (Appears after 3 seconds): http://development.ozonecoffee.co.uk/our-journal/
JS
setTimeout(function(){
jQuery('.newsletter-popup').colorbox({
trapFocus: false,
closeButton: true,
onComplete: function(){ $("#email").focus(); }
});
}, 3000);
// Close button
$( ".close-popup" ).click(function() {
$.colorbox.remove();
});
HTML
<div class="newsletter-popup">
<div class="modal">
<div class="close-popup"><span>X</span></div>
<div class="container">
<h4>Title</h4>
<form class="mailing-list-signup" id="subscribe-form" action="http://ozonecoffee.us14.list-manage.com/subscribe/post-json?u=93b92e23751a1d94fe6e57dc9&id=3caae1521b" method="get">
<input id="email" type="email" placeholder="Sign up for our newsletter" value="" name="EMAIL" >
<input type="submit" value="Sign up" name="subscribe" >
</form>
</div>
</div>
</div>

Open On button without access to JS File. Klaviyo. JQuery

I have an issue with js code. I'm trying to call a Pop-Up Box on click of the certain button and it's not working.
My aim is to assign an action to a certain button to open the pop-up when customer clicks on certain button. But the problem is that Klaviyo Pop-Up Box is set to open on page load by default settings are set in JS File that is located on Klaviyo side and i'm not able to change them directly.
Here is a problem similar to my, but not exactly what i was looking for.
Another option i was thinking about is to break the function that opens a pop-up onPageLoad and then to add this function to assign open action to a button.
This is how i thought i could call the pop-up onButtonClick.
My JS code
<script type="text/javascript" src="//www.klaviyo.com/media/js/public/klaviyo_subscribe.js"></script>
<script>
$('.button').on('click',function(){
KlaviyoSubscribe.attachToModalForm('#MainPage_PP');
});
</script>
or
<script type="text/javascript" src="//www.klaviyo.com/media/js/public/klaviyo_subscribe.js"></script>
<script>
$(':button').click(function() {
if (this.id == 'button') {
KlaviyoSubscribe.attachToModalForm('#MainPage_PP');
$('.button').popup('open');
}
</script>
Button Html Code
<input type="button" class="button" value="Input Button" />
My Pop-Up HTML Code
<div class="klaviyo_modal" id="MainPage_PP" style="display:none;">
<div class="klaviyo_inner">
×
<form action="//manage.kmail-lists.com/subscriptions/subscribe" method="POST" novalidate="novalidate" class="klaviyo_subscription_form">
<input type="hidden" name="g" value="LIST_ID">
<div class="klaviyo_fieldset">
<p class="klaviyo_header">Interested in our Newsletter?</p>
<p class="klaviyo_subheader">Stay in the know with news and promotions.</p>
</div>
<div class="klaviyo_fieldset">
<div class="klaviyo_field_group">
<label for="MainPage_PP_$email">Email Address</label>
<input type="email" id="MainPage_PP_$email" name="email"></div>
</div><div class="klaviyo_fine_print"></div>
<div class="klaviyo_form_actions">
<button type="submit" class="klaviyo_submit_button">
<span>Subscribe</span>
</button>
</div>
<div class="klaviyo_below_submit" ></div>
</form>
<div class="error_message" ></div>
<div class="success_message" ></div>
</div>
</div>
I assigned id="target" to the button to let the JS to make manipulations with it.
The important thing is that the Klaviyo JS file should be load after all actions to let the embedded button work properly.
HTML Code. Button
<input type="button" class="button" id="target" value="Input Button" />
JS Code
<script>
$( "#target" ).click(function() {
KlaviyoSubscribe.attachToModalForm('#MainPage_PP', {
//some var for Klaviyo
...
});
// It is essentially important to load the Klaviyo JS file after all actions
$.getScript("//www.klaviyo.com/media/js/public/klaviyo_subscribe.js")
});
</script>

Why is the content of my contenteditable div disappearing?

The code:
post_page.js:
Template.postPage.events({
// Updating a post
'submit form': function(e) {
e.preventDefault();
var post = {
title: $(e.target).find('[name=title]').val(),
content: $(e.target).find('[name=content]').val()
};
Posts.update(this._id, {$set: post});
},
'click .move-up': function(e) {
// Moving up a post
e.preventDefault();
var prevPost = Posts.findOne({position: this.position - 1});
if (prevPost) {
Posts.update(prevPost._id, {$set: {position: prevPost.position + 1}});
Posts.update(this._id, {$set: {position: this.position - 1}});
}
},
(...)
Template.postPage.rendered = function() {
// Plugin to turn the text area into a contenteditable div
var editor;
$(function() {
editor = new Simditor({
textarea: $("#input-content"),
placeholder: "...",
pasteImage: true,
toolbar: ["title", "bold", "italic", "underline", "link", "image", "indent", "outdent"],
upload: {
url: "/upload"
}
});
post_page.html:
<template name="postPage">
<div class="posts">
<div class="post">
<div class="post-content">
<span>title: {{title}}, position: {{position}}</span>
<a class="move-up" href="javascript:;">Move Up</a>
<a class="move-down" href="javascript:;">Move Down</a>
See Post
<a class="save-file" href="javascript:;">Save Post</a>
</div>
</div>
</div>
<form class="form" role="form">
<div class="form-group">
<label for="exampleInputEmail1">Title</label>
<input name="title" type="text" class="form-control" id="exampleInputEmail1" placeholder="Enter email" value="{{title}}">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Content</label>
<textarea name="content" class="form-control" rows="3">{{content}}</textarea>
</div>
<button type="submit" class="btn btn-default submit">Submit</button> </form> </template>
Is a single page app and everything works but for some reason the content of the contenteditable div disappears if I move up a post that isn't the one currently being displayed (the content in textarea remains and it comes back if I re-render the page):
What could be causing this? How to fix it?
Moving the post up/down causes the template to re-render completely and I suspect that because the simditor expects the DOM to persist, it either doesn't re-run on rendered or runs before Meteor's preserve-inputs package has repopulated the textarea.
You can try to work around the problem (until Meteor gets the Blaze templating engine up) by wrapping your re-rendering sensitive area (at least the textarea and possibly the complete DOM generated by simditor) in {{#constant}}. If you know the exact DOM elements you want to preserve, then you can also use preserve to prevent them from being re-rendered.

Popup using knockout js

i'm migrating one of my older jquery plugins from DOM jungle to this fancy mvvm framework knockout.
Which technique would i use to properly display a popup container? I ahve to populate it 'by call' since i get a json feed every time.
I tried an approach using the with binding, but it still attempts to populate the partial at its first runtime.
<!-- ko with: daySubmitFormViewModel -->
<div class="ec-consulation-lightbox">
<form id="cForm" class="form-container">
// Some bindings here.
</form>
</div>
<!-- /ko with: -->
It can be done without custom binding as well. Example is below
<div class="modalWindowBackground" data-bind="visible: popupDialog" >
<div class="modalWindow" data-bind="with:popupDialog">
<div class="content">
<h2 data-bind="text: title"></h2>
<p>
<span data-bind="text: message"></span>
</p>
<div class="buttonSpace">
<input type="button" class="closeButton" data-bind="value: closeButtonText, click: $root.hidePopupDialog" />
</div>
</div>
</div>
</div>
Viewmodel code:
self.showAlert = function (title, message, closeButtonText) {
self.popupDialog({ title: title, message: message, closeButtonText: closeButtonText });
};
self.hidePopupDialog = function () {
self.popupDialog(null);
};
//Code which opens a popup
self.remove = function () {
.... some code ...
if (someCondition) {
self.showAlert('SomeTitle', 'Message', 'OK');
return;
}
.... some code ...
};
Create a custom binding, have its open / close function trigger on a observable.
I've done a custom binding for jQuery Dialog that uses this approuch in combination with KO
templates.
<div id="dialog" data-bind="dialog: { autoOpen: false, modal: true, title: dialogTitle }, template: { name: 'dialog-template', data: dialogItem, 'if': dialogItem }, openDialog: dialogItem"></div>
You can find my binding here along with some others
https://github.com/AndersMalmgren/Knockout.Bindings
Live demo http://jsfiddle.net/H8xWY/102/
https://github.com/One-com/knockout-popupTemplate
That pretty much does what you ask for. It's deeply configurable, and under steady development (we use it in our web applications ourselves).
Disclaimer: I'm a One.com developer. I am also the person who originated the above mentioned lib.

Adding content to dijit Dialog

I've looked at quite a few links for people trying to add content / change the template for a Dojo Dialog. This was the most promising.
However, whenever I do something like this:
Dialog declared in HTML:
<div class="djDialog" id="dgViewer" data-dojo-type="TemplatedDialog" data-dojo-props="title: 'My Dialog', draggable:false"></div>
Dialog Template:
<div class="dijitDialog" role="dialog" aria-labelledby="${id}_title">
<div data-dojo-attach-point="titleBar" class="dijitDialogTitleBar">
<span data-dojo-attach-point="titleNode" class="dijitDialogTitle" id="${id}_title"></span>
<span data-dojo-attach-point="closeButtonNode" class="dijitDialogCloseIcon" data-dojo-attach-event="ondijitclick: onCancel" title="${buttonCancel}" role="button" tabIndex="-1">
<span data-dojo-attach-point="closeText" class="closeText" title="${buttonCancel}">x</span>
</span>
</div>
<!-- containerNode from original Dialog template -->
<div data-dojo-attach-point="containerNode" class="dijitDialogPaneContent">
<!-- All "custom" content -->
<div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'" style="height:300px">
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'">
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">
<button data-dojo-type="dijit.form.Button">Edit</button>
<button data-dojo-type="dijit.form.Button">Status</button>
</div>
</div>
<!-- End "custom" content -->
</div>
</div>
Custom Dialog class:
define([
'dojo/_base/declare',
'dijit/Dialog',
'dijit/_TemplatedMixin',
'dojo/text!./dialog_templates/View.html'],
function(
declare,
Dialog,
_Mixin,
_template){
return declare('TemplatedDialog', [Dialog, _Mixin], {
templateString : _template,
constructor : function(){
}
})
})
In my console (using Chrome) I just see:
<div data-dojo-attach-point="containerNode" class="dijitDialogPaneContent" style="width: auto; height: auto; "></div>
... and empty node where content should be.
So far I haven't found anyone who seems to have successfully extended dijit.Dialog in terms of templates. Is this possible?
Edit
After trying some variations on this template:
<div class="dijitDialog" role="dialog" aria-labelledby="${id}_title">
<div data-dojo-attach-point="titleBar" class="dijitDialogTitleBar">
<span data-dojo-attach-point="titleNode" class="dijitDialogTitle" id="${id}_title"></span>
<span data-dojo-attach-point="closeButtonNode" class="dijitDialogCloseIcon" data-dojo-attach-event="ondijitclick: onCancel" title="${buttonCancel}" role="button" tabIndex="-1">
<span data-dojo-attach-point="closeText" class="closeText" title="${buttonCancel}">x</span>
</span>
</div>
<!-- containerNode from original Dialog template -->
<div class="dijitDialogPaneContent">
<div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'" style="height:100%;width:100%">
<div data-dojo-attach-point="containerNode" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'"></div>
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'">
<button data-dojo-type="dijit.form.Button">Edit</button>
</div>
</div>
</div>
The error Uncaught TypeError: Cannot read property '0' of undefined is being thrown. This is the stack, if it helps. I'm using the uncompressed version from the Google CDN to help in debugging.
_childElements dojo.js.uncompressed.js:8341
getStepQueryFunc dojo.js.uncompressed.js:8597
query dojo.js.uncompressed.js:9005
query dojo.js.uncompressed.js:5248
_2._checkIfSingleChild _ContentPaneResizeMixin.js:2
_4._size Dialog.js:2
_4.show Dialog.js:2
Removing the data-dojo-type and -props from the containerNode resolves this, but it's still not getting things closer to having a working custom templated Dialog.
Reason for troubles with doing templating on a contentpane is, that whatever template-contents you put into the domNode referenced with the attach-point 'containerNode', you will loose on startup.
If there is no 'href' nor 'content' attributes set, they will simply be set to an empty string, thus leaving the Dialog.containerNode.innerHTML == ""
You have no need for deriving from _TemplatedMixin as the Dialog itself is a templated widget. Instead change this to _WidgetsInTemplateMixin to compensate for the BorderContainer layout widgets and your dijit.form contents. Also, your markup within the custom template should be pre-required, so you could go with something like this here:
Change the template from old attachpoint for container to this
<div data-dojo-attach-point="containerNode"
data-dojo-type="dijit.layout.ContentPane"
data-dojo-props="region:'center'">
Then add requirements to your markup widgets in the template plus the _WidgetsInTemplateMixin:
define(["dijit/Dialog",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!./dialog_templates/View.html",
// rest are for rendering example
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button"
}. ... );
Result should ressemble this, keeping your template View.html change in mind:
define([
'dojo/_base/declare',
'dijit/Dialog',
"dijit/_WidgetsInTemplateMixin",
"dojo/text!./dialog_templates/View.html",
// rest are for rendering example
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button"],
function(
declare,
Dialog,
_Mixin,
_template){
return declare('TemplatedDialog', [Dialog, _Mixin /*careful, widgetsintemplate is tricky*/ ], {
templateString : _template
})
})
You can fiddle here
EDIT:
As there is troubles with dialogs containing borderlayouts (its not unheard of anyways) here's a workaround:
_checkIfSingleChild: function() {
delete this._singleChild;
domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
},
templateString: '....'
Im not certain of the consequences, im thinking the borderlayout of yours might start to misbehave if you try to programmatically change its contents and dimensions.. But it will render - at least it does here: updated fiddle

Categories