I have many slides for a presentation made with RemarkJS. It is a HTML file slides_fr.html with a single <textarea id="source"> containing the actual content in Markdown syntax (+ one or two specific markup tags to separate the slides with a page break), and one call to the JS library RemarkJS.
I am translating this document into English (I first duplicated slides_fr.html into slides_en.html and started to translate). Problem: each time I do improvement on the slides in the English version, I'll have to remodify the original file slides_fr.html to keep them in sync. In my experience, this rarely works well on the long-term. It would be better to have both versions in the same file, with markup for language.
Question: in order to avoid having two files slides_fr.html and slides_en.html like this that will ultimately never stay in sync:
<html>
<head></head>
<body>
<textarea id="source">
First slide
My presentation about XYZ
---
Second slide
Hello world
</textarea>
<script src="https://remarkjs.com/downloads/remark-latest.min.js"></script><script>remark.create();</script>
</html>
which options are there, using HTML or Javascript or Markdown-specific syntax to have both languages in the same file like this:
<textarea id="source">
First slide ||| Première diapositive
My presentation about XYZ ||| Ma présentation à propos de XYZ
---
Second slide ||| Seconde diapositive
Hello ! ||| Bonjour
</textarea>
<javascript>
chooseLanguage(document.getElementBydId('source'), 'en'); // there is surely a better solution
// than a parsing and splitting by '|||' ?
</javascript>
As a way to better organize localized texts, you could use CSS classes to mark which language applies to each text.
Remark provides a markdown extension called "Content classes" (https://remarkjs.com/#12), it's used to apply CSS classes to texts.
I think this feature could be exploited to wrap localized texts inside the markdown source, in this fashion:
.lang_en[Second slide]
.lang_fr[Seconde diapositive]
.lang_it[Seconda diapositiva]
These will be transcripted in HTML as:
<span class="lang_en">Second slide</span>
<span class="lang_fr">Seconde diapositive</span>
<span class="lang_it">Seconda diapositiva</span>
Once texts are structured this way, you can easily show / hide them via javascript and CSS.
This fiddle shows the Remark boilerplate localized in english and italian, adapted using the above strategy (javascript language switcher not provided in the snippet): https://jsfiddle.net/k7au5oe3/
Attached are the pseudo-codes for an attempt to make a working prototype of a Linear Progress component.
HTML
<script src="https://unpkg.com/material-components-web#0.42.1/dist/material-components-web.min.js"></script>
...
<div role="progressbar" class="mdc-linear-progress" id="my-progress-bar">
<div class="mdc-linear-progress__buffering-dots"></div>
<div class="mdc-linear-progress__buffer"></div>
<div class="mdc-linear-progress__bar mdc-linear-progress__primary-bar">
<span class="mdc-linear-progress__bar-inner"></span>
</div>
<div class="mdc-linear-progress__bar mdc-linear-progress__secondary-bar">
<span class="mdc-linear-progress__bar-inner"></span>
</div>
</div>
JavaScript
const overallProgress = mdc.linearProgress.MDCLinearProgress.attachTo(document.getElementById('my-progress-bar'));
overallProgress.setProgress(0.5);
The aforementioned codes are intended to show a 50% progress. The prototype is not functional. Which part of it could have gone wrong? The below references are the best that I can get from the official reference documents.
References
Linear Progress, Material Design for Web
Linear Progress Demo, Material Design for Web
TL;DR
Simply replace the JavaScript line overallProgress.setProgress(0.5);
into overallProgress.progress=0.5;
Details
I dig the source-code of MDCLinearProgress and it turns out it was implemented to using JavaScript function setters. The way function-setters work is by declaring in the class as methods but to actually set the value is by treating it like a property.
So instead of using setProgress(value), replace it with progress=value.
My webstore uses Kudobuzz for product reviews, but our e-commerce platform (PDG) isn't supported for SEO markup data.
This widget does not support schema markup on it's own, so I want to somehow select the relevant pieces and inject the schema markup to the various divs/spans that make up the widget. One problem is figuring out how to inject code that google can parse, and another is figuring out how to make the actual selectors for this super bloated widget.
Here is a codepin of the widget and some markup data that is already on the site: http://codepen.io/anon/pen/GpddpO
Here is a link to a product page if you want to see how everything works: https://www.asseenontvhot10.com/product/2835/Professional-Leather--Vinyl-Repair-Kit
This is (roughly) the markup I'm trying to add if it helps:
<div itemscope itemtype="http://schema.org/Review">
<div itemprop="reviewBody">Blah Blah it works 5 star</div>
<div itemprop="author" itemscope itemtype="http://schema.org/Person">
Written by: <span itemprop="name">Author</span></div>
<div itemprop="itemReviewed" itemscope itemtype="http://schema.org/Thing">
<span itemprop="name">Stop Snore</span></div>
<div><meta itemprop="datePublished" content="2015-10-07">Date published: 10/07/2015</div>
<div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
<meta itemprop="worstRating" content="1"><span itemprop="ratingValue">5</span> / <span itemprop="bestRating">5</span> stars</div>
</div>
Theoretically you could write a very small amount of microdata using css :before and :after - with content but it would need all spaces and symbols converted into ISO format, eg.
#name:before { "\003cspan\2002itemprop\0022name\2033"}
#name:after { content: "\2044\003cspan003e"
even spaces need to be substitued with \2002 or an equivalent whitespace
code
should wrap this microdata to your HTML to any element called name:
<span itemprop="name">...</span>
Clearly this can only work if the widget lets you have clear ids or class names for the elements added, and it may be useless you know the type of object reviewed first (eg Book, Movie, since this needs to go at the start in the example I gave - which is incomplete). The code would need to be nested correctly so if you want further help can you edit your question with example HTML for a completed review.
Writing your own JSON-LD script at the top of the page is another option - it would be a different question (if you get stuck) but isn't embedded within the data itself
Edit
it's a good idea to test the css in a separate environment first, eg setup a jsfiddle
I am trying to make dynamic code examples for our api that can be constructed from from input html elements.
A paired down example looks like this, I give the user an input to name the device they would like to create.
<input class="observable-input" data-key="deviceName" type="text" value="deviceKey" />
I would then like that input to update code examples (replacing the device name in the example with the one the user inputs).
<code lang="python">
device = { "name": "<span data-observeKey="deviceName">Name</span>" }
client.createDevicewrite(device)
</code>
I have all of the code setup for observing a change in the input and updating the code examples, this works great. All of the syntax highlighters I have looked at, usually chop the snippet up and rerender the example wrapped with its own html (for styling). Is there an option/configurable way to get a syntax highlighter to not strip the these tags, or is there a different approach I should be looking at for preserving the syntax highlighting and still supporting dynamic updates without having to do a full text search of each snippet's rendered tags.
The example output of the pygment (current syntax highlighter I'm using).
<li>
<div class="line">
<span class="n">device</span>
<span class="o">=</span>
<span class="n">{</span>
<span class="s">"name"</span>
<span class="p">:</span>
<span class="s">"Name"</span>
<span class="n">}</span>
</div>
</li>
I decided to just go with a brute force approach, it ended up being decently performant, ill leave my code here if anyone is interested in what I did
https://gist.github.com/selecsosi/5d41dae843b9dea4888f
Since i use backbone, lodash, and jquery as my base app frameworks the gist uses those. I have a manager which will push updates from inputs to spans on the page which I use to dynamically update the code examples
I need to keep two separate controllers, one for a search area and one to display data. When a search is performed I need to fetch data from the internet and update the view with the new data. I don't want to wrap everything with a bigger controller or something like that (is this best practice?) but I'd prefer the search and display controllers to remain completely separate.
This is the html code relative to the search field and display grid
<div id="mainContainer" class="container-fluid" >
<div id="searchContainer">
<div id="search" ng-controller="SearchController as srcCtrl">
<!-- on submit call submit(title) -->
<form class="navbar-form" role="search">
<div class="input-group">
<input ng-model="title" type="text" class="form-control" placeholder="Search" name="srch-term" id="srch-term">
<div class="input-group-btn">
<button class="btn btn-default" ng-click="srcCtrl(title)"><i class="glyphicon glyphicon-search"></i></button>
</div>
</div>
</form>
</div>
</div>
<div id="animeContainer" class="container-fluid">
<div class="clearfix"></div>
<!-- animeContainer directive -->
<anime-container></anime-container>
</div>
</div>
And the Angular code:
(function(){
var app = angular.module('hummingbird', []);
app.controller('AnimeListController', function(){
this.anime = animeList; //list of anime to display in anime list
this.onSelectAnime = function(anime){
console.log(anime);
};
});
app.controller('SearchController', function(){
this.submit = function(srcstr){
//here I call the API and then I want to update the anime variable in AnimeListController
console.log(srcstr);
}
});
app.directive("animeContainer", function(){
return{
restrict: 'E',
templateUrl: 'anime-container.html',
}
});
var animeList = [{"id":25,"slug":"ghost-in-the-shell","status":"Finished Airing","url":"https://hummingbird.me/anime/ghost-in-the-shell","title":"Ghost in the Shell","alternate_title":null,"episode_count":1,"cover_image":"https://static.hummingbird.me/anime/poster_images/000/000/025/large/25.jpg?1408440508","synopsis":"In the year 2029, the barriers of our world have been broken down by the net and by cybernetics, but this brings new vulnerability to humans in the form of brain-hacking. When a highly-wanted hacker known as 'The Puppetmaster' begins involving them in politics, Section 9, a group of cybernetically enhanced cops, are called in to investigate and stop the Puppetmaster. The pursuit will call into question what makes a human and what is the Puppetmaster in a world where the distinction between human and machine is increasingly blurry.\n(Source: ANN)","show_type":"Movie"},{"id":1411,"slug":"ghost-hunt","status":"Finished Airing","url":"https://hummingbird.me/anime/ghost-hunt","title":"Ghost Hunt","alternate_title":"","episode_count":25,"cover_image":"https://static.hummingbird.me/anime/poster_images/000/001/411/large/1411.jpg?1408443952","synopsis":"Telling ghost stories is a favorite past time of Mai Taniyama and her friends—that is, until she meets 17-year-old Kazuya Shibuya, the man sent by Shibuya Psychic Research Center to investigate paranormal activity at a supposedly haunted school. When Mai gets caught in a dangerous situation, she is rescued by Kazuya's assistant. Saving her lands the assistant incapacitated, and Kazuya demands that Mai become his assistant, instead. (Source: ANN)","show_type":"TV"},{"id":4135,"slug":"seven-ghost","status":"Finished Airing","url":"https://hummingbird.me/anime/seven-ghost","title":"07-Ghost","alternate_title":"","episode_count":25,"cover_image":"https://static.hummingbird.me/anime/poster_images/000/004/135/large/4135.jpg?1408451649","synopsis":"Set in a gothic fantasy world, this is the story of Teito Klein, an orphaned slave who became the top military academy student. However, an unexpected turn of events left him pursued by the forces of the Barsburg Empire. Now an escaping convict, Teito's sheltered by the church and its law of sanctuary. Here, he discovered many mysteries surrounding himself, the church, and the Empire itself. The fact that he might be connected to a dethroned king and the mystical stone of god, \"The Eye of Mikhael\", made him the target of the empire more than ever. Fortunately the church is under the mythical 7 Ghost protection. But who are the Ghosts really. Will Teito be free from the military's clutch, and what of his said mission to uncover the history. And who is the military's Chief-of-Staff Ayanami exactly. Teito's future seems to have spiraled into an unexpectedly perilous path.\r\n[Source: ANN]","show_type":"TV"},{"id":6391,"slug":"ghost-messenger","status":"Currently Airing","url":"https://hummingbird.me/anime/ghost-messenger","title":"Ghost Messenger","alternate_title":null,"episode_count":6,"cover_image":"https://static.hummingbird.me/anime/poster_images/000/006/391/large/6391.jpg?1408458272","synopsis":"Ghost Messengers are super-power agents from the underground world called the World of Death. The World of Death is a digitalized world with cutting-edge technologies that control and manage the life and death of all living things based on its elaborate systems.\r\nOur Ghost Messenger hero, Kang-lim, has been dispatched to the human world to capture the remaining ghosts who are refusing to go to the World of Death although it is their time.\r\nAn accident occurs during his mission and Kang-lim gets captured in his own mobile phone. Little Kang-lim, a human boy who has extraordinary spiritual powers, finds the mobile phone and takes GhostMessenger Kang-lim out from the mobile phone.\r\nAnd the adventure begins.\r\n(Source: Anime World Network)","show_type":"OVA"},{"id":2360,"slug":"shinreigari-ghost-hound","status":"Finished Airing","url":"https://hummingbird.me/anime/shinreigari-ghost-hound","title":"Shinreigari: Ghost Hound","alternate_title":null,"episode_count":22,"cover_image":"https://static.hummingbird.me/anime/poster_images/000/002/360/large/2360.jpg?1408446509","synopsis":"In an isolated region of Kyushu lies the town of Suiten. Though seeming small and modest, Suiten is not a picturesque place for a vacation, unless it is from the “Unseen Worldâ€. Taro, Makoto and Masayuki, three boys with traumatic pasts, learn to let their souls cross between the two parallel worlds. However, the Unseen World is no mere copy of the real Apparent World. The Unseen World is the home of ghosts, but changes are now allowing the souls of the dead to pass over into the Apparent World, with unpredictable effects. Follow the journey of Taro, Makoto and Masayuki, as they cross between the two worlds, trying to unravel a great mystery. \n(Source: Sentai Filmworks)","show_type":"TV"}];
})();
anime-container-html
<div class="col-md-3 col-xs-6" ng-repeat="anime in animeListCtrl.anime">
<!-- anime in anime list-->
<div class="panel panel-default fixed-height highlight" ng-click="animeListCtrl.onSelectAnime(anime)">
<div class="panel-body">
<img class="img-rounded img-responsive center-block" ng-src="{{anime.cover_image}}"/>
<h3> {{anime.title}} </h3>
</div>
</div>
</div>
I'm sure there's a pretty simple solution but I only find something that seems using old versions of AngularJS (I'm using 1.2.26).
Thanks everybody!
P.S. any other tip or advice about best practices are welcome!
What you want to do is add a service that you can then inject into your other controllers/directives. In your example, I imagine something like SearchService would work. In your submit function, rather than invoking the API directly have it call into the SearchService to execute the search. The SearchService would then maintain and expose the result set. Any controller and/or directive interested in displaying the results could bind to SearchService.resultSet, for instance.
Remember that Angular services are singleton objects. So if you intend on having more than a single search form/result object you may need to enhance your service to store named searches. However, based on the code you've shown, it looks like a single resultset would suffice. You could, optionally, decorate the ctrl,directive,service as part of an anime-specific module. So instead of SearchService call it AnimeSearchService.
app.factory('AnimeSearchService', function($http) {
var service = { resultSet: {} };
service.executeSearch = function(args) {
... some API calls ...
// when they return results store them as service.resultSet
}
return service;
});
Then in your controller
app.controller('AnimeSearchController', function(AnimeSearchService){
this.submit = function(srcstr){
//here I call the API and then I want to update the anime variable in AnimeListController
AnimeSearchService.executeSearch(srcstr);
console.log(srcstr);
}
});
app.controller('AnimeListController', function(AnimeSearchService){
this.anime = AnimeSearchService.resultSet; //expose the service to the template (or optionally create a $watch to update)
this.onSelectAnime = function(anime){
console.log(anime);
};
});
UPDATE: Your issue most likely comes down to angular bindings. If you are binding to properties of the service's resultSet object then those bindings will update as those properties change. However, if you are assigning the value of a property to a new property in your own controller/scope then you will have to watch the service and update your controller/scope's copy. Perhaps this fiddle will help explain it better: http://jsfiddle.net/vv3odc7t/1/
There is also this stack overflow question about service binding in general: https://stackoverflow.com/a/23774843/1769529
Hope that helps!