ContentTools - How to add new tool with pure js / jquery? - javascript

I try to add a new tool/function to the ContentTools but I don't want to learn Coffeescript and as it states on the website it should running fine with plain jquery.
And I can not find any further documentation how to add a simple tool to my toolbar.
I hope you can help me or is there any other opensource WYSIYG Editor with this beautiful inline editing style like ContentTools which has a better documentation?

As I know, GetmeUK/ContentTools project is an open source project which is written with coffeescript and sass technologies.
What is Coffeescript :
CoffeeScript is a little language that compiles into JavaScript. Underneath that awkward Java-esque patina, JavaScript has always had a gorgeous heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.
What is sass :
Sass is the most mature, stable, and powerful professional grade CSS extension language in the world. just like coffeescripts sass finally is converted to css.
So as I know it is possible to modify the final JS files generated by coffeescript compiler, but the easier way is to learn how to compile coffeescript, then you can modify and rebuild any other open source softwares, libs, and etc... .
How to download and build GetmeUK/ContentTools project ?
first of all you have to clone the project using GIT :
git clone https://github.com/GetmeUK/ContentTools.git
for rebuilding this project you need to have NPM and GEM installed in your operating system. follow the instructions mentioned in the link https://nodejs.org/en/download/ for NPM and https://rubygems.org/pages/download for GEM.
cd ContentTools; npm install; gem install sass;
By running grunt build you can build the project and save pure JS exports for this project. With this way you can use the pure JS which is generated by compiling Coffeescript files. this is what is suggest to you.
By the way there is another harder way to sit and read the compiled JS code of this project for weeks, finally if you have chance you can understand generated codes and then after hard working sessions you can modify it :) I hope the following codes help you :
Coffeescript code:
class ContentTools.Tools.Paragraph extends ContentTools.Tools.Heading
# Convert the current text block to a paragraph (e.g <p>foo</p>)
ContentTools.ToolShelf.stow(#, 'paragraph')
#label = 'Paragraph'
#icon = 'paragraph'
#tagName = 'p'
#canApply: (element, selection) ->
# Return true if the tool can be applied to the current
# element/selection.
if element.isFixed()
return false
return element != undefined
#apply: (element, selection, callback) ->
# Apply the tool to the current element
forceAdd = #editor().ctrlDown()
if ContentTools.Tools.Heading.canApply(element) and not forceAdd
# If the element is a top level text element and the user hasn't
# indicated they want to force add a new paragraph convert it to a
# paragraph in-place.
return super(element, selection, callback)
else
# Dispatch `apply` event
toolDetail = {
'tool': this,
'element': element,
'selection': selection
}
if not #dispatchEditorEvent('tool-apply', toolDetail)
return
# If the element isn't a text element find the nearest top level
# node and insert a new paragraph element after it.
if element.parent().type() != 'Region'
element = element.closest (node) ->
return node.parent().type() is 'Region'
region = element.parent()
paragraph = new ContentEdit.Text('p')
region.attach(paragraph, region.children.indexOf(element) + 1)
# Give the newely inserted paragraph focus
paragraph.focus()
callback(true)
# Dispatch `applied` event
#dispatchEditorEvent('tool-applied', toolDetail)
The compiled JS code:
ContentTools.Tools.Paragraph = (function(_super) {
__extends(Paragraph, _super);
function Paragraph() {
return Paragraph.__super__.constructor.apply(this, arguments);
}
ContentTools.ToolShelf.stow(Paragraph, 'paragraph');
Paragraph.label = 'Paragraph';
Paragraph.icon = 'paragraph';
Paragraph.tagName = 'p';
Paragraph.canApply = function(element, selection) {
if (element.isFixed()) {
return false;
}
return element !== void 0;
};
Paragraph.apply = function(element, selection, callback) {
var forceAdd, paragraph, region, toolDetail;
forceAdd = this.editor().ctrlDown();
if (ContentTools.Tools.Heading.canApply(element) && !forceAdd) {
return Paragraph.__super__.constructor.apply.call(this, element, selection, callback);
} else {
toolDetail = {
'tool': this,
'element': element,
'selection': selection
};
if (!this.dispatchEditorEvent('tool-apply', toolDetail)) {
return;
}
if (element.parent().type() !== 'Region') {
element = element.closest(function(node) {
return node.parent().type() === 'Region';
});
}
region = element.parent();
paragraph = new ContentEdit.Text('p');
region.attach(paragraph, region.children.indexOf(element) + 1);
paragraph.focus();
callback(true);
return this.dispatchEditorEvent('tool-applied', toolDetail);
}
};
return Paragraph;
})(ContentTools.Tools.Heading);
You can go step by step by the tutorial provided by GetContentTools website and replace the code written in Coffescript with its equivalent in JS. for this goal I have some samples for you :
Every where you see #propertyName you can replace it with PackageName.ClassName.propertyName, or for calling super(parameters ...) method, you have to use the Paragraph.__super__.constructor.apply.call(this, parameters ...) syntax.
Also there is links to row files of this project which I share with you :
Tools CoffeeScript File and Exported JS File
After all I think there is no way for doing this work with having no knowledge about Coffescript syntax or concepts, I suggest you to learn it, it would help you to have codes with more performance and clearness.

Related

Using different Class Methods, depending on the used target compiler option

How can I tell TypeScript to use different-written methods (on the same class), depending on the used target option within the tsconfig.json file?
I'm currently re-writing one of my scripts into TypeScript to just "manage" one source, because at the moment I'm working with an ES5 and an ES6 file next to each other. Since TypeScript supports to just change the output target in the tsconfig.json file, I would just need one version to update and maintain.
The problem is I'm using a generator in the ES6 version, which theoretically shouldn't be this issue because TypeScript "adds" a pseudo generator to the top of my ES5 file. BUT: My Pseudo-Generator code, which I'm currently using on the ES5 file, is "cleaner" and way less code.
The Question
Is it possible to overwrite the respective "generator" method or using any special comment annotation (such as //#ts-target) to tell the compiler which code (function body) should be used depending on the used target in the configuration file? (Even If I couldn't found such solution on the official documentation).
An additional function or script, which can be added into the TypeScript compiler process would also help, I guess, because I'm compiling them using a small node.js script (which compiles both ES files without changing the tsconfig.json file directly.)
Or is there any kind of extension, to move different methods of the same class into different files? This way I could "extract" the respective "generator" methods, but this would cause another question: How can I link them depending on the target, because I'm using /// <reference /> linking on the main script file to get all together.
Any other idea?
class Option {
///#ts-target ES5
__walkerVariable1:any undefined
__walkerVariable2:any undefined
__walkerVariable3:any undefined
walker() {
/* Some Pseudo-Walker Code for ES5 */
}
///#ts-target ES6
*walker() {
/* Real Walker Code for ES6 */
}
}
Currently, a Pseudo-Generator code gets added in the ES5 version of my script. I want to prevent this by using a different method / function body with my own Pseudo-Generator. Therefore, I need to tell TypeScript that he should "ignore" the Pseudo-Generator in ES6 / the Real-Generator in ES5 and just render one of them depending on the used target option.
That was a bit tricky, but I found a solution thanks to this GitHub issue.
As mentioned above, I'm using my own node.js script to compile the TypeScript files into two different JavaScript versions (ES5 and ES6). Therefore, I'm using the TypeScript API with the ts.createProgram method. This method allows to add a host object as third parameter, which takes over some compiler processes and one of those is the file loader, called getSourceFile.
The rest is then relatively easy: Searching for a custom comment annotation (in my case \\\#ts-target:ES5 and \\\#ts-target:ES6 respectively) and filter them using RegExp. Maybe not the best solution, but it works!
function compileTS(){
let config = readConfig("ts/tsconfig.json");
let host = ts.createCompilerHost(config.options);
let sourceFile = host.getSourceFile;
// ES5 JavaScript
(function(config){
host.getSourceFile = function(filename) {
if(filename === "ts/options.ts"){
let file = fs.readFileSync("./ts/options.ts").toString();
file = file.replace(/[ ]+\/\/\/\#ts\-target\:ES6\s+([\s\S]*)\/\/\/\#ts\-target\:ES6/gm, "");
return ts.createSourceFile(filename, file, ts.ScriptTarget.ES5, true);
}
return sourceFile.call(host, filename);
}
let program = ts.createProgram(config.fileNames, config.options, host);
let emitResult = program.emit();
report(ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics));
if(emitResult.emitSkipped){
process.exit(1);
}
}(config));
// ES6 JavaScript
config.options.target = 2;
config.options.outFile = "../dist/js/tail.select-es6.js";
(function(config){
host.getSourceFile = function(filename) {
if(filename === "ts/options.ts"){
let file = fs.readFileSync("./ts/options.ts").toString();
file = file.replace(/[ ]+\/\/\/\#ts\-target\:ES5\s+([\s\S]*)\/\/\/\#ts\-target\:ES5/gm, "");
return ts.createSourceFile(filename, file, ts.ScriptTarget.ES2015, true);
}
return sourceFile.call(host, filename);
}
let program = ts.createProgram(config.fileNames, config.options, host);
let emitResult = program.emit();
report(ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics));
if(emitResult.emitSkipped){
process.exit(1);
}
}(config));
}

Rails 5/6: How to include JS functions with webpacker?

I am trying to update a Rails 3 app to Rails 6 and I have problems with the now default webpacker since my Javascript functions are not accessible.
I get: ReferenceError: Can't find variable: functionName for all js function triggers.
What I did is:
create an app_directory in /app/javascript
copied my development javascript file into the app_directory and renamed it to index.js
added console.log('Hello World from Webpacker'); to index.js
added import "app_directory"; to /app/javascript/packs/application.js
added to /config/initializers/content_security_policy.rb:
Rails.application.config.content_security_policy do |policy|
policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
end
I get 'Hello World from Webpacker' logged to console, but when trying to access a simple JS function through <div id="x" onclick="functionX()"></div> in the browser I get the reference error.
I understand that the asset pipeline has been substituted by webpacker, which should be great for including modules, but how should I include simple JS functions? What am I missing?
Thanks in advance?
For instructions on moving from the old asset pipeline to the new webpacker way of doing things, you can see here:
https://www.calleerlandsson.com/replacing-sprockets-with-webpacker-for-javascript-in-rails-5-2/
This is a howto for moving from the asset pipeline to webpacker in Rails 5.2, and it gives you an idea of how things are different in Rails 6 now that webpacker is the default for javascript. In particular:
Now it’s time to move all of your application JavaScript code from
app/assets/javascripts/ to app/javascript/.
To include them in the JavaScript pack, make sure to require them in
app/javascript/pack/application.js:
require('your_js_file')
So, create a file in app/javascript/hello.js like this:
console.log("Hello from hello.js");
Then, in app/javascript/packs/application.js, add this line:
require("hello")
(note that the extension isn't needed)
Now, you can load up a page with the browser console open and see the "Hello!" message in the console. Just add whatever you need in the app/javascript directory, or better yet create subdirectories to keep your code organized.
More information:
This question is cursed. The formerly accepted answer is not just wrong but grotesquely wrong, and the most upvoted answer is still missing the mark by a country mile.
anode84 above is still trying to do things the old way, and webpacker will get in your way if you try that. You have to completely change the way you do javascript and think about javascript when you move to webpacker. There is no "scoping issue". When you put code in a web pack it's self-contained and you use import/export to share code between files. Nothing is global by default.
I get why this is frustrating. You're probably like me, and accustomed to declaring a function in a javascript file and then calling it in your HTML file. Or just throwing some javascript at the end of your HTML file. I have been doing web programming since 1994 (not a typo), so I've seen everything evolve multiple times. Javascript has evolved. You have to learn the new way of doing things.
If you want to add an action to a form or whatever, you can create a file in app/javascript that does what you want. To get data to it, you can use data attributes, hidden fields, etc. If the field doesn't exist, then the code doesn't run.
Here's an example that you might find useful. I use this for showing a popup if a form has a Google reCAPTCHA and the user hasn't checked the box at the time of form submission:
// For any form, on submit find out if there's a recaptcha
// field on the form, and if so, make sure the recaptcha
// was completed before submission.
document.addEventListener("turbolinks:load", function() {
document.querySelectorAll('form').forEach(function(form) {
form.addEventListener('submit', function(event) {
const response_field = document.getElementById('g-recaptcha-response');
// This ensures that the response field is part of the form
if (response_field && form.compareDocumentPosition(response_field) & 16) {
if (response_field.value == '') {
alert("Please verify that you are not a robot.");
event.preventDefault();
event.stopPropagation();
return false;
}
}
});
});
});
Note that this is self-contained. It does not rely on any other modules and nothing else relies on it. You simply require it in your pack(s) and it will watch all form submissions.
Here's one more example of loading a google map with a geojson overlay when the page is loaded:
document.addEventListener("turbolinks:load", function() {
document.querySelectorAll('.shuttle-route-version-map').forEach(function(map_div) {
let shuttle_route_version_id = map_div.dataset.shuttleRouteVersionId;
let geojson_field = document.querySelector(`input[type=hidden][name="geojson[${shuttle_route_version_id}]"]`);
var map = null;
let center = {lat: 36.1638726, lng: -86.7742864};
map = new google.maps.Map(map_div, {
zoom: 15.18,
center: center
});
map.data.addGeoJson(JSON.parse(geojson_field.value));
var bounds = new google.maps.LatLngBounds();
map.data.forEach(function(data_feature) {
let geom = data_feature.getGeometry();
geom.forEachLatLng(function(latlng) {
bounds.extend(latlng);
});
});
map.setCenter(bounds.getCenter());
map.fitBounds(bounds);
});
});
When the page loads, I look for divs with the class "shuttle-route-version-map". For each one that I find, the data attribute "shuttleRouteVersionId" (data-shuttle-route-version-id) contains the ID of the route. I have stored the geojson in a hidden field that can be easily queried given that ID, and I then initialize the map, add the geojson, and then set the map center and bounds based on that data. Again, it's self-contained except for the Google Maps functionality.
You can also learn how to use import/export to share code, and that's really powerful.
So, one more that shows how to use import/export. Here's a simple piece of code that sets up a "watcher" to watch your location:
var driver_position_watch_id = null;
export const watch_position = function(logging_callback) {
var last_timestamp = null;
function success(pos) {
if (pos.timestamp != last_timestamp) {
logging_callback(pos);
}
last_timestamp = pos.timestamp;
}
function error(err) {
console.log('Error: ' + err.code + ': ' + err.message);
if (err.code == 3) {
// timeout, let's try again in a second
setTimeout(start_watching, 1000);
}
}
let options = {
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 14500
};
function start_watching() {
if (driver_position_watch_id) stop_watching_position();
driver_position_watch_id = navigator.geolocation.watchPosition(success, error, options);
console.log("Start watching location updates: " + driver_position_watch_id);
}
start_watching();
}
export const stop_watching_position = function() {
if (driver_position_watch_id) {
console.log("Stopped watching location updates: " + driver_position_watch_id);
navigator.geolocation.clearWatch(driver_position_watch_id);
driver_position_watch_id = null;
}
}
That exports two functions: "watch_position" and "stop_watching_position". To use it, you import those functions in another file.
import { watch_position, stop_watching_position } from 'watch_location';
document.addEventListener("turbolinks:load", function() {
let lat_input = document.getElementById('driver_location_check_latitude');
let long_input = document.getElementById('driver_location_check_longitude');
if (lat_input && long_input) {
watch_position(function(pos) {
lat_input.value = pos.coords.latitude;
long_input.value = pos.coords.longitude;
});
}
});
When the page loads, we look for fields called "driver_location_check_latitude" and "driver_location_check_longitude". If they exist, we set up a watcher with a callback, and the callback fills in those fields with the latitude and longitude when they change. This is how to share code between modules.
So, again, this is a very different way of doing things. Your code is cleaner and more predictable when modularized and organized properly.
This is the future, so fighting it (and setting "window.function_name" is fighting it) will get you nowhere.
Looking at how webpacker "packs" js files and functions:
/***/ "./app/javascript/dashboard/project.js":
/*! no static exports found */
/***/ (function(module, exports) {
function myFunction() {...}
So webpacker stores these functions within another function, making them inaccessible. Not sure why that is, or how to work around it properly.
There IS a workaround, though. You can:
1) change the function signatures from:
function myFunction() { ... }
to:
window.myFunction = function() { ... }
2) keep the function signatures as is, but you would still need to add a reference to them as shown here:
window.myFunction = myFunction
This will make your functions globally accessible from the "window" object.
Replace the code in your custom java Script file
from
function function_name() {// body //}
to
window.function_name = function() {// body //}
From the official rails app guide:
Where to Stick Your JavaScript
Whether you use the Rails asset
pipeline or add a tag directly to a view, you have to make a
choice about where to put any local JavaScript file.
We have a choice of three locations for a local JavaScript file:
The app/assets/javascripts folder,the lib/assets/javascripts folder and the vendor/assets/javascripts folder
Here are guidelines for selecting
a location for your scripts:
Use app/assets/javascripts for JavaScript you create for your
application.
Use lib/assets/javascripts for scripts that are shared by many
applications (but use a gem if you can).
Use vendor/assets/javascripts for copies of jQuery plugins, etc., from
other developers. In the simplest case, when all your JavaScript files
are in the app/assets/javascripts folder, there’s nothing more you
need to do.
Add JavaScript files anywhere else and you will need to understand how
to modify a manifest file.
More reading:
http://railsapps.github.io/rails-javascript-include-external.html

How to invoke arbitrary method from gradle?

Was trying to call out to the Saga Javascript code coverage from Gradle. After a long time playing with it, I was finally able to get it working. However, I am new to gradle and don't know if the way I did it makes the most sense or not! It seems like there might be quite a few ways to do this, so I thought I would post what I did here in the hopes that I might learn if this way was okay or if there was a better way.
After downloading the saga-core from maven central, it turns out that there is no Java "main". So I didn't think I could easily use JavaExec. It looked to me that I needed to create a Java object, set some params, and call the "run" method provided.
Here is the final build.gradle I ended up with:
apply plugin: 'groovy'
buildscript {
repositories {
mavenCentral()
}
dependencies {
// add the jar file withe the code you want to execute to gradle's classpath
classpath 'com.github.timurstrekalov:saga-core:1.1.2'
}
}
configurations {
compile
codeCoverageTask
}
dependencies {
groovy localGroovy()
codeCoverageTask 'com.github.timurstrekalov:saga-core:1.1.2'
}
// I thought this was simple and made sense to be co-located here rather than
// another file...
// getting the imports to compile requires adding the "buildscript" section above
import java.io.File
import com.github.timurstrekalov.saga.core.CoverageGenerator
class SagaCoverageTask extends DefaultTask {
def outputDirectory
def baseDirectory
def includes = 'my_includesPattern_here'
def excludes = 'my_excludesPattern_here'
def noInstrumentPatterns = [ 'my_noIntrumentPatterns_here' ]
#TaskAction
def runCoverage() {
// check these were set correctly!
println outputDirectory
println 'baseDir' + baseDirectory
// create an instance of the object
CoverageGenerator generator = new CoverageGenerator(baseDirectory, includes, excludes, outputDirectory)
generator.noInstrumentPatterns = noInstrumentPatterns
// there are params, but they would be handled in the same way if needed
generator.run() // invoke the arbitrary method
}
}
task genCodeCoverage(type: SagaCoverageTask) {
// needed the values of task properties, so these are set here
outputDirectory = new File('' + reportsDir + '/coverage')
baseDirectory = new File('' + projectDir + '/src')
}
Looks like a decent start. File objects should generally be created with the project.file() method because it handles relative paths better than new File() (although it's not a problem in this particular case). For example: file("$reportsDir/coverage").
If the task class grows bigger or you want to reuse it across projects/builds, you should move it somewhere else (e.g. into buildSrc) and add some test coverage.
By annotating the task class properties with #InputDirectory, #OutputDirectory, etc. you can make it so that the task will only run if some input has changed or previously generated output isn't available anymore. This can speed up the build.

What should I do to obtain javascript intellisense for my own js library in Visual Studio

I have multiple javascript files and they have their own functions. If I make reference one of them to inside any of them, it doesnt see its functions if the function is not prototype. What is logic inside the Intellisense ?
I want to use Splash function with Intellisense feature below, how can I do that ?
//My.js
/// <reference path="Test.js" />
.
//Test.js
NameSpace.prototype.UI = new function () {
this.Splash = function (value) {
try {
if (value == 1) {
$('#splash').css('height', $(document).height());
$('#splashContent').css('top', $(window).height() / 2);
$('#splash').fadeIn();
$('#splashContent').fadeIn();
setTimeout("SF.UI.Splash(0)", 3000);
}
else if (value == 0) {
$('#splash').fadeOut(1000);
$('#splashContent').fadeOut(1000);
}
else if (value == 3) {
$('#splash').css('height', $(document).height());
$('#splashContent').css('top', $(window).height() / 2);
$('#splash').fadeIn();
$('#splashContent').fadeIn();
}
} catch (e) {
Splash(0);
}
}
}
JS Intellisense is flaky at best. Despite all those articles, in my experience, it doesn't work as advertised. I would suggest:
Make sure all your JS files are in the same project (making ti work across projects is even trickier).
Make sure /// <reference path="Test.js" /> is the very first line in JS.
Make sure there are no errors in the Test.js file (run JSLint on it to be sure) or use the vsdoc approach.
Look at the Output tool window to see if it contains any errors related to updating intellisense. This will help you troubleshoot and remove errors in your referenced JS file.
Alternative to create a Test-vsdoc.js file (this way, even if your main JS file has errors, it would not cause intellisense to fail) :
//Test-vsdoc.js
NameSpace.prototype.UI = new function () {
this.Splash = function (value) {
/// <summary>This is my function summary</summary>
}
}
VS would automatically include it next time you restart the IDE (or try a force update Ctrl+Shift+J)
Assuming that is the actual contents of your js file, including the //My.js comment, then that is your problem right there.
/// <reference comments must be at the very top of the file, before any other content, otherwise they will not work.
Side note, did you know there is a code snippet for that reference? its ref, then tab tab. Also you can drag / drop a file and get the comment.
Once you have your references done, you can force VS2010 to refresh your js comments by pressing Ctrl+Shift+J while editing the .js file

Haxe for javascript without global namespace pollution?

This question only applies to Haxe version < 2.10
I've known about haxe for a while, but never really played with it until yesterday. Being curious, I decided to port showdown.js, a javascript port of markdown.pl, to haxe. This was pretty straightforward, and the javascript it generates seems to run fine (edit: If you want to see it in action, check it out here).
However, I noticed that the generated code dumps a ton of stuff in the global namespace... and what's worse, it does it by assigning values to undeclared identifiers without using the var keyword, so they're global even if you wrap the whole thing with a closure.
For example...
if(typeof js=='undefined') js = {}
...
Hash = function(p) { if( p === $_ ) return; {
...
EReg = function(r,opt) { if( r === $_ ) return; {
...
I managed to clean most of that up with sed, but I'm also bothered by stuff like this:
{
String.prototype.__class__ = String;
String.__name__ = ["String"];
Array.prototype.__class__ = Array;
Array.__name__ = ["Array"];
Int = { __name__ : ["Int"]}
Dynamic = { __name__ : ["Dynamic"]}
Float = Number;
Float.__name__ = ["Float"];
Bool = { __ename__ : ["Bool"]}
Class = { __name__ : ["Class"]}
Enum = { }
Void = { __ename__ : ["Void"]}
}
{
Math.__name__ = ["Math"];
Math.NaN = Number["NaN"];
Math.NEGATIVE_INFINITY = Number["NEGATIVE_INFINITY"];
Math.POSITIVE_INFINITY = Number["POSITIVE_INFINITY"];
Math.isFinite = function(i) {
return isFinite(i);
}
Math.isNaN = function(i) {
return isNaN(i);
}
}
This is some pretty unsavory javascript.
Questions
Is there a fork or clone of haxe somewhere that doesn't pollute globals? Is it worth it to modify the haxe source to get what I want, or has someone already solved this? Googling hasn't turned up much. I'm open to any suggestions. Meanwhile, I'm dying to see what kind of PHP code this thing's going to produce... :D
Answers?
Here are some of the ideas I've tried:
postprocessing
Here's my humble build script; it does a pretty good job of stripping stuff out, but it doesn't catch everything. I'm hesitant to remove the modifications to the built-in constructor prototypes; I'm sure that would break things. Fixing everything might be a bit of a task, and I don't want to start on it if someone's already done the work...
haxe -cp ~/Projects/wmd-new -main Markdown -js markdown.js
echo "this.Markdown=(function(){ var \$closure, Float;" > markdown.clean.js;
sed "s/^if(typeof js=='undefined') js = {}$/if(typeof js=='undefined') var js = {};/g ;
s/^\([ \x09]*\)\([\$_a-zA-Z0-9]* = \({\|function\)\)/\1var \2/g ;
/^[ \x09]*\(else \)\?null;$/d ;
" markdown.js >> markdown.clean.js
echo "return Markdown}());" >> markdown.clean.js;
java -jar closure/compiler.jar --js markdown.clean.js \
--compilation_level SIMPLE_OPTIMIZATIONS \
> markdown.cc.js
--js-namespace switch saves the day
Thanks to Dean Burge for pointing out the namespace switch. This pretty much solved my problem, with a minor bit of help. Here's my current build script. I think this catches all the global variables...
NS=N\$
haxe -cp ~/Projects/wmd-new -main Markdown --js-namespace $NS -js markdown.js
# export our function and declare some vars
echo "this.markdown=(function(){var \$_,\$Main,\$closure,\$estr,js,"$NS"" > markdown.clean.js;
# strip silly lines containing "null;" or "else null;"
sed "/^[ \x09]*\(else \)\?null;$/d ;" markdown.js >> markdown.clean.js
# finish the closure
echo "return "$NS".Markdown.makeHtml}());" >> markdown.clean.js;
I use the namespace switch on the compiler to clean those global root types up.
Haxe is not meant to be used for writing an isolated reusable component in a javascript web application. This is evidenced by the fact that the compiler emits standard library for every goddamn compilation. Most optimal use of javascript target is to write an application entirely in haxe and call external stuff using untyped blocks hoping it won't break anything. You should treat haxe output like a flash clip, oblivious to the environment it runs in, assumes it is the only thing running.
Or you might try wrapping the code with a with() block.
there's a namespaced (experimental) haxe compiler here http://github.com/webr3/haxe
The JSTM JavaScript generator macro optimizes haxe output in a number of ways:
the javascript output is split into seperate files per type
these files are optimized
a loader script loads the required types asynchronously
only one global variable is used: jstm
only code that is actually required to run your app is downloaded
new types can be loaded at runtime which makes possible highly scalable apps
check out http://code.google.com/p/jstm/ for more info.

Categories