<commit_before>module("can/view/ejs, rendering",{
	setup : function(){

		this.animals = ['sloth', 'bear', 'monkey']
		if(!this.animals.each){
			this.animals.each = function(func){
				for(var i =0; i < this.length; i++){
					func(this[i])
				}
			}
		}

		this.squareBrackets = "
<% this.animals.each(function(animal){%>" +
		               "
<%= animal %>
" +
			      "<%});%>
"
	    this.squareBracketsNoThis = "
<% animals.each(function(animal){%>" +
		               "
<%= animal %>
" +
			      "<%});%>
"
	    this.angleBracketsNoThis  = "
<% animals.each(function(animal){%>" +
		               "
<%= animal %>
" +
			      "<%});%>
";

	}
})

test("render with left bracket", function(){
	var compiled = new Can.EJS({text: this.squareBrackets, type: '['}).render({animals: this.animals})
	equals(compiled, "

sloth

bear

monkey

", "renders with bracket")
})
test("render with with", function(){
	var compiled = new Can.EJS({text: this.squareBracketsNoThis, type: '['}).render({animals: this.animals}) ;
	equals(compiled, "

sloth

bear

monkey

", "renders bracket with no this")
})
test("default carrot", function(){
	var compiled = new Can.EJS({text: this.angleBracketsNoThis}).render({animals: this.animals}) ;

	equals(compiled, "

sloth

bear

monkey

")
})
test("render with double angle", function(){
	var text = "<%% replace_me %>"+
			  "
<% animals.each(function(animal){%>" +
	               "
<%= animal %>
" +
		      "<%});%>
";
	var compiled = new Can.EJS({text: text}).render({animals: this.animals}) ;
	equals(compiled, "<% replace_me %>

sloth

bear

monkey

", "works")
});

test("comments", function(){
	var text = "<%# replace_me %>"+
			  "
<% animals.each(function(animal){%>" +
	               "
<%= animal %>
" +
		      "<%});%>
";
	var compiled = new Can.EJS({text: text}).render({animals: this.animals}) ;
	equals(compiled,"

sloth

bear

monkey

" )
});

test("multi line", function(){
	var text = "a \n b \n c",
		result = new Can.EJS({text: text}).render({}) ;

	equals(result, text)
})

test("escapedContent", function(){
	var text = "<%= tags %>&<%= number %><input value='<%= quotes %>'/>";
	var compiled = new Can.EJS({text: text}).render({tags: "foo < bar < car > zar > poo",
							quotes : "I use 'quote' fingers \"a lot\"",
							number : 123}) ;

	var div = document.createElement('div');
	div.innerHTML = compiled;

	equals(div.getElementsByTagName('span')[0].firstChild.nodeValue, "foo < bar < car > zar > poo" );
	equals(div.getElementsByTagName('strong')[0].firstChild.nodeValue, 123 );
	equals(div.getElementsByTagName('input')[0].value, "I use 'quote' fingers \"a lot\"" );
	equals(div.getElementsByTagName('label')[0].innerHTML, "&" );
})

test("unescapedContent", function(){
	var text = "<%== tags %>
<%= tags %>
<input value='<%== quotes %>'/>";
	var compiled = new Can.EJS({text: text}).render({tags: "foobar",
							quotes : "I use 'quote' fingers \"a lot\""}) ;

	var div = document.createElement('div');
	div.innerHTML = compiled;

	equals(div.getElementsByTagName('span')[0].firstChild.nodeType, 1 );
	equals(div.getElementsByTagName('div')[0].firstChild.nodeValue.toLowerCase(), "foobar" );
	equals(div.getElementsByTagName('span')[0].innerHTML.toLowerCase(), "foobar" );
	equals(div.getElementsByTagName('input')[0].value, "I use 'quote' fingers \"a lot\"", "escapped no matter what" );
});

test("returning blocks", function(){
	var somethingHelper = function(cb){
		return cb([1,2,3,4])
	}

	var res = Can.
		render("//can/view/ejs/test_template.ejs",{
			something: somethingHelper,
			items: ['a','b']
		});
	// make sure expected values are in res
	ok(/\s4\s/.test(res), "first block called" );
	equals(res.match(/ItemsLength4/g).length, 4, "innerBlock and each")
});

test("easy hookup", function(){
	var div = document.createElement('div');
	div.appendChild(Can.view("//can/view/ejs/easyhookup.ejs",{text: "yes"}))

	ok( div.getElementsByTagName('div')[0].className.indexOf("yes") != -1, "has yes" )
});

test("helpers", function() {
	Can.EJS.Helpers.prototype.simpleHelper = function()
	{
		return 'Simple';
	}

	Can.EJS.Helpers.prototype.elementHelper = function()
	{
		return function(el) {
			el.innerHTML = 'Simple';
		}
	}

	var text = "
<%= simpleHelper() %>
";
	var compiled = new Can.EJS({text: text}).render() ;
	equals(compiled, "
Simple
");

	text = "<div id=\"hookup\" <%= elementHelper() %>>
";
	compiled = new Can.EJS({text: text}).render() ;
	Can.append( Can.$('#qunit-test-area'), Can.view.frag(compiled));
	equals(Can.$('#hookup')[0].innerHTML, "Simple");
});

test("live binding", function(){

	var text = "<div class='<%== task.attr('completed') ? 'complete' : ''%>'><%== task.attr('name') %>
";
	var task = new Can.Observe({
		name : 'dishes'
	})
	var compiled = new Can.EJS({text: text}).render({task:  task}) ;

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled))

	equals(div.getElementsByTagName('div')[0].innerHTML,"dishes", "html correctly dishes")
	equals(div.getElementsByTagName('div')[0].className,"", "class empty")

	task.attr('name','lawn')

	equals(div.getElementsByTagName('div')[0].innerHTML,"lawn", "html correctly lawn")
	equals(div.getElementsByTagName('div')[0].className,"", "class empty")

	task.attr('completed', true);

	equals(div.getElementsByTagName('div')[0].className,"complete", "class changed to complete")
});

test("block live binding", function(){

	var text = "
<% if( obs.attr('sex') == 'male' ){ %>"+
			"Mr."+
		"<% } else { %>"+
		  "Ms."+
		"<% } %>"+
		"
"

	var obs = new Can.Observe({
		sex : 'male'
	})

	var compiled = new Can.EJS({text: text}).render({obs: obs});

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled))

	equals(div.getElementsByTagName('div')[0].innerHTML, "Mr.","initial content")

	obs.attr('sex','female')

	equals(div.getElementsByTagName('div')[0].innerHTML, "Ms.","updated label")

})

test("hookups in tables", function(){
	var text = "
<% if( obs.attr('sex') == 'male' ){ %>"+
			"
Mr.
"+
		"<% } else { %>"+
		  "
Ms.
"+
		"<% } %>"+
		"
"

	var obs = new Can.Observe({
		sex : 'male'
	})

	var compiled = new Can.EJS({text: text}).render({obs: obs});

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled));

	equals(div.getElementsByTagName('tbody')[0].innerHTML, "
Mr.
","initial content")

	obs.attr('sex','female')

	equals(div.getElementsByTagName('tbody')[0].innerHTML, "
Ms.
","updated label")
})

test('multiple hookups in a single attribute', function() {
	var text =	'<div class=\'<%= obs.attr("foo") %>' +
							'<%= obs.attr("bar") %><%= obs.attr("baz") %>\'>
',

	obs = new Can.Observe({
		foo: 'a',
		bar: 'b',
		baz: 'c'
	}),

	compiled = new Can.EJS({ text: text }).render({ obs: obs })

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled));

	equals(div.innerHTML, '

', 'initial render');

	obs.attr('bar', 'e');

	equals(div.innerHTML, '

', 'updated values');

	obs.attr('bar', 'f');

	equals(div.innerHTML, '

', 'updated values');
});

test('adding and removing multiple html content within a single element', function(){

	var text =	'
<%== obs.attr("a") %><%== obs.attr("b") %><%== obs.attr("c") %>
',

	obs = new Can.Observe({
		a: 'a',
		b: 'b',
		c: 'c'
	});

	compiled = new Can.EJS({ text: text }).render({ obs: obs })

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled));

	equals(div.innerHTML, '
abc
', 'initial render');

	obs.attr({a: '', b : '', c: ''});

	equals(div.innerHTML, '

', 'updated values');

	obs.attr({c: 'c'});

	equals(div.innerHTML, '
c
', 'updated values');
});

test('live binding and removeAttr', function(){

	var text = '<% if(obs.attr("show")) { %>' +
			'<p class="<%= obs.attr("className")%>"><%= obs.attr("message") %>
' +
		'<% } %>',

		obs = new Can.Observe({
			show: true,
			className: 'myMessage',
			message: 'Live long and prosper'
		}),

		compiled = new Can.EJS({ text: text }).render({ obs: obs }),

		div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled));

	equals(div.innerHTML, '
Live long and prosper
', 'initial render');

	obs.removeAttr('className');

	equals(div.innerHTML, '
Live long and prosper
', 'attribute is undefined');

	obs.attr('className', 'newClass');

	equals(div.innerHTML, '
Live long and prosper
', 'attribute updated');

	obs.removeAttr('message');

	equals(div.innerHTML, '
undefined
', 'text node value is undefined');

	obs.attr('message', 'Warp drive, Mr. Sulu');

	equals(div.innerHTML, '
Warp drive, Mr. Sulu
', 'text node updated');

	obs.removeAttr('show');

	equals(div.innerHTML, '', 'value in block statement is undefined');

	obs.attr('show', true);

	equals(div.innerHTML, '
Warp drive, Mr. Sulu
', 'value in block statement updated');

});

test('hookup within a tag', function () {
	var text =	'<div <%== obs.attr("foo") %> '
		//+ 'disabled '
		+ '<%== obs.attr("baz") %>>lorem ipsum
',

	obs = new Can.Observe({
		foo: 'class="a"',
		baz: 'some=\'property\''
	}),

	compiled = new Can.EJS({ text: text }).render({ obs: obs });

	var div = $('
');

	div.append(Can.view.frag(compiled));
	var anchor = div.find('.a');

	equals(anchor.attr('class'), 'a');
	equals(anchor.attr('some'), 'property');
	//equals(div.innerHTML, '
lorem ipsum
', 'initial render');

	obs.attr('foo', 'class="b"');
	equals(anchor.attr('class'), 'b');
	equals(anchor.attr('some'), 'property');
	//equals(div.innerHTML, '
lorem ipsum
', 'updated values');

	obs.attr('baz', 'some=\'new property\'');
	equals(anchor.attr('class'), 'b');
	equals(anchor.attr('some'), 'new property');
	//equals(div.innerHTML, '
lorem ipsum
', 'updated values');

	obs.attr('foo', '');
	obs.attr('baz', '');
	equals(anchor.attr('class'), undefined);
	equals(anchor.attr('some'), undefined);

	//equals(div.innerHTML, '
lorem ipsum
', 'removing values');
});
<commit_msg>The 'hookup within a tag' test required jQuery, now supports all libraries.<commit_after>module("can/view/ejs, rendering",{
	setup : function(){

		this.animals = ['sloth', 'bear', 'monkey']
		if(!this.animals.each){
			this.animals.each = function(func){
				for(var i =0; i < this.length; i++){
					func(this[i])
				}
			}
		}

		this.squareBrackets = "
<% this.animals.each(function(animal){%>" +
		               "
<%= animal %>
" +
			      "<%});%>
"
	    this.squareBracketsNoThis = "
<% animals.each(function(animal){%>" +
		               "
<%= animal %>
" +
			      "<%});%>
"
	    this.angleBracketsNoThis  = "
<% animals.each(function(animal){%>" +
		               "
<%= animal %>
" +
			      "<%});%>
";

	}
})

test("render with left bracket", function(){
	var compiled = new Can.EJS({text: this.squareBrackets, type: '['}).render({animals: this.animals})
	equals(compiled, "

sloth

bear

monkey

", "renders with bracket")
})
test("render with with", function(){
	var compiled = new Can.EJS({text: this.squareBracketsNoThis, type: '['}).render({animals: this.animals}) ;
	equals(compiled, "

sloth

bear

monkey

", "renders bracket with no this")
})
test("default carrot", function(){
	var compiled = new Can.EJS({text: this.angleBracketsNoThis}).render({animals: this.animals}) ;

	equals(compiled, "

sloth

bear

monkey

")
})
test("render with double angle", function(){
	var text = "<%% replace_me %>"+
			  "
<% animals.each(function(animal){%>" +
	               "
<%= animal %>
" +
		      "<%});%>
";
	var compiled = new Can.EJS({text: text}).render({animals: this.animals}) ;
	equals(compiled, "<% replace_me %>

sloth

bear

monkey

", "works")
});

test("comments", function(){
	var text = "<%# replace_me %>"+
			  "
<% animals.each(function(animal){%>" +
	               "
<%= animal %>
" +
		      "<%});%>
";
	var compiled = new Can.EJS({text: text}).render({animals: this.animals}) ;
	equals(compiled,"

sloth

bear

monkey

" )
});

test("multi line", function(){
	var text = "a \n b \n c",
		result = new Can.EJS({text: text}).render({}) ;

	equals(result, text)
})

test("escapedContent", function(){
	var text = "<%= tags %>&<%= number %><input value='<%= quotes %>'/>";
	var compiled = new Can.EJS({text: text}).render({tags: "foo < bar < car > zar > poo",
							quotes : "I use 'quote' fingers \"a lot\"",
							number : 123}) ;

	var div = document.createElement('div');
	div.innerHTML = compiled;

	equals(div.getElementsByTagName('span')[0].firstChild.nodeValue, "foo < bar < car > zar > poo" );
	equals(div.getElementsByTagName('strong')[0].firstChild.nodeValue, 123 );
	equals(div.getElementsByTagName('input')[0].value, "I use 'quote' fingers \"a lot\"" );
	equals(div.getElementsByTagName('label')[0].innerHTML, "&" );
})

test("unescapedContent", function(){
	var text = "<%== tags %>
<%= tags %>
<input value='<%== quotes %>'/>";
	var compiled = new Can.EJS({text: text}).render({tags: "foobar",
							quotes : "I use 'quote' fingers \"a lot\""}) ;

	var div = document.createElement('div');
	div.innerHTML = compiled;

	equals(div.getElementsByTagName('span')[0].firstChild.nodeType, 1 );
	equals(div.getElementsByTagName('div')[0].firstChild.nodeValue.toLowerCase(), "foobar" );
	equals(div.getElementsByTagName('span')[0].innerHTML.toLowerCase(), "foobar" );
	equals(div.getElementsByTagName('input')[0].value, "I use 'quote' fingers \"a lot\"", "escapped no matter what" );
});

test("returning blocks", function(){
	var somethingHelper = function(cb){
		return cb([1,2,3,4])
	}

	var res = Can.
		render("//can/view/ejs/test_template.ejs",{
			something: somethingHelper,
			items: ['a','b']
		});
	// make sure expected values are in res
	ok(/\s4\s/.test(res), "first block called" );
	equals(res.match(/ItemsLength4/g).length, 4, "innerBlock and each")
});

test("easy hookup", function(){
	var div = document.createElement('div');
	div.appendChild(Can.view("//can/view/ejs/easyhookup.ejs",{text: "yes"}))

	ok( div.getElementsByTagName('div')[0].className.indexOf("yes") != -1, "has yes" )
});

test("helpers", function() {
	Can.EJS.Helpers.prototype.simpleHelper = function()
	{
		return 'Simple';
	}

	Can.EJS.Helpers.prototype.elementHelper = function()
	{
		return function(el) {
			el.innerHTML = 'Simple';
		}
	}

	var text = "
<%= simpleHelper() %>
";
	var compiled = new Can.EJS({text: text}).render() ;
	equals(compiled, "
Simple
");

	text = "<div id=\"hookup\" <%= elementHelper() %>>
";
	compiled = new Can.EJS({text: text}).render() ;
	Can.append( Can.$('#qunit-test-area'), Can.view.frag(compiled));
	equals(Can.$('#hookup')[0].innerHTML, "Simple");
});

test("live binding", function(){

	var text = "<div class='<%== task.attr('completed') ? 'complete' : ''%>'><%== task.attr('name') %>
";
	var task = new Can.Observe({
		name : 'dishes'
	})
	var compiled = new Can.EJS({text: text}).render({task:  task}) ;

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled))

	equals(div.getElementsByTagName('div')[0].innerHTML,"dishes", "html correctly dishes")
	equals(div.getElementsByTagName('div')[0].className,"", "class empty")

	task.attr('name','lawn')

	equals(div.getElementsByTagName('div')[0].innerHTML,"lawn", "html correctly lawn")
	equals(div.getElementsByTagName('div')[0].className,"", "class empty")

	task.attr('completed', true);

	equals(div.getElementsByTagName('div')[0].className,"complete", "class changed to complete")
});

test("block live binding", function(){

	var text = "
<% if( obs.attr('sex') == 'male' ){ %>"+
			"Mr."+
		"<% } else { %>"+
		  "Ms."+
		"<% } %>"+
		"
"

	var obs = new Can.Observe({
		sex : 'male'
	})

	var compiled = new Can.EJS({text: text}).render({obs: obs});

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled))

	equals(div.getElementsByTagName('div')[0].innerHTML, "Mr.","initial content")

	obs.attr('sex','female')

	equals(div.getElementsByTagName('div')[0].innerHTML, "Ms.","updated label")

})

test("hookups in tables", function(){
	var text = "
<% if( obs.attr('sex') == 'male' ){ %>"+
			"
Mr.
"+
		"<% } else { %>"+
		  "
Ms.
"+
		"<% } %>"+
		"
"

	var obs = new Can.Observe({
		sex : 'male'
	})

	var compiled = new Can.EJS({text: text}).render({obs: obs});

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled));

	equals(div.getElementsByTagName('tbody')[0].innerHTML, "
Mr.
","initial content")

	obs.attr('sex','female')

	equals(div.getElementsByTagName('tbody')[0].innerHTML, "
Ms.
","updated label")
})

test('multiple hookups in a single attribute', function() {
	var text =	'<div class=\'<%= obs.attr("foo") %>' +
							'<%= obs.attr("bar") %><%= obs.attr("baz") %>\'>
',

	obs = new Can.Observe({
		foo: 'a',
		bar: 'b',
		baz: 'c'
	}),

	compiled = new Can.EJS({ text: text }).render({ obs: obs })

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled));

	equals(div.innerHTML, '

', 'initial render');

	obs.attr('bar', 'e');

	equals(div.innerHTML, '

', 'updated values');

	obs.attr('bar', 'f');

	equals(div.innerHTML, '

', 'updated values');
});

test('adding and removing multiple html content within a single element', function(){

	var text =	'
<%== obs.attr("a") %><%== obs.attr("b") %><%== obs.attr("c") %>
',

	obs = new Can.Observe({
		a: 'a',
		b: 'b',
		c: 'c'
	});

	compiled = new Can.EJS({ text: text }).render({ obs: obs })

	var div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled));

	equals(div.innerHTML, '
abc
', 'initial render');

	obs.attr({a: '', b : '', c: ''});

	equals(div.innerHTML, '

', 'updated values');

	obs.attr({c: 'c'});

	equals(div.innerHTML, '
c
', 'updated values');
});

test('live binding and removeAttr', function(){

	var text = '<% if(obs.attr("show")) { %>' +
			'<p class="<%= obs.attr("className")%>"><%= obs.attr("message") %>
' +
		'<% } %>',

		obs = new Can.Observe({
			show: true,
			className: 'myMessage',
			message: 'Live long and prosper'
		}),

		compiled = new Can.EJS({ text: text }).render({ obs: obs }),

		div = document.createElement('div');

	div.appendChild(Can.view.frag(compiled));

	equals(div.innerHTML, '
Live long and prosper
', 'initial render');

	obs.removeAttr('className');

	equals(div.innerHTML, '
Live long and prosper
', 'attribute is undefined');

	obs.attr('className', 'newClass');

	equals(div.innerHTML, '
Live long and prosper
', 'attribute updated');

	obs.removeAttr('message');

	equals(div.innerHTML, '
undefined
', 'text node value is undefined');

	obs.attr('message', 'Warp drive, Mr. Sulu');

	equals(div.innerHTML, '
Warp drive, Mr. Sulu
', 'text node updated');

	obs.removeAttr('show');

	equals(div.innerHTML, '', 'value in block statement is undefined');

	obs.attr('show', true);

	equals(div.innerHTML, '
Warp drive, Mr. Sulu
', 'value in block statement updated');

});

test('hookup within a tag', function () {
	var text =	'<div <%== obs.attr("foo") %> '
		//+ 'disabled '
		+ '<%== obs.attr("baz") %>>lorem ipsum
',

	obs = new Can.Observe({
		foo: 'class="a"',
		baz: 'some=\'property\''
	}),

	compiled = new Can.EJS({ text: text }).render({ obs: obs });

	var div = document.createElement('div');
	div.appendChild(Can.view.frag(compiled));
	var anchor = div.getElementsByTagName('div')[0];

	equals(anchor.getAttribute('class'), 'a');
	equals(anchor.getAttribute('some'), 'property');
	//equals(div.innerHTML, '
lorem ipsum
', 'initial render');

	obs.attr('foo', 'class="b"');
	equals(anchor.getAttribute('class'), 'b');
	equals(anchor.getAttribute('some'), 'property');
	//equals(div.innerHTML, '
lorem ipsum
', 'updated values');

	obs.attr('baz', 'some=\'new property\'');
	equals(anchor.getAttribute('class'), 'b');
	equals(anchor.getAttribute('some'), 'new property');
	//equals(div.innerHTML, '
lorem ipsum
', 'updated values');

	obs.attr('foo', '');
	obs.attr('baz', '');
	equals(anchor.getAttribute('class'), undefined);
	equals(anchor.getAttribute('some'), undefined);

	//equals(div.innerHTML, '
lorem ipsum
', 'removing values');
});
<|endoftext|>


<commit_before>function GM_ScriptStorage(script) {
  this.prefMan = new GM_PrefManager(["scriptvals.",
                                     script.namespace,
                                     "/",
                                     script.name,
                                     "."].join(""));
}

GM_ScriptStorage.prototype.setValue = function(name, val) {
  if (!GM_apiLeakCheck("GM_setValue")) {
    return;
  }

  this.prefMan.setValue(name, val);
};

GM_ScriptStorage.prototype.getValue = function(name, defVal) {
  if (!GM_apiLeakCheck("GM_getValue")) {
    return;
  }

  return this.prefMan.getValue(name, defVal);
};

function GM_Resources(script){
  this.script = script;
}

GM_Resources.prototype.getResourceURL = function(name) {
  if (!GM_apiLeakCheck("GM_getResourceURL")) {
    return;
  }

  return this.getDep_(name).dataContent;
};

GM_Resources.prototype.getResourceText = function(name) {
  if (!GM_apiLeakCheck("GM_getResourceText")) {
    return;
  }

  return this.getDep_(name).textContent;
};

GM_Resources.prototype.getDep_ = function(name) {
  var resources = this.script.resources;
  for (var i = 0, resource; resource = resources[i]; i++)
    if (resource.name == name)
      return resource;
  throw new Error("No resource with name: " + name); // NOTE: Non localised string
};

function GM_ScriptLogger(script) {
  var namespace = script.namespace;

  if (namespace.substring(namespace.length - 1) != "/") {
    namespace += "/";
  }

  this.prefix = [namespace, script.name, ": "].join("");
}

GM_ScriptLogger.prototype.log = function(message) {
  GM_log(this.prefix + message, true);
};

GM_ScriptStorage.prototype.deleteValue = function(name) {
  return this.prefMan.remove(name);
}

GM_ScriptStorage.prototype.listValues = function() {
  return this.prefMan.listValues();
}

// Based on Mark Pilgrim's GM_addGlobalStyle from
// http://diveintogreasemonkey.org/patterns/add-css.html. Used by permission
// under GPL: http://diveintogreasemonkey.org/license/gpl.html
function GM_addStyle(doc, css) {
  var head, style;
  head = doc.getElementsByTagName("head")[0];
  if (!head) { return; }
  style = doc.createElement("style");
  style.type = "text/css";
  style.innerHTML = css;
  head.appendChild(style);
}

function GM_console(script) {
  // based on http://www.getfirebug.com/firebug/firebugx.js
  var names = [
    "debug", "warn", "error", "info", "assert", "dir", "dirxml",
    "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile",
    "profileEnd"
  ];

  for (var i=0, name; name=names[i]; i++) {
    this[name] = function() {};
  }

  // Important to use this private variable so that user scripts can't make
  // this call something else by redefining <this> or <logger>.
  var logger = new GM_ScriptLogger(script);
  this.log = function() {
    logger.log(
      Array.prototype.slice.apply(arguments).join("\n")
    );
  };
}

GM_console.prototype.log = function() {
};
<commit_msg>Add GM_apiLeakCheck() calls to _deleteValue and _listValues.<commit_after>function GM_ScriptStorage(script) {
  this.prefMan = new GM_PrefManager(["scriptvals.",
                                     script.namespace,
                                     "/",
                                     script.name,
                                     "."].join(""));
}

GM_ScriptStorage.prototype.setValue = function(name, val) {
  if (!GM_apiLeakCheck("GM_setValue")) {
    return;
  }

  this.prefMan.setValue(name, val);
};

GM_ScriptStorage.prototype.getValue = function(name, defVal) {
  if (!GM_apiLeakCheck("GM_getValue")) {
    return;
  }

  return this.prefMan.getValue(name, defVal);
};

function GM_Resources(script){
  this.script = script;
}

GM_Resources.prototype.getResourceURL = function(name) {
  if (!GM_apiLeakCheck("GM_getResourceURL")) {
    return;
  }

  return this.getDep_(name).dataContent;
};

GM_Resources.prototype.getResourceText = function(name) {
  if (!GM_apiLeakCheck("GM_getResourceText")) {
    return;
  }

  return this.getDep_(name).textContent;
};

GM_Resources.prototype.getDep_ = function(name) {
  var resources = this.script.resources;
  for (var i = 0, resource; resource = resources[i]; i++)
    if (resource.name == name)
      return resource;
  throw new Error("No resource with name: " + name); // NOTE: Non localised string
};

function GM_ScriptLogger(script) {
  var namespace = script.namespace;

  if (namespace.substring(namespace.length - 1) != "/") {
    namespace += "/";
  }

  this.prefix = [namespace, script.name, ": "].join("");
}

GM_ScriptLogger.prototype.log = function(message) {
  GM_log(this.prefix + message, true);
};

GM_ScriptStorage.prototype.deleteValue = function(name) {
  if (!GM_apiLeakCheck("GM_setValue")) {
    return;
  }

  return this.prefMan.remove(name);
}

GM_ScriptStorage.prototype.listValues = function() {
  if (!GM_apiLeakCheck("GM_setValue")) {
    return;
  }

  return this.prefMan.listValues();
}

// Based on Mark Pilgrim's GM_addGlobalStyle from
// http://diveintogreasemonkey.org/patterns/add-css.html. Used by permission
// under GPL: http://diveintogreasemonkey.org/license/gpl.html
function GM_addStyle(doc, css) {
  var head, style;
  head = doc.getElementsByTagName("head")[0];
  if (!head) { return; }
  style = doc.createElement("style");
  style.type = "text/css";
  style.innerHTML = css;
  head.appendChild(style);
}

function GM_console(script) {
  // based on http://www.getfirebug.com/firebug/firebugx.js
  var names = [
    "debug", "warn", "error", "info", "assert", "dir", "dirxml",
    "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile",
    "profileEnd"
  ];

  for (var i=0, name; name=names[i]; i++) {
    this[name] = function() {};
  }

  // Important to use this private variable so that user scripts can't make
  // this call something else by redefining <this> or <logger>.
  var logger = new GM_ScriptLogger(script);
  this.log = function() {
    logger.log(
      Array.prototype.slice.apply(arguments).join("\n")
    );
  };
}

GM_console.prototype.log = function() {
};
<|endoftext|>


<commit_before>var result_mapping = [
    {'key': 'id',
     'short': 'id'},
    {'key': '{af4b2e00-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'filename'
    },
    {'key': '{f5d1dcd8-c553-4346-8d4d-672c85bb59be}',
     'short': 'license',
    },
    {'key': '{9b071045-118c-4f42-afa1-c3121783ac66}',
     'short': 'creator',
    },
    {'key': '{af4b2e2c-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'copyright',
    },
    {'key': '{2ce9c8eb-b83d-4a91-9d09-2141cac7de12}',
     'short': 'description',
    },
    {'key': '{af4b2e12-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'img_height'
    },
    {'key': '{af4b2e11-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'img_width'
    },
    {'key': '{af4b2e0f-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'dpi',
    },
    {'key': '{418a4c92-fe63-11d3-9030-0080ad80c556}',
     'short': 'longitude',
    },
    {'key': '{418a4c91-fe63-11d3-9030-0080ad80c556}',
     'short': 'latitude',
    },
    {'key': '{ca6903b8-c3c4-47fa-9592-2d486d766ce0}',
     'short': 'actorname',
    },
    {'key': '{38664623-b081-4251-8c1c-383ef57ac54d}',
     'short': 'locationnote'
    },
    {'key': '{a02dd229-5aa8-4bb4-825a-038cf21f92bd}',
     'short': 'year'
    },
    {'key': '{76f2cca6-3652-40ee-b198-5d0afe931caa}',
     'short': 'assetno'
    },
    {'key': '{59ac5106-a3b4-4152-8647-66cebcb6af48}',
     'short': 'creation_time_from'
    },
    {'key': '{eaf2f030-2bfb-4d75-98ab-fb9bd33affcc}',
     'short': 'creation_time_to'
    },
    {'key': '{65f668d1-b070-494f-a9a3-7b6c3d370e65}',
     'short': 'acceptance_time_from'
    },
    {'key': '{0db7de9e-37fa-47a5-b087-0b1e6066ee86}',
     'short': 'acceptance_time_to'
    },
    {'key': '{1cb94319-9e1f-428e-993f-a78c78947f60}',
     'short': 'timenote'
    },
    {'key': '{8e820869-e338-4761-aa88-3dcd89f8cbae}',
     'short': 'archiveno'
    },
    {'key': '{d6025301-ef5e-4722-b101-78fd28262a98}',
     'short': 'topno'
    },
    {'key': '{8ac71e5d-5ca9-42cf-8c21-02c41e1af5cd}',
     'short': 'archivename'
    },
    {'key': '{9aef60d3-38b4-4e47-8104-d018b83ea6c6}',
     'short': 'unitno'
    },
    {'key': '{51b661a3-585e-4dbd-9e98-85e12ad91190}',
     'short': 'place'
    },
    {'key': '{24252838-de09-46e4-8f7d-f3fb85cde78f}',
     'short': 'city'
    },
    {'key': '{6fb1f61b-14a3-4851-bba0-cf7e71ef59cb}',
     'short': 'short_title'
    },
    {'key': '{af4b2e0c-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'categories'
    },
    {'key': '{659ca0c6-92d8-4f7a-af58-8a3c69df2ede}',
     'short': 'street'
    },
    {'key': '{ed64a9f8-8d19-4f60-a615-5bd3b188e98a}',
     'short': 'address'
    },
    {'key': '{5bbe594f-d029-4bb5-9c42-8f1ac8c94b7f}',
     'short': 'zipcode'
    },
    {'key': '{af4b2e02-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'modification_time'
    },
    {'key': '{a493be21-0f70-4cae-9394-703eca848bad}',
     'short': 'review_state'
    }
];

exports.result_mapping = result_mapping;

function format_result(fields) {
    var result = {};

    for(var i=0; i < result_mapping.length; ++i) {
        if(result_mapping[i].key in fields) {
            result[result_mapping[i].short] = fields[result_mapping[i].key];
        }
    }

    return result;
}

exports.format_result = format_result;

<commit_msg>Implementing a function to extend metadata from a master asset.<commit_after>var cip = require('./cip-methods.js');
var Q = require('q');

var result_mapping = [
    {'key': 'id',
     'short': 'id'},
    {'key': '{af4b2e00-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'filename'
    },
    {'key': '{f5d1dcd8-c553-4346-8d4d-672c85bb59be}',
     'short': 'license',
    },
    {'key': '{9b071045-118c-4f42-afa1-c3121783ac66}',
     'short': 'creator',
    },
    {'key': '{af4b2e2c-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'copyright',
    },
    {'key': '{2ce9c8eb-b83d-4a91-9d09-2141cac7de12}',
     'short': 'description',
    },
    {'key': '{af4b2e12-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'img_height'
    },
    {'key': '{af4b2e11-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'img_width'
    },
    {'key': '{af4b2e0f-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'dpi',
    },
    {'key': '{418a4c92-fe63-11d3-9030-0080ad80c556}',
     'short': 'longitude',
    },
    {'key': '{418a4c91-fe63-11d3-9030-0080ad80c556}',
     'short': 'latitude',
    },
    {'key': '{ca6903b8-c3c4-47fa-9592-2d486d766ce0}',
     'short': 'actorname',
    },
    {'key': '{38664623-b081-4251-8c1c-383ef57ac54d}',
     'short': 'locationnote'
    },
    {'key': '{a02dd229-5aa8-4bb4-825a-038cf21f92bd}',
     'short': 'year'
    },
    {'key': '{76f2cca6-3652-40ee-b198-5d0afe931caa}',
     'short': 'assetno'
    },
    {'key': '{59ac5106-a3b4-4152-8647-66cebcb6af48}',
     'short': 'creation_time_from'
    },
    {'key': '{eaf2f030-2bfb-4d75-98ab-fb9bd33affcc}',
     'short': 'creation_time_to'
    },
    {'key': '{65f668d1-b070-494f-a9a3-7b6c3d370e65}',
     'short': 'acceptance_time_from'
    },
    {'key': '{0db7de9e-37fa-47a5-b087-0b1e6066ee86}',
     'short': 'acceptance_time_to'
    },
    {'key': '{1cb94319-9e1f-428e-993f-a78c78947f60}',
     'short': 'timenote'
    },
    {'key': '{8e820869-e338-4761-aa88-3dcd89f8cbae}',
     'short': 'archiveno'
    },
    {'key': '{d6025301-ef5e-4722-b101-78fd28262a98}',
     'short': 'topno'
    },
    {'key': '{8ac71e5d-5ca9-42cf-8c21-02c41e1af5cd}',
     'short': 'archivename'
    },
    {'key': '{9aef60d3-38b4-4e47-8104-d018b83ea6c6}',
     'short': 'unitno'
    },
    {'key': '{51b661a3-585e-4dbd-9e98-85e12ad91190}',
     'short': 'place'
    },
    {'key': '{24252838-de09-46e4-8f7d-f3fb85cde78f}',
     'short': 'city'
    },
    {'key': '{6fb1f61b-14a3-4851-bba0-cf7e71ef59cb}',
     'short': 'short_title'
    },
    {'key': '{af4b2e0c-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'categories'
    },
    {'key': '{659ca0c6-92d8-4f7a-af58-8a3c69df2ede}',
     'short': 'street'
    },
    {'key': '{ed64a9f8-8d19-4f60-a615-5bd3b188e98a}',
     'short': 'address'
    },
    {'key': '{5bbe594f-d029-4bb5-9c42-8f1ac8c94b7f}',
     'short': 'zipcode'
    },
    {'key': '{af4b2e02-5f6a-11d2-8f20-0000c0e166dc}',
     'short': 'modification_time'
    },
    {'key': '{a493be21-0f70-4cae-9394-703eca848bad}',
     'short': 'review_state'
    }
];

exports.result_mapping = result_mapping;

function format_result(fields) {
    var result = {};

    for(var i=0; i < result_mapping.length; ++i) {
        if(result_mapping[i].key in fields) {
            result[result_mapping[i].short] = fields[result_mapping[i].key];
        }
    }

    return result;
}

exports.format_result = format_result;

function extend_metadata(nm, catalog_alias, asset, formatted_item) {
    return Q.Promise(function(resolve, reject) {
        // Get the asset's relations, to check for a master.
        // Cosider if this promise chain can be cleaned up.
        cip.get_related_assets(asset, "iscontainedin").then(function(iscontainedin_relations) {
            cip.get_related_assets(asset, "isvariantof").then(function(isvariantof_relations) {
                cip.get_related_assets(asset, "isalternateof").then(function(isalternateof_relations) {
                    var relation_ids = [].concat(
                        iscontainedin_relations.ids,
                        isvariantof_relations.ids,
                        isalternateof_relations.ids
                    );

                    if(relation_ids.length == 1) {
                        var master_asset_id = relation_ids[0];
                        // Let's only consider the case were we have a single master asset.
                        cip.get_asset(nm, catalog_alias, master_asset_id)
                        .then(function(assets) {
                            if(assets.length === 1) {
                                var master_asset = assets[0];
                                var result = format_result(master_asset.fields);
                                // Overwrite where the asset has a value.
                                for(var f in formatted_item) {
                                    var overwriting_value = formatted_item[f];
                                    if(overwriting_value !== null && overwriting_value !== "") {
                                        console.log("Overwriting", f, "with", overwriting_value);
                                        result[f] = overwriting_value;
                                    }
                                }
                                console.log("Resolving", result);
                                resolve( result );
                            } else {
                                reject( new Error("The asset referenced less or more than a single asset.") );
                            }
                        });
                    } else {
                        resolve( fields );
                    }
                });
            });
        });
        // Get the master's metadata
        // Overwrite the temporary copy of the masters metadata with the content of
        // fields, and return the new extended metadata.
    });
}

exports.extend_metadata = extend_metadata;
<|endoftext|>


<commit_before>(function() {
    'use strict';

    var client;

    angular.module('chaise.viewer', ['ERMrest', 'ngSanitize', 'chaise.filters', 'ui.select'])

    // Configure the context info from the URI
    .config(['context', function configureContext(context) {
        if (chaiseConfig.headTitle !== undefined) {
            document.getElementsByTagName('head')[0].getElementsByTagName('title')[0].innerHTML = chaiseConfig.headTitle;
        }

        // TODO: Style guide preferences?
        context.serviceURL = window.location.origin + '/ermrest';

        if (chaiseConfig.ermrestLocation) {
            context.serviceURL = chaiseConfig.ermrestLocation;
        }

        var hash = window.location.hash;

        if (hash === undefined || hash == '' || hash.length == 1) {
            return;
        }

        var parts = hash.substring(1).split('/');
        context.catalogID = parts[0];
        if (parts[1]) {
            var params = parts[1].split(':');
            if (params.length > 1) {
                context.schemaName = params[0];
                context.tableName = params[1];
            } else {
                context.tableName = params[0];
            }
        }
        if (parts[2]) {
            params = parts[2].split('=');
            if (params.length > 1) {
                context.imageID = params[1];
            }
        }

        // TODO: Check if context has everything it needs before proceeding. If not, Bad Request
    }])

    // Get a client connection to ERMrest
    // Note: Only Providers and Constants can be dependencies in .config blocks. So
    // if you want to use a factory or service (e.g. $window or your custom one)
    // in a .config block, you add append 'Provider' to the dependency name and
    // run .$get() on it. This returns a Provider instance of the factory/service.
    .config(['ermrestClientFactoryProvider', 'context', function configureClient(ermrestClientFactoryProvider, context) {
        client = ermrestClientFactoryProvider.$get().getClient(context.serviceURL);
    }])

    // Set user info
    .config(['userProvider', 'context', function configureUser(userProvider, context) {
        client.getSession().then(function success(session) {
            console.log('Session: ', session);
            var groups = context.groups;
            var attributes = session.attributes;
            var user = userProvider.$get();

            // Support new and old webauthn!
            user.name = session.client;

            if (attributes.indexOf(groups.curators) > -1) {
                return user.role = 'curator';
            } else if (attributes.indexOf(groups.annotators) > -1) {
                return user.role = 'annotator';
            } else if (attributes.indexOf(groups.users) > -1) {
                return user.role = 'user';
            } else {
                user.role = null;
            }
            console.log('User: ', user);
        }, function error(response) {
            // TODO: Abstract this away..
            if (response.status == 401 || response.status == 404) {
                if (chaiseConfig.authnProvider == 'goauth') {
                    // TODO: Is it worth injecting $window here?
                    getGoauth(encodeSafeURIComponent(window.location.href));
                }
                console.log(response);
                throw response;
            }
        });

        function getGoauth(referrer) {
            var url = '/ermrest/authn/preauth?referrer=' + referrer;
            // Inject $http service
            var $http = angular.injector(['ng']).get('$http');
            $http.get(url).then(function success(response) {
                console.log('Success: ', response);
                window.open(response.data.redirect_url, '_self');
            }, function error(response) {
                console.log('Error: ', response);
                throw response;
            });
        }

        function encodeSafeURIComponent (str) {
            return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
                return '%' + c.charCodeAt(0).toString(16).toUpperCase();
            });
        }
    }])

    // Hydrate values providers and set up iframe
    .run(['$window', 'context', 'image', 'annotations', 'comments', 'sections', 'anatomies', 'statuses', 'vocabs', 'user', function runApp($window, context, image, annotations, comments, sections, anatomies, statuses, vocabs) {
        var origin = $window.location.origin;
        var iframe = $window.frames[0];
        var annotoriousReady = false;

        var catalog = client.getCatalog(context.catalogID);
        catalog.introspect().then(function success(schemas) {
            console.log('Schemas: ', schemas);
            var schema = schemas[context.schemaName];
            if (schema) {
                var table = schema.getTable(context.tableName);
                var filteredTable = table.getFilteredTable(['id=' + context.imageID]);
                if (filteredTable) {
                    filteredTable.getEntities().then(function success(_entities) {
                        image.entity = _entities[0];
                        iframe.location.replace(image.entity.data.uri);
                        console.log('Image: ', image);

                        var sectionTable = image.entity.getRelatedTable(context.schemaName, 'section_annotation');
                        sectionTable.getEntities().then(function success(_sections) {
                            var length = _sections.length;
                            for (var i = 0; i < length; i++) {
                                sections.push(_sections[i]);
                            }
                            if (annotoriousReady) {
                                iframe.postMessage({messageType: 'loadAnnotations', content: sections}, origin);
                            }
                            console.log('Sections: ', sections);
                        }, function error(response) {
                            throw response;
                        });

                        var annotationTable = image.entity.getRelatedTable(context.schemaName, 'annotation');
                        annotationTable.getEntities().then(function success(_annotations) {
                            var length = _annotations.length;
                            for (var i = 0; i < length; i++) {
                                annotations.push(_annotations[i]);
                            }

                            if (annotoriousReady) {
                                iframe.postMessage({messageType: 'loadAnnotations', content: annotations}, origin);
                            }
                            console.log('Annotations: ', annotations);
                        }, function error(response) {
                            throw response;
                        });

                        // Get all the comments for this image
                        var commentTable = annotationTable.getRelatedTable(context.schemaName, 'annotation_comment');
                        commentTable.getEntities().then(function success(_comments) {
                            var length = _comments.length;
                            for (var i = 0; i < length; i++) {
                                var annotationId = _comments[i].data.annotation_id;
                                if (!comments[annotationId]) {
                                    comments[annotationId] = [];
                                }
                                comments[annotationId].push(_comments[i]);
                            }
                            console.log('Comments: ', comments);
                        }, function error(response) {
                            console.log(response);
                        });
                    }, function error(response) {
                        throw response;
                    });
                }

                // Get all rows from "anatomy" table
                var anatomyTable = schema.getTable('anatomy');
                anatomyTable.getEntities().then(function success(_anatomies) {
                    anatomies.push('No Anatomy');
                    var length = _anatomies.length;
                    for (var j = 0; j < length; j++) {
                        anatomies.push(_anatomies[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "image_grade_code" table.
                var statusTable = schema.getTable('image_grade_code');
                statusTable.getEntities().then(function success(_statuses) {
                    var length = _statuses.length;
                    for (var j = 0; j < length; j++) {
                        statuses.push(_statuses[j].data.code);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "tissues" table
                var tissueTable = schema.getTable('tissue');
                tissueTable.getEntities().then(function success(_tissues) {
                    var length = _tissues.length;
                    vocabs['tissue'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['tissue'].push(_tissues[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "age stage" table
                var ageStageTable = schema.getTable('age_stage');
                ageStageTable.getEntities().then(function success(_stages) {
                    var length = _stages.length;
                    vocabs['age_stage'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['age_stage'].push(_stages[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "gender" table
                var genderTable = schema.getTable('gender');
                genderTable.getEntities().then(function success(_genders) {
                    var length = _genders.length;
                    vocabs['gender'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['gender'].push(_genders[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "specimen_fixation" table
                var specimenFixationTable = schema.getTable('specimen_fixation');
                specimenFixationTable.getEntities().then(function success(_fixations) {
                    var length = _fixations.length;
                    vocabs['specimen_fixation'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['specimen_fixation'].push(_fixations[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "embedding_medium" table
                var embeddingMediumTable = schema.getTable('embedding_medium');
                embeddingMediumTable.getEntities().then(function success(_media) {
                    var length = _media.length;
                    vocabs['embedding_medium'] = [];
                    for (var j = 0; j < _media.length; j++) {
                        vocabs['embedding_medium'].push(_media[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "staining_protocol" table
                var stainingProtocolTable = schema.getTable('staining_protocol');
                stainingProtocolTable.getEntities().then(function success(_protocols) {
                    var length = _protocols.length;
                    vocabs['staining_protocol'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['staining_protocol'].push(_protocols[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });
            }
        }, function error(response) {
            console.log(response);
        });

        // Set up a listener for all "message" events
        $window.addEventListener('message', function(event) {
            if (event.origin === origin) {
                if (event.data.messageType == 'annotoriousReady') {
                    annotoriousReady = event.data.content;
                    if (annotoriousReady) {
                        iframe.postMessage({messageType: 'loadSpecialAnnotations', content: sections}, origin);
                        iframe.postMessage({messageType: 'loadAnnotations', content: annotations}, origin);
                    }
                }
            } else {
                console.log('Invalid event origin. Event origin: ', origin, '. Expected origin: ', window.location.origin);
            }
        });

        // Initialize Bootstrap tooltips
        $(document).ready(function(){
            $('[data-toggle="tooltip"]').tooltip({
                placement: 'bottom',
                container: 'body',
                html: true
            });
        });
    }]);

    // Refresh the page when the window's hash changes. Needed because Angular
    // normally doesn't refresh page when hash changes.
    window.onhashchange = function() {
        if (window.location.hash != '#undefined') {
            location.reload();
        } else {
            history.replaceState("", document.title, window.location.pathname);
            location.reload();
        }
        function goBack() {
            window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
            window.location.lasthash.pop();
        }
    }
})();
<commit_msg>Load existing arrow annotations<commit_after>(function() {
    'use strict';

    var client;

    angular.module('chaise.viewer', ['ERMrest', 'ngSanitize', 'chaise.filters', 'ui.select'])

    // Configure the context info from the URI
    .config(['context', function configureContext(context) {
        if (chaiseConfig.headTitle !== undefined) {
            document.getElementsByTagName('head')[0].getElementsByTagName('title')[0].innerHTML = chaiseConfig.headTitle;
        }

        // TODO: Style guide preferences?
        context.serviceURL = window.location.origin + '/ermrest';

        if (chaiseConfig.ermrestLocation) {
            context.serviceURL = chaiseConfig.ermrestLocation;
        }

        var hash = window.location.hash;

        if (hash === undefined || hash == '' || hash.length == 1) {
            return;
        }

        var parts = hash.substring(1).split('/');
        context.catalogID = parts[0];
        if (parts[1]) {
            var params = parts[1].split(':');
            if (params.length > 1) {
                context.schemaName = params[0];
                context.tableName = params[1];
            } else {
                context.tableName = params[0];
            }
        }
        if (parts[2]) {
            params = parts[2].split('=');
            if (params.length > 1) {
                context.imageID = params[1];
            }
        }

        // TODO: Check if context has everything it needs before proceeding. If not, Bad Request
    }])

    // Get a client connection to ERMrest
    // Note: Only Providers and Constants can be dependencies in .config blocks. So
    // if you want to use a factory or service (e.g. $window or your custom one)
    // in a .config block, you add append 'Provider' to the dependency name and
    // run .$get() on it. This returns a Provider instance of the factory/service.
    .config(['ermrestClientFactoryProvider', 'context', function configureClient(ermrestClientFactoryProvider, context) {
        client = ermrestClientFactoryProvider.$get().getClient(context.serviceURL);
    }])

    // Set user info
    .config(['userProvider', 'context', function configureUser(userProvider, context) {
        client.getSession().then(function success(session) {
            console.log('Session: ', session);
            var groups = context.groups;
            var attributes = session.attributes;
            var user = userProvider.$get();

            // Support new and old webauthn!
            user.name = session.client;

            if (attributes.indexOf(groups.curators) > -1) {
                return user.role = 'curator';
            } else if (attributes.indexOf(groups.annotators) > -1) {
                return user.role = 'annotator';
            } else if (attributes.indexOf(groups.users) > -1) {
                return user.role = 'user';
            } else {
                user.role = null;
            }
            console.log('User: ', user);
        }, function error(response) {
            // TODO: Abstract this away..
            if (response.status == 401 || response.status == 404) {
                if (chaiseConfig.authnProvider == 'goauth') {
                    // TODO: Is it worth injecting $window here?
                    getGoauth(encodeSafeURIComponent(window.location.href));
                }
                console.log(response);
                throw response;
            }
        });

        function getGoauth(referrer) {
            var url = '/ermrest/authn/preauth?referrer=' + referrer;
            // Inject $http service
            var $http = angular.injector(['ng']).get('$http');
            $http.get(url).then(function success(response) {
                console.log('Success: ', response);
                window.open(response.data.redirect_url, '_self');
            }, function error(response) {
                console.log('Error: ', response);
                throw response;
            });
        }

        function encodeSafeURIComponent (str) {
            return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
                return '%' + c.charCodeAt(0).toString(16).toUpperCase();
            });
        }
    }])

    // Hydrate values providers and set up iframe
    .run(['$window', 'context', 'image', 'annotations', 'comments', 'sections', 'anatomies', 'statuses', 'vocabs', 'user', function runApp($window, context, image, annotations, comments, sections, anatomies, statuses, vocabs) {
        var origin = $window.location.origin;
        var iframe = $window.frames[0];
        var annotoriousReady = false;
        var arrows = [];
        var rectangles = [];

        var catalog = client.getCatalog(context.catalogID);
        catalog.introspect().then(function success(schemas) {
            console.log('Schemas: ', schemas);
            var schema = schemas[context.schemaName];
            if (schema) {
                var table = schema.getTable(context.tableName);
                var filteredTable = table.getFilteredTable(['id=' + context.imageID]);
                if (filteredTable) {
                    filteredTable.getEntities().then(function success(_entities) {
                        image.entity = _entities[0];
                        iframe.location.replace(image.entity.data.uri);
                        console.log('Image: ', image);

                        var sectionTable = image.entity.getRelatedTable(context.schemaName, 'section_annotation');
                        sectionTable.getEntities().then(function success(_sections) {
                            var length = _sections.length;
                            for (var i = 0; i < length; i++) {
                                sections.push(_sections[i]);
                            }
                            if (annotoriousReady) {
                                iframe.postMessage({messageType: 'loadSpecialAnnotations', content: sections}, origin);
                            }
                            console.log('Sections: ', sections);
                        }, function error(response) {
                            throw response;
                        });

                        var annotationTable = image.entity.getRelatedTable(context.schemaName, 'annotation');
                        annotationTable.getEntities().then(function success(_annotations) {
                            var length = _annotations.length;
                            for (var i = 0; i < length; i++) {
                                var annotation = _annotations[i];
                                annotations.push(annotation);
                                if (annotation.data.type == 'arrow') {
                                    arrows.push(annotation);
                                } else if (annotation.data.type == 'rectangle') {
                                    console.log('Rect: ', annotation);
                                    rectangles.push(annotation);
                                }
                            }

                            if (annotoriousReady) {
                                iframe.postMessage({messageType: 'loadArrowAnnotations', content: arrows}, origin);
                                iframe.postMessage({messageType: 'loadAnnotations', content: rectangles}, origin);
                            }
                            console.log('Annotations: ', annotations);
                        }, function error(response) {
                            throw response;
                        });

                        // Get all the comments for this image
                        var commentTable = annotationTable.getRelatedTable(context.schemaName, 'annotation_comment');
                        commentTable.getEntities().then(function success(_comments) {
                            var length = _comments.length;
                            for (var i = 0; i < length; i++) {
                                var annotationId = _comments[i].data.annotation_id;
                                if (!comments[annotationId]) {
                                    comments[annotationId] = [];
                                }
                                comments[annotationId].push(_comments[i]);
                            }
                            console.log('Comments: ', comments);
                        }, function error(response) {
                            console.log(response);
                        });
                    }, function error(response) {
                        throw response;
                    });
                }

                // Get all rows from "anatomy" table
                var anatomyTable = schema.getTable('anatomy');
                anatomyTable.getEntities().then(function success(_anatomies) {
                    anatomies.push('No Anatomy');
                    var length = _anatomies.length;
                    for (var j = 0; j < length; j++) {
                        anatomies.push(_anatomies[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "image_grade_code" table.
                var statusTable = schema.getTable('image_grade_code');
                statusTable.getEntities().then(function success(_statuses) {
                    var length = _statuses.length;
                    for (var j = 0; j < length; j++) {
                        statuses.push(_statuses[j].data.code);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "tissues" table
                var tissueTable = schema.getTable('tissue');
                tissueTable.getEntities().then(function success(_tissues) {
                    var length = _tissues.length;
                    vocabs['tissue'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['tissue'].push(_tissues[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "age stage" table
                var ageStageTable = schema.getTable('age_stage');
                ageStageTable.getEntities().then(function success(_stages) {
                    var length = _stages.length;
                    vocabs['age_stage'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['age_stage'].push(_stages[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "gender" table
                var genderTable = schema.getTable('gender');
                genderTable.getEntities().then(function success(_genders) {
                    var length = _genders.length;
                    vocabs['gender'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['gender'].push(_genders[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "specimen_fixation" table
                var specimenFixationTable = schema.getTable('specimen_fixation');
                specimenFixationTable.getEntities().then(function success(_fixations) {
                    var length = _fixations.length;
                    vocabs['specimen_fixation'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['specimen_fixation'].push(_fixations[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "embedding_medium" table
                var embeddingMediumTable = schema.getTable('embedding_medium');
                embeddingMediumTable.getEntities().then(function success(_media) {
                    var length = _media.length;
                    vocabs['embedding_medium'] = [];
                    for (var j = 0; j < _media.length; j++) {
                        vocabs['embedding_medium'].push(_media[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });

                // Get all rows from "staining_protocol" table
                var stainingProtocolTable = schema.getTable('staining_protocol');
                stainingProtocolTable.getEntities().then(function success(_protocols) {
                    var length = _protocols.length;
                    vocabs['staining_protocol'] = [];
                    for (var j = 0; j < length; j++) {
                        vocabs['staining_protocol'].push(_protocols[j].data.term);
                    }
                }, function error(response) {
                    throw response;
                });
            }
        }, function error(response) {
            console.log(response);
        });

        // Set up a listener for all "message" events
        $window.addEventListener('message', function(event) {
            if (event.origin === origin) {
                if (event.data.messageType == 'annotoriousReady') {
                    annotoriousReady = event.data.content;
                    if (annotoriousReady) {
                        iframe.postMessage({messageType: 'loadSpecialAnnotations', content: sections}, origin);
                        iframe.postMessage({messageType: 'loadArrowAnnotations', content: arrows}, origin);
                        iframe.postMessage({messageType: 'loadAnnotations', content: rectangles}, origin);
                    }
                }
            } else {
                console.log('Invalid event origin. Event origin: ', origin, '. Expected origin: ', window.location.origin);
            }
        });

        // Initialize Bootstrap tooltips
        $(document).ready(function(){
            $('[data-toggle="tooltip"]').tooltip({
                placement: 'bottom',
                container: 'body',
                html: true
            });
        });
    }]);

    // Refresh the page when the window's hash changes. Needed because Angular
    // normally doesn't refresh page when hash changes.
    window.onhashchange = function() {
        if (window.location.hash != '#undefined') {
            location.reload();
        } else {
            history.replaceState("", document.title, window.location.pathname);
            location.reload();
        }
        function goBack() {
            window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
            window.location.lasthash.pop();
        }
    }
})();
<|endoftext|>


<commit_before>var convert = require('color-convert');
var EventEmitter = require('events');
var merge = require('merge');
var util = require('util');
var workerproxy = require('workerproxy');

var ResourceLoader = require('./resourceloader');

module.exports = AssetsManager;

function AssetsManager(opt_options) {
  EventEmitter.call(this);

  var options = {
    workerPath: __dirname + '/../worker.js'
  };

  Object.seal(options);
  merge(options, opt_options);
  Object.freeze(options);

  this.options = options;

  var worker = new Worker(options.workerPath);
  this.api = workerproxy(worker);

  this._emitting = {};
  this._imageCache = Object.create(null);
}
util.inherits(AssetsManager, EventEmitter);

/**
 * Indexes a directory. All files in the directory will be reachable through
 * the assets database after this completes. All .pak/.modpak files will also
 * be loaded into the index.
 *
 * The virtual path argument is a prefix for the entries in the directory.
 */
AssetsManager.prototype.addDirectory = function (path, dirEntry, callback) {
  var self = this;

  var pending = 1;
  var decrementPending = function () {
    pending--;
    if (!pending) {
      callback(null);
    }
  };

  var reader = dirEntry.createReader();
  var next = function () {
    reader.readEntries(function (entries) {
      if (!entries.length) {
        process.nextTick(decrementPending);
        return;
      }

      entries.forEach(function (entry) {
        if (entry.name[0] == '.') return;

        var entryPath = path + '/' + entry.name;

        if (entry.isDirectory) {
          pending++;
          self.addDirectory(entryPath, entry, decrementPending);
        } else {
          pending++;
          entry.file(function (file) {
            self.addFile(entryPath, file, decrementPending);
          }, decrementPending);
        }
      });
      next();
    });
  };
  next();
};

AssetsManager.prototype.addFile = function (path, file, callback) {
  this.api.addFile(path, file, callback);
};

AssetsManager.prototype.addRoot = function (dirEntry, callback) {
  this.addDirectory('', dirEntry, callback);
};

AssetsManager.prototype.emitOncePerTick = function (event) {
  if (this._emitting[event]) return;
  this._emitting[event] = true;

  var self = this, args = Array.prototype.slice.call(arguments);
  process.nextTick(function () {
    self.emit.apply(self, args);
    delete self._emitting[event];
  });
};

AssetsManager.prototype.getBlobURL = function (path, callback) {
  this.api.getBlobURL(path, callback);
};

/**
 * Gets the image for the specified path. This function is synchronous, but may
 * depend on asynchronous operations. If the image is not immediately available
 * this function will return null. Once more images are available, an "images"
 * event will be emitted.
 */
AssetsManager.prototype.getImage = function (path) {
  // Example path: "/directory/image.png:frame?hueshift=60?fade=ffffff=0.1"
  if (path in this._imageCache) return this._imageCache[path];

  // Extract image operations.
  var ops = path.split('?');
  // Extract the path (and potentially frame) segment.
  var pathAndFrame = ops.shift().split(':');
  // Get the plain path to the image file.
  var filePath = pathAndFrame[0];

  // If the image is not in the cache, load it and trigger an "images" event
  // when it's done.
  if (!(filePath in this._imageCache)) {
    this._imageCache[filePath] = null;

    var self = this;
    this.getBlobURL(filePath, function (err, url) {
      if (err) {
        console.warn('Failed to load %s (%s)', filePath, err.message);
        return;
      }

      var image = new Image();
      image.src = url;
      image.onload = function () {
        self._imageCache[filePath] = image;
        self.emitOncePerTick('images');
      };
    });
  }

  var image = this._imageCache[filePath];
  if (!image) return null;

  // Apply operations (such as hue shift) on the image.
  var canvas = document.createElement('canvas');
  canvas.width = image.width;
  canvas.height = image.height;

  var context = canvas.getContext('2d');
  context.drawImage(image, 0, 0, image.width, image.height);

  // Parse all the operations to be applied to the image.
  // TODO: addmask, brightness, fade, replace, saturation
  var hue = 0;
  for (var i = 0; i < ops.length; i++) {
    var op = ops[i].split(/[=;]/);
    switch (op[0]) {
      case 'hueshift':
        hue = parseFloat(op[1]);
        break;
      default:
        console.warn('Unsupported image operation:', op);
    }
  }

  var imageData = context.getImageData(0, 0, image.width, image.height),
      data = imageData.data;
  for (var i = 0; i < data.length; i += 4) {
    hsv = convert.rgb2hsv(data[i], data[i + 1], data[i + 2]);

    if (hue) {
      hsv[0] += hue;
      if (hsv[0] < 0) hsv[0] += 360
      else if (hsv[0] >= 360) hsv[0] -= 360;
    }

    rgb = convert.hsv2rgb(hsv);

    data[i] = rgb[0];
    data[i + 1] = rgb[1];
    data[i + 2] = rgb[2];
  }
  context.putImageData(imageData, 0, 0);

  // Create a new object for the modified image and cache it.
  image = new Image();
  image.src = canvas.toDataURL();
  this._imageCache[path] = image;

  return image;
};

AssetsManager.prototype.getResourceLoader = function (extension) {
  return new ResourceLoader(this, extension);
};

AssetsManager.prototype.getResourcePath = function (resource, path) {
  if (path[0] == '/') return path;
  var base = resource.__path__;
  return base.substr(0, base.lastIndexOf('/') + 1) + path;
};

AssetsManager.prototype.getTileImage = function (resource, field, opt_hueShift) {
  path = this.getResourcePath(resource, resource[field]);

  // Add hueshift image operation if needed.
  if (opt_hueShift) {
    path += '?hueshift=' + (opt_hueShift / 255 * 360);
  }

  return this.getImage(path);
};

AssetsManager.prototype.loadResources = function (extension, callback) {
  var self = this;
  this.api.loadResources(extension, function (err, resources) {
    callback(err, resources);
    if (!err) {
      self.emitOncePerTick('resources');
    }
  });
};
<commit_msg>Don't try to handle frames in getImage<commit_after>var convert = require('color-convert');
var EventEmitter = require('events');
var merge = require('merge');
var util = require('util');
var workerproxy = require('workerproxy');

var ResourceLoader = require('./resourceloader');

module.exports = AssetsManager;

function AssetsManager(opt_options) {
  EventEmitter.call(this);

  var options = {
    workerPath: __dirname + '/../worker.js'
  };

  Object.seal(options);
  merge(options, opt_options);
  Object.freeze(options);

  this.options = options;

  var worker = new Worker(options.workerPath);
  this.api = workerproxy(worker);

  this._emitting = {};
  this._imageCache = Object.create(null);
}
util.inherits(AssetsManager, EventEmitter);

/**
 * Indexes a directory. All files in the directory will be reachable through
 * the assets database after this completes. All .pak/.modpak files will also
 * be loaded into the index.
 *
 * The virtual path argument is a prefix for the entries in the directory.
 */
AssetsManager.prototype.addDirectory = function (path, dirEntry, callback) {
  var self = this;

  var pending = 1;
  var decrementPending = function () {
    pending--;
    if (!pending) {
      callback(null);
    }
  };

  var reader = dirEntry.createReader();
  var next = function () {
    reader.readEntries(function (entries) {
      if (!entries.length) {
        process.nextTick(decrementPending);
        return;
      }

      entries.forEach(function (entry) {
        if (entry.name[0] == '.') return;

        var entryPath = path + '/' + entry.name;

        if (entry.isDirectory) {
          pending++;
          self.addDirectory(entryPath, entry, decrementPending);
        } else {
          pending++;
          entry.file(function (file) {
            self.addFile(entryPath, file, decrementPending);
          }, decrementPending);
        }
      });
      next();
    });
  };
  next();
};

AssetsManager.prototype.addFile = function (path, file, callback) {
  this.api.addFile(path, file, callback);
};

AssetsManager.prototype.addRoot = function (dirEntry, callback) {
  this.addDirectory('', dirEntry, callback);
};

AssetsManager.prototype.emitOncePerTick = function (event) {
  if (this._emitting[event]) return;
  this._emitting[event] = true;

  var self = this, args = Array.prototype.slice.call(arguments);
  process.nextTick(function () {
    self.emit.apply(self, args);
    delete self._emitting[event];
  });
};

AssetsManager.prototype.getBlobURL = function (path, callback) {
  this.api.getBlobURL(path, callback);
};

/**
 * Gets the image for the specified path. This function is synchronous, but may
 * depend on asynchronous operations. If the image is not immediately available
 * this function will return null. Once more images are available, an "images"
 * event will be emitted.
 */
AssetsManager.prototype.getImage = function (path) {
  // Example path: "/directory/image.png?hueshift=60?fade=ffffff=0.1"
  if (path in this._imageCache) return this._imageCache[path];

  // Extract image operations.
  var ops = path.split('?');
  // Get the plain path to the image file.
  var filePath = ops.shift();

  // If the image is not in the cache, load it and trigger an "images" event
  // when it's done.
  if (!(filePath in this._imageCache)) {
    this._imageCache[filePath] = null;

    var self = this;
    this.getBlobURL(filePath, function (err, url) {
      if (err) {
        console.warn('Failed to load %s (%s)', filePath, err.message);
        return;
      }

      var image = new Image();
      image.src = url;
      image.onload = function () {
        self._imageCache[filePath] = image;
        self.emitOncePerTick('images');
      };
    });
  }

  var image = this._imageCache[filePath];
  if (!image) return null;

  // Apply operations (such as hue shift) on the image.
  var canvas = document.createElement('canvas');
  canvas.width = image.width;
  canvas.height = image.height;

  var context = canvas.getContext('2d');
  context.drawImage(image, 0, 0, image.width, image.height);

  // Parse all the operations to be applied to the image.
  // TODO: addmask, brightness, fade, replace, saturation
  var hue = 0;
  for (var i = 0; i < ops.length; i++) {
    var op = ops[i].split(/[=;]/);
    switch (op[0]) {
      case 'hueshift':
        hue = parseFloat(op[1]);
        break;
      default:
        console.warn('Unsupported image operation:', op);
    }
  }

  var imageData = context.getImageData(0, 0, image.width, image.height),
      data = imageData.data;
  for (var i = 0; i < data.length; i += 4) {
    hsv = convert.rgb2hsv(data[i], data[i + 1], data[i + 2]);

    if (hue) {
      hsv[0] += hue;
      if (hsv[0] < 0) hsv[0] += 360
      else if (hsv[0] >= 360) hsv[0] -= 360;
    }

    rgb = convert.hsv2rgb(hsv);

    data[i] = rgb[0];
    data[i + 1] = rgb[1];
    data[i + 2] = rgb[2];
  }
  context.putImageData(imageData, 0, 0);

  // Create a new object for the modified image and cache it.
  image = new Image();
  image.src = canvas.toDataURL();
  this._imageCache[path] = image;

  return image;
};

AssetsManager.prototype.getResourceLoader = function (extension) {
  return new ResourceLoader(this, extension);
};

AssetsManager.prototype.getResourcePath = function (resource, path) {
  if (path[0] == '/') return path;
  var base = resource.__path__;
  return base.substr(0, base.lastIndexOf('/') + 1) + path;
};

AssetsManager.prototype.getTileImage = function (resource, field, opt_hueShift) {
  path = this.getResourcePath(resource, resource[field]);

  // Add hueshift image operation if needed.
  if (opt_hueShift) {
    path += '?hueshift=' + (opt_hueShift / 255 * 360);
  }

  return this.getImage(path);
};

AssetsManager.prototype.loadResources = function (extension, callback) {
  var self = this;
  this.api.loadResources(extension, function (err, resources) {
    callback(err, resources);
    if (!err) {
      self.emitOncePerTick('resources');
    }
  });
};
<|endoftext|>


<commit_before>/* eslint-disable no-console */
'use strict';
const stripIndent = require('strip-indent');

class FreestyleSourceExtractionAstPlugin {
  constructor(env) {
    this.originalSource = env.contents;
  }

  extractSource(nodes) {
    // nodes should be a contiguous collection of ast nodes
    let startNode = nodes[0];
    let endNode = nodes[nodes.length - 1];
    let start = startNode.loc.start;
    let end = endNode.loc.end;
    let lines = this.originalSource.split('\n').slice(start.line - 1, end.line);
    lines[0] = new Array(start.column).join(' ') + lines[0].slice(start.column);
    lines[lines.length - 1] = lines[lines.length - 1].slice(0, end.column);
    return stripIndent(lines.join('\n'));
  }

  transform(ast) {
    const plugin = this;
    const { builders: b } = this.syntax;
    const visitor = {
      ElementNode(node) {
        if (['FreestyleUsage', 'FreestyleDynamic'].includes(node.tag)) {
          const sourceString = plugin.extractSource(node.children);
          node.attributes.push(b.attr('@source', b.text(sourceString)));
        }
        if (['Freestyle::Usage'].includes(node.tag)) {
          // Note: if the ember-named-blocks-polyfill polyfill AST transform executed before ours, our code will not work
          // Due to a bug in ember, the current way to ensure this is to configure freestyle to run *after* named-blocks-polyfill
          // via the ember-addon section of freestyle's package.json. ¯\_(ツ)_/¯
          let exampleNode = node.children.find((n) => n.tag === ':example');
          if (!exampleNode) {
            exampleNode = node; // not using named blocks
          }
          const sourceString = plugin.extractSource(exampleNode.children);
          node.attributes.push(b.attr('@source', b.text(sourceString)));
        }
      },
      BlockStatement(node) {
        if (
          ['freestyle-usage', 'freestyle-dynamic'].includes(
            node.path.original
          ) &&
          (!node.loc.source ||
            !node.loc.source.includes(
              'templates/components/freestyle-dynamic.hbs'
            ))
        ) {
          const sourceString = plugin.extractSource(node.program.body);
          node.hash.pairs.push(b.pair('source', b.string(sourceString)));
        }
      },
    };

    this.syntax.traverse(ast, visitor);
    return ast;
  }
}

module.exports = FreestyleSourceExtractionAstPlugin;
<commit_msg>fix: Improve handling of named-blocks-polyfill mangled hbs<commit_after>/* eslint-disable no-console */
'use strict';
const stripIndent = require('strip-indent');

function hasNamedBlocksPolyfillSyntax(node) {
  return (
    node.children.length === 1 &&
    node.children[0].path.original === 'if' &&
    node.children[0].params[0].path.original === '-is-named-block-invocation'
  );
}

function extractFromNamedBlocksPolyfillSyntax(node) {
  if (node.children[0].params[0].params[1].value === 'example') {
    return node.children[0].program;
  } else if (
    node.children[0].inverse.body[0].params[0].params[1].value === 'example'
  ) {
    return node.children[0].inverse.body[0].program;
  } else if (
    node.children[0].inverse.body[0].inverse.body[0].params[0].params[1]
      .value == 'example'
  ) {
    return node.children[0].inverse.body[0].inverse.body[0].program;
  }
}

function cleanupNamedBlocksPolyfillSyntax(sourceString) {
  sourceString = sourceString.trim();
  sourceString = sourceString.replace(
    /({{#if \(-is-named-block-invocation __arg0 "(.+?)"\)}})(\s*)(.*?)(\s*){{\/if}}/gs,
    '\n  <:$2>$3$4$5</:$2>\n'
  );
  sourceString = sourceString.replace(
    / @namedBlocksInfo={{hash .+?=0}} as \|__arg0\|/,
    ''
  );
  sourceString = sourceString.replace(/(\s\s+)/g, '\n$1');
  sourceString = sourceString.replace(/ @(\w+)=/g, '\n  @$1=');
  sourceString =
    sourceString +
    `

  <!-- Formatting lost due to named-blocks-polyfill. Fallback re-formatting applied.
       Original source code formatting will be available once the polyfill is no longer needed. -->
  `;
  return sourceString;
}

class FreestyleSourceExtractionAstPlugin {
  constructor(env) {
    this.originalSource = env.contents;
  }

  extractSource(nodes) {
    // nodes should be a contiguous collection of ast nodes
    let startNode = nodes[0];
    let endNode = nodes[nodes.length - 1];
    let start = startNode.loc.start;
    let end = endNode.loc.end;
    let lines = this.originalSource.split('\n').slice(start.line - 1, end.line);
    lines[0] = new Array(start.column).join(' ') + lines[0].slice(start.column);
    lines[lines.length - 1] = lines[lines.length - 1].slice(0, end.column);
    return stripIndent(lines.join('\n'));
  }

  transform(ast) {
    const plugin = this;
    const { builders: b } = this.syntax;
    const visitor = {
      ElementNode(node) {
        if (['FreestyleUsage', 'FreestyleDynamic'].includes(node.tag)) {
          const sourceString = plugin.extractSource(node.children);
          node.attributes.push(b.attr('@source', b.text(sourceString)));
        }
        if (['Freestyle::Usage'].includes(node.tag)) {
          // Note: if the ember-named-blocks-polyfill polyfill AST transform executed before ours, our code will not work
          // Due to a bug in ember, the current way to ensure this is to configure freestyle to run *after* named-blocks-polyfill
          // via the ember-addon section of freestyle's package.json. ¯\_(ツ)_/¯
          let exampleNode = node.children.find((n) => n.tag === ':example');
          let sourceString;
          if (exampleNode) {
            sourceString = plugin.extractSource(exampleNode.children);
          } else if (hasNamedBlocksPolyfillSyntax(node)) {
            // Unfortunately, we may still run after the named-blocks-polyfill when in an Ember Addon, so we do the best we can here.
            exampleNode = extractFromNamedBlocksPolyfillSyntax(node);
            sourceString = plugin.extractSource(exampleNode.body);
            sourceString = cleanupNamedBlocksPolyfillSyntax(sourceString);
          } else {
            exampleNode = node; // not using named blocks
            sourceString = plugin.extractSource(exampleNode.children);
          }
          node.attributes.push(b.attr('@source', b.text(sourceString)));
        }
      },
      BlockStatement(node) {
        if (
          ['freestyle-usage', 'freestyle-dynamic'].includes(
            node.path.original
          ) &&
          (!node.loc.source ||
            !node.loc.source.includes(
              'templates/components/freestyle-dynamic.hbs'
            ))
        ) {
          const sourceString = plugin.extractSource(node.program.body);
          node.hash.pairs.push(b.pair('source', b.string(sourceString)));
        }
      },
    };

    this.syntax.traverse(ast, visitor);
    return ast;
  }
}

module.exports = FreestyleSourceExtractionAstPlugin;
<|endoftext|>


<commit_before>"use strict";
var Scroller = {
  animate: null,
  calibrationBoundary: 150,
  maxCalibration: 1.6,
  minCalibration: 0.5,
  // high:  60f / 1000ms :  400ms / 24f # 600 / 28
  // low:   15f /  250ms :   33ms /  2f # 200 / 6
  maxInterval: 28,
  minDelay: 600,
  performScroll: function(el, di, amount) {
    var before;
    if (di) {
      if (el) {
        before = el.scrollTop;
        el.scrollTop += amount;
        return el.scrollTop !== before;
      } else {
        before = window.scrollY;
        window.scrollBy(0, amount);
        return window.scrollY !== before;
      }
    } else if (el) {
      before = el.scrollLeft;
      el.scrollLeft += amount;
      return el.scrollLeft !== before;
    } else {
      before = window.scrollX;
      window.scrollBy(amount, 0);
      return window.scrollX !== before;
    }
  },
  scroll: function(element, di, amount) {
    if (!amount) { return; }
    if (!Settings.values.smoothScroll) {
      this.performScroll(element, di, amount);
      DomUtils.isVisibile(Scroller.current) || (Scroller.current = element);
      return;
    }
    this.animate(amount, di, element);
  }
};

Scroller = {
  Core: Scroller,
  current: null,
  keyIsDown: 0,
  Properties: [{
    axisName: "scrollLeft",
    max: "scrollWidth",
    viewSize: "clientWidth"
  }, {
    axisName: "scrollTop",
    max: "scrollHeight",
    viewSize: "clientHeight"
  }],
  scrollBy: function(direction, amount, factor, zoomX) {
    var element, di;
    di = direction === "y" ? 1 : 0;
    element = this.findScrollable(this.getActivatedElement(), di, amount, factor);
    amount *= this.getDimension(element, di, factor);
    if (zoomX && element && di === 0 && element.clientWidth <= element.clientHeight * 2) {
      amount = Math.ceil(amount * 0.6);
    }
    this.Core.scroll(element, di, amount);
  },
  scrollTo: function(direction, factor) {
    var amount, element, di = direction === "y" ? 1 : 0;;
    factor >= 0 ? (amount = factor, factor = "") : (amount = 1);
    element = this.findScrollable(this.getActivatedElement(), di, amount, factor);
    amount = amount * this.getDimension(element, di, factor) - (element
      ? element[this.Properties[di].axisName] : di ? window.scrollY : window.scrollX);
    this.Core.scroll(element, di, amount);
  },
  findScrollable: function(element, di, amount, factor) {
    while (element !== document.body && !(this.scrollDo(element, di, amount, factor)
        && this.shouldScroll(element, di))) {
      element = element.parentElement
        || (element.parentNode && element.parentNode.host)
        || document.body;
    }
    return element;
  },
  getActivatedElement: function() {
    var element = this.current;
    if (element) {
      return element;
    }
    element = document.body;
    return element = this.current = element
      ? (this.selectFirst(element) || element) : null;
  },
  getDimension: function(el, di, name) {
    return !name ? 1
      : (!el) ? document.documentElement[this.Properties[di][name]]
      : (name !== "viewSize" || el !== document.body) ? el[this.Properties[di][name]]
      : di ? window.innerHeight : window.innerWidth;
  },
  scrollDo: function(element, di, amount, factor) {
    amount = (amount * this.getDimension(element, di, factor) > 0) ? 1 : -1;
    return this.Core.performScroll(element, di, amount) && this.Core.performScroll(element, di, -amount);
  },
  selectFirst: function(element) {
    if (this.scrollDo(element, 1, 1, "") || this.scrollDo(element, 1, -1, "")) {
      return element;
    }
    DomUtils.prepareCrop();
    var children = [], rect, _ref = element.children, _len = _ref.length;
    while (0 <= --_len) {
      element = _ref[_len];
      if (rect = DomUtils.getVisibleClientRect(element)) {
        children.push([(rect[2] - rect[0]) * (rect[3] - rect[1]), element]);
      }
    }
    if (_len = children.length) {
      children = children.sort(this.sortBy0);
      while (0 <= --_len) {
        if (element = this.selectFirst(children[_len][1])) {
          return element;
        }
      }
    }
    return null;
  },
  shouldScroll: function(element, di) {
    var computedStyle = window.getComputedStyle(element), _ref;
    return (computedStyle.getPropertyValue("display") === "none") //
      || (computedStyle.getPropertyValue(di ? "overflow-y" : "overflow-x") === "hidden") //
      || ((_ref = computedStyle.getPropertyValue("visibility")) === "hidden" || _ref === "collapse") //
      ? false : true;
  },
  sortBy0: function(a, b) {
    return a[0] - b[0];
  }
};

Scroller.Core.animate = function () {
  var amount = 0, calibration = 1.0, di = 0, duration = 0, element = null, //
  sign = 0, timestamp = -1, totalDelta = 0, totalElapsed = 0.0, //
  animate = function(new_timestamp) {
    var int1 = timestamp, elapsed, _this;
    timestamp = new_timestamp;
    if (int1 === -1) {
      requestAnimationFrame(animate);
      return;
    }
    elapsed = new_timestamp - int1;
    int1 = (totalElapsed += elapsed);
    _this = Scroller.Core;
    if (Scroller.keyIsDown) {
      if (int1 >= 75) {
        if (int1 > _this.minDelay) { --Scroller.keyIsDown; }
        int1 = calibration;
        if (_this.minCalibration <= int1 && int1 <= _this.maxCalibration) {
          int1 = _this.calibrationBoundary / amount / int1;
          calibration *= (int1 > 1.05) ? 1.05 : (int1 < 0.95) ? 0.95 : 1.0;
        }
      }
      int1 = Math.ceil(amount * (elapsed / duration) * calibration);
    } else {
      int1 = Math.ceil(amount * (elapsed / duration) * calibration);
      int1 = Math.max(0, Math.min(int1, amount - totalDelta));
    }
    if (int1 && _this.performScroll(element, di, sign * int1)) {
      totalDelta += int1;
      requestAnimationFrame(animate);
    } else if (Scroller.current !== element) {
      DomUtils.isVisibile(Scroller.current) || (Scroller.current = element);
    }
  };
  this.animate = function(new_amount, new_di, new_el) {
    amount = Math.abs(new_amount), calibration = 1.0, di = new_di;
    duration = Math.max(100, 20 * Math.log(amount)), element = new_el;
    sign = new_amount === 0 ? 0 : new_amount < 0 ? -1 : 1;
    timestamp = -1, totalDelta = 0, totalElapsed = 0.0;
    Scroller.keyIsDown = Scroller.Core.maxInterval;
    var keyboard = Settings.values.keyboard;
    this.maxInterval = Math.round(keyboard[1] / 16.67) + 4;
    this.minDelay = (((keyboard[0] - keyboard[1]) / 30) | 0) * 30;
    requestAnimationFrame(animate);
  };
  this.animate.apply(this, arguments);
};
<commit_msg>fix a typo<commit_after>"use strict";
var Scroller = {
  animate: null,
  calibrationBoundary: 150,
  maxCalibration: 1.6,
  minCalibration: 0.5,
  // high:  60f / 1000ms :  400ms / 24f # 600 / 28
  // low:   15f /  250ms :   33ms /  2f # 200 / 6
  maxInterval: 28,
  minDelay: 600,
  performScroll: function(el, di, amount) {
    var before;
    if (di) {
      if (el) {
        before = el.scrollTop;
        el.scrollTop += amount;
        return el.scrollTop !== before;
      } else {
        before = window.scrollY;
        window.scrollBy(0, amount);
        return window.scrollY !== before;
      }
    } else if (el) {
      before = el.scrollLeft;
      el.scrollLeft += amount;
      return el.scrollLeft !== before;
    } else {
      before = window.scrollX;
      window.scrollBy(amount, 0);
      return window.scrollX !== before;
    }
  },
  scroll: function(element, di, amount) {
    if (!amount) { return; }
    if (!Settings.values.smoothScroll) {
      this.performScroll(element, di, amount);
      DomUtils.isVisibile(Scroller.current) || (Scroller.current = element);
      return;
    }
    this.animate(amount, di, element);
  }
};

Scroller = {
  Core: Scroller,
  current: null,
  keyIsDown: 0,
  Properties: [{
    axisName: "scrollLeft",
    max: "scrollWidth",
    viewSize: "clientWidth"
  }, {
    axisName: "scrollTop",
    max: "scrollHeight",
    viewSize: "clientHeight"
  }],
  scrollBy: function(direction, amount, factor, zoomX) {
    var element, di;
    di = direction === "y" ? 1 : 0;
    element = this.findScrollable(this.getActivatedElement(), di, amount, factor);
    amount *= this.getDimension(element, di, factor);
    if (zoomX && element && di === 0 && element.clientWidth <= element.clientHeight * 2) {
      amount = Math.ceil(amount * 0.6);
    }
    this.Core.scroll(element, di, amount);
  },
  scrollTo: function(direction, factor) {
    var amount, element, di = direction === "y" ? 1 : 0;;
    factor >= 0 ? (amount = factor, factor = "") : (amount = 1);
    element = this.findScrollable(this.getActivatedElement(), di, amount, factor);
    amount = amount * this.getDimension(element, di, factor) - (element
      ? element[this.Properties[di].axisName] : di ? window.scrollY : window.scrollX);
    this.Core.scroll(element, di, amount);
  },
  findScrollable: function(element, di, amount, factor) {
    while (element !== document.body && !(this.scrollDo(element, di, amount, factor)
        && this.shouldScroll(element, di))) {
      element = element.parentElement
        || (element.parentNode && element.parentNode.host)
        || document.body;
    }
    return element;
  },
  getActivatedElement: function() {
    var element = this.current;
    if (element) {
      return element;
    }
    element = document.body;
    return element = this.current = element
      ? (this.selectFirst(element) || element) : null;
  },
  getDimension: function(el, di, name) {
    return !name ? 1
      : (!el) ? document.documentElement[this.Properties[di][name]]
      : (name !== "viewSize" || el !== document.body) ? el[this.Properties[di][name]]
      : di ? window.innerHeight : window.innerWidth;
  },
  scrollDo: function(element, di, amount, factor) {
    amount = (amount * this.getDimension(element, di, factor) > 0) ? 1 : -1;
    return this.Core.performScroll(element, di, amount) && this.Core.performScroll(element, di, -amount);
  },
  selectFirst: function(element) {
    if (this.scrollDo(element, 1, 1, "") || this.scrollDo(element, 1, -1, "")) {
      return element;
    }
    DomUtils.prepareCrop();
    var children = [], rect, _ref = element.children, _len = _ref.length;
    while (0 <= --_len) {
      element = _ref[_len];
      if (rect = DomUtils.getVisibleClientRect(element)) {
        children.push([(rect[2] - rect[0]) * (rect[3] - rect[1]), element]);
      }
    }
    if (_len = children.length) {
      children = children.sort(this.sortBy0);
      while (0 <= --_len) {
        if (element = this.selectFirst(children[_len][1])) {
          return element;
        }
      }
    }
    return null;
  },
  shouldScroll: function(element, di) {
    var computedStyle = window.getComputedStyle(element), _ref;
    return (computedStyle.getPropertyValue("display") === "none") //
      || (computedStyle.getPropertyValue(di ? "overflow-y" : "overflow-x") === "hidden") //
      || ((_ref = computedStyle.getPropertyValue("visibility")) === "hidden" || _ref === "collapse") //
      ? false : true;
  },
  sortBy0: function(a, b) {
    return a[0] - b[0];
  }
};

Scroller.Core.animate = function () {
  var amount = 0, calibration = 1.0, di = 0, duration = 0, element = null, //
  sign = 0, timestamp = -1, totalDelta = 0, totalElapsed = 0.0, //
  animate = function(new_timestamp) {
    var int1 = timestamp, elapsed, _this;
    timestamp = new_timestamp;
    if (int1 === -1) {
      requestAnimationFrame(animate);
      return;
    }
    elapsed = new_timestamp - int1;
    int1 = (totalElapsed += elapsed);
    _this = Scroller.Core;
    if (Scroller.keyIsDown) {
      if (int1 >= 75) {
        if (int1 > _this.minDelay) { --Scroller.keyIsDown; }
        int1 = calibration;
        if (_this.minCalibration <= int1 && int1 <= _this.maxCalibration) {
          int1 = _this.calibrationBoundary / amount / int1;
          calibration *= (int1 > 1.05) ? 1.05 : (int1 < 0.95) ? 0.95 : 1.0;
        }
      }
      int1 = Math.ceil(amount * (elapsed / duration) * calibration);
    } else {
      int1 = Math.ceil(amount * (elapsed / duration) * calibration);
      int1 = Math.max(0, Math.min(int1, amount - totalDelta));
    }
    if (int1 && _this.performScroll(element, di, sign * int1)) {
      totalDelta += int1;
      requestAnimationFrame(animate);
    } else if (Scroller.current !== element) {
      DomUtils.isVisibile(Scroller.current) || (Scroller.current = element);
    }
  };
  this.animate = function(new_amount, new_di, new_el) {
    amount = Math.abs(new_amount), calibration = 1.0, di = new_di;
    duration = Math.max(100, 20 * Math.log(amount)), element = new_el;
    sign = new_amount === 0 ? 0 : new_amount < 0 ? -1 : 1;
    timestamp = -1, totalDelta = 0, totalElapsed = 0.0;
    var keyboard = Settings.values.keyboard;
    this.maxInterval = Math.round(keyboard[1] / 16.67) + 4;
    this.minDelay = (((keyboard[0] - keyboard[1]) / 30) | 0) * 30;
    Scroller.keyIsDown = this.maxInterval;
    requestAnimationFrame(animate);
  };
  this.animate.apply(this, arguments);
};
<|endoftext|>