<commit_before>/**
 * Copyright 2015, Yahoo Inc.
 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */
'use strict';

var React = require('react');
var objectAssign = require('object-assign');
var hoistNonReactStatics = require('hoist-non-react-statics');

function createComponent(Component, stores, getStateFromStores, customContextTypes) {
    var componentName = Component.displayName || Component.name;
    var componentContextTypes = objectAssign({
        getStore: React.PropTypes.func.isRequired
    }, customContextTypes);
    var StoreConnector = React.createClass({
        displayName: componentName + 'StoreConnector',
        contextTypes: componentContextTypes,
        getInitialState: function getInitialState() {
            return this.getStateFromStores();
        },
        componentDidMount: function componentDidMount() {
            stores.forEach(function storesEach(Store) {
                this.context.getStore(Store).addChangeListener(this._onStoreChange);
            }, this);
        },
        componentWillUnmount: function componentWillUnmount() {
            stores.forEach(function storesEach(Store) {
                this.context.getStore(Store).removeChangeListener(this._onStoreChange);
            }, this);
        },
        componentWillReceiveProps: function componentWillReceiveProps(nextProps){
            this.setState(this.getStateFromStores(nextProps));
        },
        getStateFromStores: function (props) {
            props = props || this.props;
            if ('function' !== typeof getStateFromStores) {
                //@TODO remove this branch in next minor
                if ('production' !== process.env.NODE_ENV) {
                    console.warn('Using connectToStores with a state getter ' +
                        'map has been deprecated. Please use the new API as ' +
                        'documented at ' +
                        'http://fluxible.io/api/addons/connectToStores.html');
                }
                var state = {};
                Object.keys(getStateFromStores).forEach(function (storeName) {
                    var stateGetter = getStateFromStores[storeName];
                    var store = this.context.getStore(storeName);
                    objectAssign(state, stateGetter(store, props));
                }, this);
                return state;
            }
            // @TODO remove merged context and pass only context to state getter
            //      in next minor version
            var storeInstances = {};
            var context = this.context;
            stores.forEach(function (store) {
                var storeName = store.storeName || store.name || store;
                if ('production' !== process.env.NODE_ENV) {
                    try {
                        Object.defineProperty(storeInstances, storeName, {
                            get: function () {
                                console.warn(componentName + '\'s connectToStores ' +
                                    'state getter is trying to access ' + storeName +
                                    '. connectToStore no longer passes the ' +
                                    'stores to the state getter. The state getter ' +
                                    'signature is now (context, props) and you ' +
                                    'should access the store using ' +
                                    '`context.getStore(' + storeName + ')`. See ' +
                                    'https://github.com/yahoo/fluxible/pull/124 ' +
                                    'for more details on this change.');
                                return context.getStore(store);
                            }
                        });
                    } catch (e) {
                        // https://github.com/es-shims/es5-shim#may-fail
                        // Object.defineProperty will fail on IE8
                        storeInstances[storeName] = context.getStore(store);
                    }
                } else {
                    storeInstances[storeName] = this.context.getStore(store);
                }
            }, this);
            var mergedContext = objectAssign(storeInstances, this.context);
            return getStateFromStores(mergedContext, props);

            // @TODO just do this in next minor version
            //return getStateFromStores(this.context, props);
        },
        _onStoreChange: function onStoreChange() {
            if (this.isMounted()) {
                this.setState(this.getStateFromStores());
            }
        },
        render: function render() {
            return React.createElement(Component, objectAssign({}, this.props, this.state));
        }
    });

    hoistNonReactStatics(StoreConnector, Component);

    return StoreConnector;
}

/**
 * Registers change listeners and retrieves state from stores using the `getStateFromStores`
 * method. Concept provided by Dan Abramov via
 * https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
 *
 * Example:
 *   connectToStores(Component, [FooStore], {
 *       FooStore: function (store, props) {
 *           return {
 *               foo: store.getFoo()
 *           }
 *       }
 *   })
 *
 * Also supports the decorator pattern:
 *   @connectToStores([FooStore],  {
 *       FooStore: function (store, props) {
 *           return {
 *               foo: store.getFoo()
 *           }
 *       }
 *   })
 *   class ConnectedComponent extends React.Component {
 *       render() {
 *           return <div/>;
 *       }
 *   }
 *
 * @method connectToStores
 * @param {React.Component} [Component] component to pass state as props to.
 * @param {array} stores List of stores to listen for changes
 * @param {function} getStateFromStores function that receives all stores and should return
 *      the full state object. Receives `stores` hash and component `props` as arguments
 * @returns {React.Component} or {Function} if using decorator pattern
 */
module.exports = function connectToStores(Component, stores, getStateFromStores) {
    // support decorator pattern
    if (arguments.length === 2) {
        stores = arguments[0];
        getStateFromStores = arguments[1];
        return function connectToStoresDecorator(ComponentToDecorate) {
            return createComponent(ComponentToDecorate, stores, getStateFromStores);
        };
    }

    return createComponent.apply(null, arguments);
};
<commit_msg>make connectToStores work with phantomjs arguments implementation<commit_after>/**
 * Copyright 2015, Yahoo Inc.
 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */
'use strict';

var React = require('react');
var objectAssign = require('object-assign');
var hoistNonReactStatics = require('hoist-non-react-statics');

function createComponent(Component, stores, getStateFromStores, customContextTypes) {
    var componentName = Component.displayName || Component.name;
    var componentContextTypes = objectAssign({
        getStore: React.PropTypes.func.isRequired
    }, customContextTypes);
    var StoreConnector = React.createClass({
        displayName: componentName + 'StoreConnector',
        contextTypes: componentContextTypes,
        getInitialState: function getInitialState() {
            return this.getStateFromStores();
        },
        componentDidMount: function componentDidMount() {
            stores.forEach(function storesEach(Store) {
                this.context.getStore(Store).addChangeListener(this._onStoreChange);
            }, this);
        },
        componentWillUnmount: function componentWillUnmount() {
            stores.forEach(function storesEach(Store) {
                this.context.getStore(Store).removeChangeListener(this._onStoreChange);
            }, this);
        },
        componentWillReceiveProps: function componentWillReceiveProps(nextProps){
            this.setState(this.getStateFromStores(nextProps));
        },
        getStateFromStores: function (props) {
            props = props || this.props;
            if ('function' !== typeof getStateFromStores) {
                //@TODO remove this branch in next minor
                if ('production' !== process.env.NODE_ENV) {
                    console.warn('Using connectToStores with a state getter ' +
                        'map has been deprecated. Please use the new API as ' +
                        'documented at ' +
                        'http://fluxible.io/api/addons/connectToStores.html');
                }
                var state = {};
                Object.keys(getStateFromStores).forEach(function (storeName) {
                    var stateGetter = getStateFromStores[storeName];
                    var store = this.context.getStore(storeName);
                    objectAssign(state, stateGetter(store, props));
                }, this);
                return state;
            }
            // @TODO remove merged context and pass only context to state getter
            //      in next minor version
            var storeInstances = {};
            var context = this.context;
            stores.forEach(function (store) {
                var storeName = store.storeName || store.name || store;
                if ('production' !== process.env.NODE_ENV) {
                    try {
                        Object.defineProperty(storeInstances, storeName, {
                            get: function () {
                                console.warn(componentName + '\'s connectToStores ' +
                                    'state getter is trying to access ' + storeName +
                                    '. connectToStore no longer passes the ' +
                                    'stores to the state getter. The state getter ' +
                                    'signature is now (context, props) and you ' +
                                    'should access the store using ' +
                                    '`context.getStore(' + storeName + ')`. See ' +
                                    'https://github.com/yahoo/fluxible/pull/124 ' +
                                    'for more details on this change.');
                                return context.getStore(store);
                            }
                        });
                    } catch (e) {
                        // https://github.com/es-shims/es5-shim#may-fail
                        // Object.defineProperty will fail on IE8
                        storeInstances[storeName] = context.getStore(store);
                    }
                } else {
                    storeInstances[storeName] = this.context.getStore(store);
                }
            }, this);
            var mergedContext = objectAssign(storeInstances, this.context);
            return getStateFromStores(mergedContext, props);

            // @TODO just do this in next minor version
            //return getStateFromStores(this.context, props);
        },
        _onStoreChange: function onStoreChange() {
            if (this.isMounted()) {
                this.setState(this.getStateFromStores());
            }
        },
        render: function render() {
            return React.createElement(Component, objectAssign({}, this.props, this.state));
        }
    });

    hoistNonReactStatics(StoreConnector, Component);

    return StoreConnector;
}

/**
 * Registers change listeners and retrieves state from stores using the `getStateFromStores`
 * method. Concept provided by Dan Abramov via
 * https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
 *
 * Example:
 *   connectToStores(Component, [FooStore], {
 *       FooStore: function (store, props) {
 *           return {
 *               foo: store.getFoo()
 *           }
 *       }
 *   })
 *
 * Also supports the decorator pattern:
 *   @connectToStores([FooStore],  {
 *       FooStore: function (store, props) {
 *           return {
 *               foo: store.getFoo()
 *           }
 *       }
 *   })
 *   class ConnectedComponent extends React.Component {
 *       render() {
 *           return <div/>;
 *       }
 *   }
 *
 * @method connectToStores
 * @param {React.Component} [Component] component to pass state as props to.
 * @param {array} stores List of stores to listen for changes
 * @param {function} getStateFromStores function that receives all stores and should return
 *      the full state object. Receives `stores` hash and component `props` as arguments
 * @returns {React.Component} or {Function} if using decorator pattern
 */
module.exports = function connectToStores(Component, stores, getStateFromStores) {

    // support decorator pattern
    if (arguments.length === 2) {
        var _stores = Component;
        var _getStateFromStores = stores;
        return function connectToStoresDecorator(ComponentToDecorate) {
            return createComponent(ComponentToDecorate, _stores, _getStateFromStores);
        };
    }

    return createComponent.apply(null, arguments);
};
<|endoftext|>


<commit_before>module.exports = {
  hosturl: 'http://206.253.166.149/',
  // Sessions are valid for only 3 hours
  statstimeout: 3*60*60*1000,
  //refresh stale avatars older than 5 minutes (for testing)
  avatarrefresh: 5*60*1000
};<commit_msg>Oops, this cfg variable is irrelevant now.<commit_after>module.exports = {
  hosturl: 'http://206.253.166.149/',
  // Sessions are valid for only 3 hours
  statstimeout: 3*60*60*1000
};<|endoftext|>


<commit_before>function load_draft_list(page_num,page_size,callback){
	$.getJSON(config['web_root']+"Home/Draft/list_drafts",
		{page_num:page_num,page_size:page_size},function(data){
		var $table = $("#west table")
		// do some clean jobs
		$table.html('')
		$("#go_previous,#go_next").addClass("disabled")
		// create new elements
		for(var i=0;i<data.drafts.length;i++){
			var $del = $("").addClass("btn btn-danger btn-xs pull-right remove_draft")
			.append($("").addClass("glyphicon glyphicon-remove"))
			.attr("data-id",data.drafts[i]['id'])
			var $td = $("")
			var $tr = $("
").attr("data-id",data.drafts[i]['id'])
			$td.text(data.drafts[i]['title'])
			$td.append($del)
			$tr.append($td)
			$table.append($tr)
		}
		draft_list_events_binding($table,data)
		var total_page = Math.ceil(data['total']/page_size)
		$("#current_page").text(data['current'])
		$("#total_page").text(total_page)
		if(data['current']>1){
			$("#go_previous").removeClass("disabled").attr("data-page",data['current']-1)
		}
		if(data['current']<total_page){
			$("#go_next").removeClass("disabled").attr("data-page",data['current']+1)
		}
		if(callback){
			callback(data)
		}
	})
}
function draft_list_events_binding($table,data){
	$table.find("a.remove_draft").click(function(){
		var c = confirm("Are you sure to delete?")
		if(!c){
			return false;
		}
		var id = $(this).attr("data-id")
		$.getJSON(config['web_root']+"Home/Draft/delete_draft",
			{id:id},function(data){
			if(data.result==="ok"){
				$table.find("tr[data-id="+id+"]").remove()
				if(id===$("#update_title").attr("data-id")){
					clear_draft();
				}
			}else{
				alert(data.info)
			}
		})
		return false;
	})
	$table.find("tr").click(function(){
		var id = $(this).attr("data-id")
		load_draft(id)
	})
}
function load_draft(id,callback){
	$.getJSON(config['web_root']+"Home/Draft/load_draft",
	{id:id},function(data){
		$("#title").val(data['title'])
		$("#create_time").text(data['create_time'])
		$("#last_save_time").text(data['last_save_time'])
		$("#update_title").attr("data-id",id)
		editor.setValue(data['content'])
		var result = parse(data['content'])
		$("#ptitle").text(result['title'])
		re_load_thumb(get_thumb_name(data.title)+'.jpg')
		load_images(id)
		load_attachments(id)
		if(callback){
			callback(data)
		}
	})
	function get_thumb_name(title){
		var first_word = title.split("-")[3]
		var begin_idx = title.indexOf(first_word)
		return title.substring(begin_idx)
	}
}
function re_load_thumb(title){
	$("#thumb").attr("src",config['thumb_dir']+title)
}
function parse(content){
	var head = content.split("---")[1]
	var lines = head.split("\n")
	var result = {}
	for(var i=0;i<lines.length;i++){
		var titleIdx = lines[i].indexOf("title:")
		if(titleIdx>=0){
			result['title'] = lines[i].substring(titleIdx+6).trim().split('"')[1]
			continue
		}
		titleIdx = lines[i].indexOf("date:")
		if(titleIdx>=0){
			result['date'] = new Date(
				Date.parse(
					lines[i].substring(titleIdx+5).trim()
					)
				).toDateString()
			continue
		}
		titleIdx = lines[i].indexOf("categories:")
		if(titleIdx>=0){
			result['tags'] = lines[i].substring(titleIdx+11).trim().split(' ')
			continue
		}
	}
	return result
}
function load_images(id,callback){
	$.getJSON(config['web_root']+"Home/Image/list_images",{id:id},
		function(data){
			var $table = $("#east-center table")
			// do some clean jobs
			$table.html('').attr("data-id",id)
			// create new elements
			for(var i=0;i<data.length;i++){
				var $del = $("").addClass("btn btn-danger btn-xs pull-right remove_image")
				.append($("").addClass("glyphicon glyphicon-remove"))
				.attr("data-filename",data[i]['filename'])
				var $td = $("")
				var $tr = $("
").attr("data-filename",data[i]['filename'])
				$td.append(
					$("").text(data[i]['filename'])
					.attr("href",config['inset_dir']+data[i]['filename'])
					.attr("target","_blank")
					)
				$td.append($del)
				$tr.append($td)
				$table.append($tr)
			}
			image_list_events_binding($table,data)
		})
}
function image_list_events_binding($table,data){
	$table.find("a.remove_image").click(function(){
		var c = confirm("Are you sure to delete?")
		if(!c){
			return;
		}
		var filename = $(this).attr("data-filename")
		$.getJSON(config['web_root']+"Home/Image/delete_image",
			{filename:filename},function(data){
			if(data.result==="ok"){
				$table.find("tr[data-filename='"+filename+"']").remove()
			}else{
				alert(data.info)
			}
		})
	})
}
function load_attachments(id,callback){
	$.getJSON(config['web_root']+"Home/Attachment/list_attachments",{id:id},
		function(data){
			var $table = $("#east-south table")
			// do some clean jobs
			$table.html('').attr("data-id",id)
			// create new elements
			for(var i=0;i<data.length;i++){
				var $del = $("").addClass("btn btn-danger btn-xs pull-right remove_attachment")
				.append($("").addClass("glyphicon glyphicon-remove"))
				.attr("data-filename",data[i]['filename'])
				var $td = $("")
				var $tr = $("
").attr("data-filename",data[i]['filename'])
				$td.append(
					$("").text(data[i]['filename'])
					.attr("href",config['attachment_dir']+data[i]['filename'])
					.attr("target","_blank")
					)
				$td.append($del)
				$tr.append($td)
				$table.append($tr)
			}
			attachment_list_events_binding($table,data)
		})
}
function attachment_list_events_binding($table,data){
	$table.find("a.remove_attachment").click(function(){
		var c = confirm("Are you sure to delete?")
		if(!c){
			return;
		}
		var filename = $(this).attr("data-filename")
		$.getJSON(config['web_root']+"Home/Attachment/delete_attachment",
			{filename:filename},function(data){
			if(data.result==="ok"){
				$table.find("tr[data-filename='"+filename+"']").remove()
			}else{
				alert(data.info)
			}
		})
	})
}
function clear_draft(){
	$("#east-center table,#east-south table,#update_title").removeAttr("data-id")
	$("#east-center table,#east-south table").html('')
	$("#thumb").attr("src",'')
	$("#title").val('')
	$("#create_time,#last_save_time,#ptitle").text('')
	editor.setValue('')
}

function bind_file_upload_event($file_input,callback){
	var $progress_bar = $file_input.parent().siblings(".progress").find(".progress-bar")
	up = new uploader($file_input.get(0), {
		url:$file_input.attr("data-url"),
		autoUpload:true,
		before:function(){
			$progress_bar.css("width",'0')
			if(!$("#update_title").attr("data-id")){
				alert("Please select a draft.")
				return false
			}
			$progress_bar.text($file_input.val().split(/(\\|\/)/g).pop())
			return true;
		},
		get_data:function(){
			return {id:$("#update_title").attr("data-id")}
		},
		progress:function(ev){
			$progress_bar.css("width",Math.floor((ev.loaded/ev.total)*100)+'%')
		},
		error:function(ev){ alert(ev); },
		success:function(data){
			if(data['result']==='ok'){
				alert('Upload successfully!')
				if(callback){
					callback(data)
				}
			}else{
				alert("Upload error:"+data['info'])
			}
		}
	});
}
function compile(mixed_content){
	var splited = mixed_content.split("---")
	var head = splited[1]
	var content = splited[2]
	var content_lines = content.split('\n')
	var is_code_line=false
	for(var i=0;i<content_lines.length;i++){
		var line = content_lines[i]
		if(line.indexOf('{%')!=-1&&line.indexOf('%}')!=-1&&line.indexOf('endhighlight')!=-1){
			content_lines[i] = "

"
			is_code_line=false
			continue;
		}
		if(line.indexOf('{%')!=-1&&line.indexOf('%}')!=-1&&line.indexOf('highlight')!=-1){
			var lang = line.split('highlight')[1].split('%}')[0].trim()
			content_lines[i] = "

"
			is_code_line=true
			continue;
		}
		if(is_code_line){
			content_lines[i] = content_lines[i].replace(/</g,"<").replace(/>/g,">")
		}
	}
	var parsed_content = content_lines.join('\n').replace(/{{ site.images }}\//g,config['inset_dir'])
	var result = parse(mixed_content)
	return {parsed_content:parsed_content,title:result['title'],date:result['date'],tags:result['tags']}
}
$(function(){
	$("#go_previous,#go_next").click(function(){
		if($(this).hasClass("disabled")){
			return;
		}
		load_draft_list($(this).attr("data-page"),config['page_size'])
		return false;
	})
	$("#thumb").error(function(){
		$(this).attr("src",$(this).attr("data-holder"))
	})
	load_draft_list(1,10)
	editor = CodeMirror.fromTextArea(document.getElementById("content"), {
	    mode: 'markdown',
	    lineNumbers: true,
	    theme: "default",
	    lineWrapping: true,
	    extraKeys: {"Enter": "newlineAndIndentContinueMarkdownList"}
	  });
	$("#update_title").click(function(){
		var id = $(this).attr("data-id")
		if(!id){
			return
		}
		$.getJSON(config["web_root"]+"Home/Draft/rename_draft",
			{id:id,new_title:$("#title").val()},
			function(data){
				if(data['result'] === 'ok'){
					alert("Rename draft ok")
					load_draft_list($("#current_page").text(),config['page_size'])
				}else{
					alert(data.info)
				}
			})
	})
	//test
	bind_file_upload_event($("#thumb_upload"),function(data){
		re_load_thumb(data.filename)
	})
	bind_file_upload_event($("#inset_upload"),function(data){
		load_images($("#update_title").attr("data-id"))
	})
	bind_file_upload_event($("#attachment_upload"),function(data){
		load_attachments($("#update_title").attr("data-id"))
	})
	$("#save-btn").click(function(){
		var id=$("#update_title").attr('data-id')
		if(!id){
			alert("Please select a draft")
			return false
		}
		$.post(config['web_root']+"Home/Draft/update_draft",{id:id,content:editor.getValue()},function(data){
			if(data['aff']===1){
				alert("Save draft ok.")
			}else{
				alert(data['error'])
			}
		},'json')
		return false
	})
	var now = new Date()
	$("#new-title-input").val(
		now.getFullYear()+"-"+(now.getMonth()+1)+"-"+now.getDate()
		+"-new-draft-title-here")
	$("#create_draft_btn").click(function(){
		$.getJSON(config['web_root']+"Home/Draft/create_draft",
			{title:$("#new-title-input").val()},
			function(data){
				if(data['aff']){
					clear_draft()
					load_draft_list(1,config['page_size'])
				}
			})
		return false;
	})
	$("#preview-btn").click(function(){
		var id=$("#update_title").attr('data-id')
		if(!id){
			alert("Please select a draft")
			return false
		}
		var mixed_content = editor.getValue();
		var converter = new Markdown.Converter();
		try{
			var result = compile(mixed_content)
			var html = converter.makeHtml(result['parsed_content']);
			//html.replace(/unformatedcode>\n/g,'>')
			result['html'] = html.replace(/unformatedcode>\n/g,'>')
			console.log(html);
			console.log(result['date'])
			console.log(result['title'])
			console.log(JSON.stringify(result['tags']))
			window.parent.postMessage(result, '*');
		}catch(error){
			alert(JSON.stringify(error))
		}
		return false;
	})
})<commit_msg>add ctrl+s shortcut<commit_after>function load_draft_list(page_num,page_size,callback){
	$.getJSON(config['web_root']+"Home/Draft/list_drafts",
		{page_num:page_num,page_size:page_size},function(data){
		var $table = $("#west table")
		// do some clean jobs
		$table.html('')
		$("#go_previous,#go_next").addClass("disabled")
		// create new elements
		for(var i=0;i<data.drafts.length;i++){
			var $del = $("").addClass("btn btn-danger btn-xs pull-right remove_draft")
			.append($("").addClass("glyphicon glyphicon-remove"))
			.attr("data-id",data.drafts[i]['id'])
			var $td = $("")
			var $tr = $("
").attr("data-id",data.drafts[i]['id'])
			$td.text(data.drafts[i]['title'])
			$td.append($del)
			$tr.append($td)
			$table.append($tr)
		}
		draft_list_events_binding($table,data)
		var total_page = Math.ceil(data['total']/page_size)
		$("#current_page").text(data['current'])
		$("#total_page").text(total_page)
		if(data['current']>1){
			$("#go_previous").removeClass("disabled").attr("data-page",data['current']-1)
		}
		if(data['current']<total_page){
			$("#go_next").removeClass("disabled").attr("data-page",data['current']+1)
		}
		if(callback){
			callback(data)
		}
	})
}
function draft_list_events_binding($table,data){
	$table.find("a.remove_draft").click(function(){
		var c = confirm("Are you sure to delete?")
		if(!c){
			return false;
		}
		var id = $(this).attr("data-id")
		$.getJSON(config['web_root']+"Home/Draft/delete_draft",
			{id:id},function(data){
			if(data.result==="ok"){
				$table.find("tr[data-id="+id+"]").remove()
				if(id===$("#update_title").attr("data-id")){
					clear_draft();
				}
			}else{
				alert(data.info)
			}
		})
		return false;
	})
	$table.find("tr").click(function(){
		var id = $(this).attr("data-id")
		load_draft(id)
	})
}
function load_draft(id,callback){
	$.getJSON(config['web_root']+"Home/Draft/load_draft",
	{id:id},function(data){
		$("#title").val(data['title'])
		$("#create_time").text(data['create_time'])
		$("#last_save_time").text(data['last_save_time'])
		$("#update_title").attr("data-id",id)
		editor.setValue(data['content'])
		var result = parse(data['content'])
		$("#ptitle").text(result['title'])
		re_load_thumb(get_thumb_name(data.title)+'.jpg')
		load_images(id)
		load_attachments(id)
		if(callback){
			callback(data)
		}
	})
	function get_thumb_name(title){
		var first_word = title.split("-")[3]
		var begin_idx = title.indexOf(first_word)
		return title.substring(begin_idx)
	}
}
function re_load_thumb(title){
	$("#thumb").attr("src",config['thumb_dir']+title)
}
function parse(content){
	var head = content.split("---")[1]
	var lines = head.split("\n")
	var result = {}
	for(var i=0;i<lines.length;i++){
		var titleIdx = lines[i].indexOf("title:")
		if(titleIdx>=0){
			result['title'] = lines[i].substring(titleIdx+6).trim().split('"')[1]
			continue
		}
		titleIdx = lines[i].indexOf("date:")
		if(titleIdx>=0){
			result['date'] = new Date(
				Date.parse(
					lines[i].substring(titleIdx+5).trim()
					)
				).toDateString()
			continue
		}
		titleIdx = lines[i].indexOf("categories:")
		if(titleIdx>=0){
			result['tags'] = lines[i].substring(titleIdx+11).trim().split(' ')
			continue
		}
	}
	return result
}
function load_images(id,callback){
	$.getJSON(config['web_root']+"Home/Image/list_images",{id:id},
		function(data){
			var $table = $("#east-center table")
			// do some clean jobs
			$table.html('').attr("data-id",id)
			// create new elements
			for(var i=0;i<data.length;i++){
				var $del = $("").addClass("btn btn-danger btn-xs pull-right remove_image")
				.append($("").addClass("glyphicon glyphicon-remove"))
				.attr("data-filename",data[i]['filename'])
				var $td = $("")
				var $tr = $("
").attr("data-filename",data[i]['filename'])
				$td.append(
					$("").text(data[i]['filename'])
					.attr("href",config['inset_dir']+data[i]['filename'])
					.attr("target","_blank")
					)
				$td.append($del)
				$tr.append($td)
				$table.append($tr)
			}
			image_list_events_binding($table,data)
		})
}
function image_list_events_binding($table,data){
	$table.find("a.remove_image").click(function(){
		var c = confirm("Are you sure to delete?")
		if(!c){
			return;
		}
		var filename = $(this).attr("data-filename")
		$.getJSON(config['web_root']+"Home/Image/delete_image",
			{filename:filename},function(data){
			if(data.result==="ok"){
				$table.find("tr[data-filename='"+filename+"']").remove()
			}else{
				alert(data.info)
			}
		})
	})
}
function load_attachments(id,callback){
	$.getJSON(config['web_root']+"Home/Attachment/list_attachments",{id:id},
		function(data){
			var $table = $("#east-south table")
			// do some clean jobs
			$table.html('').attr("data-id",id)
			// create new elements
			for(var i=0;i<data.length;i++){
				var $del = $("").addClass("btn btn-danger btn-xs pull-right remove_attachment")
				.append($("").addClass("glyphicon glyphicon-remove"))
				.attr("data-filename",data[i]['filename'])
				var $td = $("")
				var $tr = $("
").attr("data-filename",data[i]['filename'])
				$td.append(
					$("").text(data[i]['filename'])
					.attr("href",config['attachment_dir']+data[i]['filename'])
					.attr("target","_blank")
					)
				$td.append($del)
				$tr.append($td)
				$table.append($tr)
			}
			attachment_list_events_binding($table,data)
		})
}
function attachment_list_events_binding($table,data){
	$table.find("a.remove_attachment").click(function(){
		var c = confirm("Are you sure to delete?")
		if(!c){
			return;
		}
		var filename = $(this).attr("data-filename")
		$.getJSON(config['web_root']+"Home/Attachment/delete_attachment",
			{filename:filename},function(data){
			if(data.result==="ok"){
				$table.find("tr[data-filename='"+filename+"']").remove()
			}else{
				alert(data.info)
			}
		})
	})
}
function clear_draft(){
	$("#east-center table,#east-south table,#update_title").removeAttr("data-id")
	$("#east-center table,#east-south table").html('')
	$("#thumb").attr("src",'')
	$("#title").val('')
	$("#create_time,#last_save_time,#ptitle").text('')
	editor.setValue('')
}

function bind_file_upload_event($file_input,callback){
	var $progress_bar = $file_input.parent().siblings(".progress").find(".progress-bar")
	up = new uploader($file_input.get(0), {
		url:$file_input.attr("data-url"),
		autoUpload:true,
		before:function(){
			$progress_bar.css("width",'0')
			if(!$("#update_title").attr("data-id")){
				alert("Please select a draft.")
				return false
			}
			$progress_bar.text($file_input.val().split(/(\\|\/)/g).pop())
			return true;
		},
		get_data:function(){
			return {id:$("#update_title").attr("data-id")}
		},
		progress:function(ev){
			$progress_bar.css("width",Math.floor((ev.loaded/ev.total)*100)+'%')
		},
		error:function(ev){ alert(ev); },
		success:function(data){
			if(data['result']==='ok'){
				alert('Upload successfully!')
				if(callback){
					callback(data)
				}
			}else{
				alert("Upload error:"+data['info'])
			}
		}
	});
}
function compile(mixed_content){
	var splited = mixed_content.split("---")
	var head = splited[1]
	var content = splited[2]
	var content_lines = content.split('\n')
	var is_code_line=false
	for(var i=0;i<content_lines.length;i++){
		var line = content_lines[i]
		if(line.indexOf('{%')!=-1&&line.indexOf('%}')!=-1&&line.indexOf('endhighlight')!=-1){
			content_lines[i] = "

"
			is_code_line=false
			continue;
		}
		if(line.indexOf('{%')!=-1&&line.indexOf('%}')!=-1&&line.indexOf('highlight')!=-1){
			var lang = line.split('highlight')[1].split('%}')[0].trim()
			content_lines[i] = "

"
			is_code_line=true
			continue;
		}
		if(is_code_line){
			content_lines[i] = content_lines[i].replace(/</g,"<").replace(/>/g,">")
		}
	}
	var parsed_content = content_lines.join('\n').replace(/{{ site.images }}\//g,config['inset_dir'])
	var result = parse(mixed_content)
	return {parsed_content:parsed_content,title:result['title'],date:result['date'],tags:result['tags']}
}
$(function(){
	$("#go_previous,#go_next").click(function(){
		if($(this).hasClass("disabled")){
			return;
		}
		load_draft_list($(this).attr("data-page"),config['page_size'])
		return false;
	})
	$("#thumb").error(function(){
		$(this).attr("src",$(this).attr("data-holder"))
	})
	load_draft_list(1,10)
	editor = CodeMirror.fromTextArea(document.getElementById("content"), {
	    mode: 'markdown',
	    lineNumbers: true,
	    theme: "default",
	    lineWrapping: true,
	    extraKeys: {"Enter": "newlineAndIndentContinueMarkdownList"}
	  });
	$("#update_title").click(function(){
		var id = $(this).attr("data-id")
		if(!id){
			return
		}
		$.getJSON(config["web_root"]+"Home/Draft/rename_draft",
			{id:id,new_title:$("#title").val()},
			function(data){
				if(data['result'] === 'ok'){
					alert("Rename draft ok")
					load_draft_list($("#current_page").text(),config['page_size'])
				}else{
					alert(data.info)
				}
			})
	})
	//test
	bind_file_upload_event($("#thumb_upload"),function(data){
		re_load_thumb(data.filename)
	})
	bind_file_upload_event($("#inset_upload"),function(data){
		load_images($("#update_title").attr("data-id"))
	})
	bind_file_upload_event($("#attachment_upload"),function(data){
		load_attachments($("#update_title").attr("data-id"))
	})
	$("#save-btn").click(save_draft)

	function save_draft(){
		var id=$("#update_title").attr('data-id')
		if(!id){
			alert("Please select a draft")
			return false
		}
		$.post(config['web_root']+"Home/Draft/update_draft",{id:id,content:editor.getValue()},function(data){
			if(data['aff']===1){
				alert("Save draft ok.")
			}else{
				alert(data['error'])
			}
		},'json')
		return false
	}
	$(document).keydown(function(event) {

	    //19 for Mac Command+S
	    if (!( String.fromCharCode(event.which).toLowerCase() == 's' && event.ctrlKey) && !(event.which == 19)) return true;

	    save_draft()

	    event.preventDefault();
	    return false;
	});
	var now = new Date()
	$("#new-title-input").val(
		now.getFullYear()+"-"+(now.getMonth()+1)+"-"+now.getDate()
		+"-new-draft-title-here")
	$("#create_draft_btn").click(function(){
		$.getJSON(config['web_root']+"Home/Draft/create_draft",
			{title:$("#new-title-input").val()},
			function(data){
				if(data['aff']){
					clear_draft()
					load_draft_list(1,config['page_size'])
				}
			})
		return false;
	})
	$("#preview-btn").click(function(){
		var id=$("#update_title").attr('data-id')
		if(!id){
			alert("Please select a draft")
			return false
		}
		var mixed_content = editor.getValue();
		var converter = new Markdown.Converter();
		try{
			var result = compile(mixed_content)
			var html = converter.makeHtml(result['parsed_content']);
			//html.replace(/unformatedcode>\n/g,'>')
			result['html'] = html.replace(/unformatedcode>\n/g,'>')
			console.log(html);
			console.log(result['date'])
			console.log(result['title'])
			console.log(JSON.stringify(result['tags']))
			window.parent.postMessage(result, '*');
		}catch(error){
			alert(JSON.stringify(error))
		}
		return false;
	})
})<|endoftext|>


<commit_before>'use strict';

const got = require('got');
const path = require('path');
const moment = require('moment');
const pad = require('lodash.padstart');
const _ = require('lodash');
const AWS = require('aws-sdk')
const local = require('kes/src/local');
const metadata = require('../../lib/metadata');
var through2 = require('through2')

const bands = [
  'ANG.txt',
  'B1.TIF',
  'B2.TIF',
  'B3.TIF',
  'B4.TIF',
  'B5.TIF',
  'B6.TIF',
  'B7.TIF',
  'B8.TIF',
  'B9.TIF',
  'B10.TIF',
  'B11.TIF',
  'BQA.TIF',
  'MTL.txt'
];

const s3 = new AWS.S3()

function googleLinks(data) {
  const row = pad(data.row, 3, '0');
  const _path = pad(data.path, 3, '0');
  //const sceneId = data.sceneID;
  const productId = data.LANDSAT_PRODUCT_ID;

  const c1Console = `https://console.cloud.google.com/storage/browser/gcp-public-data-landsat/LC08/${path.join(_path, row, productId)}`;
  const c1Base = `https://storage.googleapis.com/gcp-public-data-landsat/LC08/01/${path.join(_path, row, productId)}`;

  const c1Files = bands.map((b) => `${c1Base}/${productId}_${b}`);

  const c1 = {
    index: c1Console,
    files: c1Files
  };

  return { c1 };
}

//! Check to see if a URL exists
function fileExists(url) {
  var params = {
    Bucket: 'landsat-pds',
    Key: url.slice(36)
  }
  return new Promise(function(resolve, reject) {
    s3.headObject(params, function (err, metadata) {
      if (err && err.code === 'NotFound') {
        reject(url)
      } else {
        resolve(url)
      }
    })
  })
  /*return new Promise(function(resolve, reject) {
    got(url).then(response => {
      resolve(url);
    }).catch(e => {
      reject(url);
    });
  });*/
}

//! iterate over an array synchronously, invoke function on each element
function arrayIterate(values, fn) {
  return new Promise(function(resolve, reject) {
    // Are there any values to check?
    if (values.length === 0) {
      // All were rejected
      reject();
    }
    // Try the first value
    fn(values[0]).then(function(val) {
      // Resolved, we're all done
      //console.log('Resolved ' + val);
      resolve(val);
    }).catch(function() {
      // Rejected, remove the first item from the array and recursively
      // try the next one
      //console.log('Rejected ' + values[0]);
      values.shift();
      arrayIterate(values, fn).then(resolve).catch(reject);
    });
  });
}

function awsLinks(data) {
  const row = pad(data.row, 3, '0');
  const _path = pad(data.path, 3, '0');
  const sceneId = data.sceneID;
  const productId = data.LANDSAT_PRODUCT_ID;

  const c1Base = `https://landsat-pds.s3.amazonaws.com/c1/L8/${path.join(_path, row, productId)}`;
  const c1Files = bands.map((b) => `${c1Base}/${productId}_${b}`);

  const c1 = {
    index: `${c1Base}/index.html`,
    files: c1Files,
    thumbnail: `${c1Base}/${productId}_thumb_large.jpg`
  };

  return new Promise(function(resolve, reject) {
    // AWS doesn't include all C1 scenes, we return the old urls for
    // any scenes that is before May 1 2017
    info = {};
    if (moment(data.acquisitionDate) > moment('2017-04-30')) {
      resolve(c1)
    } else {
      const _sceneId = sceneId.slice(0, -2);
      var sid;
      const rev = sceneId.slice(-2)
      var prefix = `http://landsat-pds.s3.amazonaws.com/L8/${path.join(_path, row, _sceneId)}`;
      var links = _.range(rev, -1, -1).map(r => `${prefix}` + pad(r, 2, '0') + '/index.html');

      arrayIterate(links, fileExists).then(val => {
        prefix = prefix + val.slice(-13, -11)
        sid = _sceneId + val.slice(-13, -11)
        const pre = {
          index: `${prefix}/index.html`,
          files: bands.map((b) => `${prefix}/${sid}_${b}`),
          thumbnail: `${prefix}/${sid}_thumb_large.jpg`
        };
        resolve(pre);
      }).catch(e => {
        reject(`${prefix} not available`);
      });
    }
  });
}

function transform(data, encoding, next) {
  const numberFields = [
    'cloudCoverFull',
    'path',
    'row',
    'upperLeftCornerLatitude',
    'upperLeftCornerLongitude',
    'upperRightCornerLatitude',
    'upperRightCornerLongitude',
    'lowerLeftCornerLatitude',
    'lowerLeftCornerLongitude',
    'lowerRightCornerLatitude',
    'lowerRightCornerLongitude',
    'sceneCenterLatitude',
    'sceneCenterLongitude',
    'cloudCover',
    'sunElevation',
    'sunAzimuth',
    'receivingStation',
    'imageQuality1',
    //'ROLL_ANGLE',
    //'GEOMETRIC_RMSE_MODEL',
    //'GEOMETRIC_RMSE_MODEL_X',
    //'GEOMETRIC_RMSE_MODEL_Y',
    //'COLLECTION_NUMBER',
    //'CLOUD_COVER_LAND'
  ];

  numberFields.forEach(f => {
    data[f] = parseFloat(data[f]);
  });

  const data_geometry = { // eslint-disable-line camelcase
    type: 'Polygon',
    crs: {
      type: 'name',
      properties: {
        name: 'urn:ogc:def:crs:EPSG:8.9:4326'
      }
    },
    coordinates: [[
      [data.upperRightCornerLongitude, data.upperRightCornerLatitude],
      [data.upperLeftCornerLongitude, data.upperLeftCornerLatitude],
      [data.lowerLeftCornerLongitude, data.lowerLeftCornerLatitude],
      [data.lowerRightCornerLongitude, data.lowerRightCornerLatitude],
      [data.upperRightCornerLongitude, data.upperRightCornerLatitude]
    ]]
  };

  const leftLong = Math.min(data.lowerLeftCornerLongitude, data.upperLeftCornerLongitude)
  const rightLong = Math.max(data.lowerRightCornerLongitude, data.upperRightCornerLongitude)
  if (Math.abs(rightLong - leftLong) > 300) {
    console.log(`warning: skipping ${data.sceneID} for crossing 180th Meridian (${JSON.stringify(data_geometry)})`)
    next()
  } else {
    const google = googleLinks(data);

    const customFields = {
      scene_id: data.sceneID,
      product_id: data.LANDSAT_PRODUCT_ID,
      satellite_name: 'landsat-8',
      cloud_coverage: data.cloudCoverFull,
      date: data.acquisitionDate,
      thumbnail: data.browseURL,
      data_geometry,
      download_links: {
        usgs: `https://earthexplorer.usgs.gov/download/12864/${data.sceneID}/STANDARD/EE`,
        aws_s3: [],
        google: google.c1.files
      },
      aws_thumbnail: null,
      aws_index: null,
      google_index: google.c1.index,
    };

    delete data.cloudCoverFull

    awsLinks(data).then((info) => {
      //console.log('awslinks', info)
      customFields.download_links.aws_s3 = info.files
      customFields.aws_thumbnail = info.thumbnail
      customFields.aws_index = info.index
      record = Object.assign({}, customFields, data)
      this.push(record)
      next()
    }).catch(e => {
      console.log(`error processing ${customFields.scene_id}: ${e}`)
      next()
    })
  }

  //if (thumbnailUrl) {
  //customFields.thumbnail = url.resolve(thumbnailUrl, `${data.LANDSAT_PRODUCT_ID}.jpg`);
  //}
}

function handler (event, context, cb) {
  var _transform = through2({'objectMode': true, 'consume': true}, transform)
  console.log('Landsat handler:', event)
  metadata.update(event, _transform, cb)
}

local.localRun(() => {
  const a = {
    bucket: 'sat-api',
    key: 'test',
    satellite: 'landsat',
    currentFileNum: 11,
    lastFileNum: 11,
    arn: 'arn:aws:states:us-east-1:552819999234:stateMachine:landsat-meta'
  };
  console.log('running locally')

  handler(a, null, (e, r) => {});
});

module.exports.handler = handler;
<commit_msg>landsat: check for 1 earlier revision first<commit_after>'use strict';

const got = require('got');
const path = require('path');
const moment = require('moment');
const pad = require('lodash.padstart');
const _ = require('lodash');
const AWS = require('aws-sdk')
const local = require('kes/src/local');
const metadata = require('../../lib/metadata');
var through2 = require('through2')

const bands = [
  'ANG.txt',
  'B1.TIF',
  'B2.TIF',
  'B3.TIF',
  'B4.TIF',
  'B5.TIF',
  'B6.TIF',
  'B7.TIF',
  'B8.TIF',
  'B9.TIF',
  'B10.TIF',
  'B11.TIF',
  'BQA.TIF',
  'MTL.txt'
];

const s3 = new AWS.S3()

function googleLinks(data) {
  const row = pad(data.row, 3, '0');
  const _path = pad(data.path, 3, '0');
  //const sceneId = data.sceneID;
  const productId = data.LANDSAT_PRODUCT_ID;

  const c1Console = `https://console.cloud.google.com/storage/browser/gcp-public-data-landsat/LC08/${path.join(_path, row, productId)}`;
  const c1Base = `https://storage.googleapis.com/gcp-public-data-landsat/LC08/01/${path.join(_path, row, productId)}`;

  const c1Files = bands.map((b) => `${c1Base}/${productId}_${b}`);

  const c1 = {
    index: c1Console,
    files: c1Files
  };

  return { c1 };
}

//! Check to see if a URL exists
function fileExists(url) {
  var params = {
    Bucket: 'landsat-pds',
    Key: url.slice(36)
  }
  return new Promise(function(resolve, reject) {
    s3.headObject(params, function (err, metadata) {
      if (err && err.code === 'NotFound') {
        reject(url)
      } else {
        resolve(url)
      }
    })
  })
  /*return new Promise(function(resolve, reject) {
    got(url).then(response => {
      resolve(url);
    }).catch(e => {
      reject(url);
    });
  });*/
}

Array.prototype.swap = function (x,y) {
  var b = this[x];
  this[x] = this[y];
  this[y] = b;
  return this;
}

//! iterate over an array synchronously, invoke function on each element
function arrayIterate(values, fn) {
  return new Promise(function(resolve, reject) {
    // Are there any values to check?
    if (values.length === 0) {
      // All were rejected
      reject();
    }
    // Try the first value
    fn(values[0]).then(function(val) {
      // Resolved, we're all done
      resolve(val);
    }).catch(function() {
      // Rejected, remove the first item from the array and recursively
      // try the next one
      values.shift();
      arrayIterate(values, fn).then(resolve).catch(reject);
    });
  });
}

function awsLinks(data) {
  const row = pad(data.row, 3, '0');
  const _path = pad(data.path, 3, '0');
  const sceneId = data.sceneID;
  const productId = data.LANDSAT_PRODUCT_ID;

  const c1Base = `https://landsat-pds.s3.amazonaws.com/c1/L8/${path.join(_path, row, productId)}`;
  const c1Files = bands.map((b) => `${c1Base}/${productId}_${b}`);

  const c1 = {
    index: `${c1Base}/index.html`,
    files: c1Files,
    thumbnail: `${c1Base}/${productId}_thumb_large.jpg`
  };

  return new Promise(function(resolve, reject) {
    // AWS doesn't include all C1 scenes, we return the old urls for
    // any scenes that is before May 1 2017
    info = {};
    if (moment(data.acquisitionDate) > moment('2017-04-30')) {
      resolve(c1)
    } else {
      const _sceneId = sceneId.slice(0, -2);
      var sid;
      const rev = sceneId.slice(-2)
      var prefix = `http://landsat-pds.s3.amazonaws.com/L8/${path.join(_path, row, _sceneId)}`;
      var links = _.range(rev, -1, -1).map(r => `${prefix}` + pad(r, 2, '0') + '/index.html');
      if (links.length > 1) {
        links.swap(0, 1)
      }

      arrayIterate(links, fileExists).then(val => {
        prefix = prefix + val.slice(-13, -11)
        sid = _sceneId + val.slice(-13, -11)
        const pre = {
          index: `${prefix}/index.html`,
          files: bands.map((b) => `${prefix}/${sid}_${b}`),
          thumbnail: `${prefix}/${sid}_thumb_large.jpg`
        };
        resolve(pre);
      }).catch(e => {
        reject(`${prefix} not available`);
      });
    }
  });
}

function transform(data, encoding, next) {
  const numberFields = [
    'cloudCoverFull',
    'path',
    'row',
    'upperLeftCornerLatitude',
    'upperLeftCornerLongitude',
    'upperRightCornerLatitude',
    'upperRightCornerLongitude',
    'lowerLeftCornerLatitude',
    'lowerLeftCornerLongitude',
    'lowerRightCornerLatitude',
    'lowerRightCornerLongitude',
    'sceneCenterLatitude',
    'sceneCenterLongitude',
    'cloudCover',
    'sunElevation',
    'sunAzimuth',
    'receivingStation',
    'imageQuality1',
    //'ROLL_ANGLE',
    //'GEOMETRIC_RMSE_MODEL',
    //'GEOMETRIC_RMSE_MODEL_X',
    //'GEOMETRIC_RMSE_MODEL_Y',
    //'COLLECTION_NUMBER',
    //'CLOUD_COVER_LAND'
  ];

  numberFields.forEach(f => {
    data[f] = parseFloat(data[f]);
  });

  const data_geometry = { // eslint-disable-line camelcase
    type: 'Polygon',
    crs: {
      type: 'name',
      properties: {
        name: 'urn:ogc:def:crs:EPSG:8.9:4326'
      }
    },
    coordinates: [[
      [data.upperRightCornerLongitude, data.upperRightCornerLatitude],
      [data.upperLeftCornerLongitude, data.upperLeftCornerLatitude],
      [data.lowerLeftCornerLongitude, data.lowerLeftCornerLatitude],
      [data.lowerRightCornerLongitude, data.lowerRightCornerLatitude],
      [data.upperRightCornerLongitude, data.upperRightCornerLatitude]
    ]]
  };

  const leftLong = Math.min(data.lowerLeftCornerLongitude, data.upperLeftCornerLongitude)
  const rightLong = Math.max(data.lowerRightCornerLongitude, data.upperRightCornerLongitude)
  if (Math.abs(rightLong - leftLong) > 300) {
    console.log(`warning: skipping ${data.sceneID} for crossing 180th Meridian (${JSON.stringify(data_geometry)})`)
    next()
  } else {
    const google = googleLinks(data);

    const customFields = {
      scene_id: data.sceneID,
      product_id: data.LANDSAT_PRODUCT_ID,
      satellite_name: 'landsat-8',
      cloud_coverage: data.cloudCoverFull,
      date: data.acquisitionDate,
      thumbnail: data.browseURL,
      data_geometry,
      download_links: {
        usgs: `https://earthexplorer.usgs.gov/download/12864/${data.sceneID}/STANDARD/EE`,
        aws_s3: [],
        google: google.c1.files
      },
      aws_thumbnail: null,
      aws_index: null,
      google_index: google.c1.index,
    };

    delete data.cloudCoverFull

    awsLinks(data).then((info) => {
      //console.log('awslinks', info)
      customFields.download_links.aws_s3 = info.files
      customFields.aws_thumbnail = info.thumbnail
      customFields.aws_index = info.index
      record = Object.assign({}, customFields, data)
      this.push(record)
      next()
    }).catch(e => {
      console.log(`error processing ${customFields.scene_id}: ${e}`)
      next()
    })
  }

  //if (thumbnailUrl) {
  //customFields.thumbnail = url.resolve(thumbnailUrl, `${data.LANDSAT_PRODUCT_ID}.jpg`);
  //}
}

function handler (event, context, cb) {
  var _transform = through2({'objectMode': true, 'consume': true}, transform)
  console.log('Landsat handler:', event)
  metadata.update(event, _transform, cb)
}

local.localRun(() => {
  const a = {
    bucket: 'sat-api',
    key: 'test',
    satellite: 'landsat',
    currentFileNum: 11,
    lastFileNum: 11,
    arn: 'arn:aws:states:us-east-1:552819999234:stateMachine:landsat-meta'
  };
  console.log('running locally')

  handler(a, null, (e, r) => {});
});

module.exports.handler = handler;
<|endoftext|>


<commit_before>//plugin mission 內頁的后塊 收合
(function ($) {
    $.fn.blockBox = function (opts) {
        // default configuration
        var config = $.extend({}, {
            initStatus: 'on'
        }, opts);
        // main function
        function initBox(object) {
            var dObj = $(object);
            var dBoxContent = dObj.find('.js_boxContent');
            var dBoxSwitch = dObj.find('.js_switchBox');
            var isBoxOpen = function () {
                if (dBoxContent && (dObj.find('.js_boxContent').is(":visible") == true)) {
                    config.initStatus = 'on';
                }
                else {
                    config.initStatus = 'off';
                }
            };

            dBoxSwitch.on('click', function (e) {
                e.preventDefault();
                isBoxOpen();
                var dThisBtn = $(this);
                if (!dThisBtn.hasClass('disabled')) {
                    dThisBtn.addClass('disabled');
                    if (config.initStatus == 'on') {
                        dBoxContent.slideUp(1000, function () {
                            dThisBtn.removeClass('disabled')
                            dThisBtn.removeClass('on').addClass('off');
                        });
                    }
                    else {
                        dBoxContent.slideDown(1000, function () {
                            dThisBtn.removeClass('disabled');
                            dThisBtn.removeClass('off').addClass('on');

                        });
                    }
                }
            });
        }
        // initialize every element
        this.each(function () {
            initBox($(this));
        });
        return this;
    };
    // start
    $(function () {
        $(".js_box").blockBox();
    });
})(jQuery); //end blockBox

var missionDaintiest = {};
missionDaintiest.module = {
    version: '0.1',
    namespace: function (ns_string) {
        var parts = ns_string.split('.'),
            parent = missionDaintiest,
            i;
        if (parts[0] === 'missionDaintiest') {
            parts = parts.slice(1);
        }
        for (i = 0; i < parts.length; i += 1) {
            if (typeof parent[parts[i]] === 'undefined') {
                parent[parts[i]] = {};
            }
            parent = parent[parts[i]];
        }
        return parent;
    },
    inherit: function (Child, Parent) {
        Child.prototype = new Parent();
    },
    secretGeneratorExecution: function (dModule) {
        var dModule = $(dModule);
        // slot
        var dSlotMc = dModule.find('.slotMc');
        var dRedBall = dSlotMc.find('.redBall');
        var dSlotBox = dSlotMc.find('ul.wheel');

        var dSend = dModule.find('.btnFormSubmit');
        var dShare = dModule.find('.btnFormShare');
        var dTabs = dModule.find('.stepList');
        // setting data
        var friendsArr = [];
        var imagesArr = JSON.parse(dModule.find('input.imageData').val() || [])
        // 從facebook取回來的朋友上限
        var FRIENDS_FACEBOOK_MAX = 300;
        // { 1: 文字, 2: 朋友 }
        var OPTION_TEXT = '1';
        var OPTION_FACEBOOK_FRIENDS = '2';
        // 要拉霸的朋友數量
        var FRIENDO_SLOT_MAX = 50;
        // 要存檔的資料
        var resultData = [];
        // jSlots的結束方式與我們不一樣，要自己控制結束。
        var slotCounts = 0;

        // function
        // get Friendos random list for next friends slot
        var getRandomList = function (maxLength) {
            //進來的list長度跟朋友清單的總長度相較之下 取小值
            var maxCount = Math.min(maxLength, friendsArr.length);
            var shuffleFriendList = friendsArr.slice();
            shuffle(shuffleFriendList);
            var randomFriendsDatas = shuffleFriendList.slice(0, maxCount);
            return randomFriendsDatas;
        };

        //洗牌
        var shuffle = function (array) {
            var counter = array.length, temp, index;

            // While there are elements in the array
            while (counter > 0) {
                // Pick a random index
                index = Math.floor(Math.random() * counter);

                // Decrease counter by 1
                counter--;

                // And swap the last element with it
                temp = array[counter];
                array[counter] = array[index];
                array[index] = temp;
            }

            return array;
        };

        var setTab = function (stepNumber) {
            dTabs.find('li').removeClass('active');
            dTabs.find('.step' + stepNumber).addClass('active');
        };

        var initSlotMc = function () {

            // 呼叫 jSlots 前，要先初始化 li 個數
            $.each(dSlotBox.filter('[data-optiontype="' + OPTION_FACEBOOK_FRIENDS + '"]'), function (index, item) {
                var dSelf = $(this);
                var len = Math.min(FRIENDO_SLOT_MAX, friendsArr.length);
                var i;
                var slotHtml = '';
                dSelf.empty();
                for (i = 0; i < len; i++) {
                    slotHtml += '

';
                }
                dSelf.append(slotHtml);
            });

            //bind slotbar animate and link true button
            dRedBall.click(function (e) {
                var dSelf = $(this);
                var dSlotMc = dSelf.closest('.slotMc');
                var dDefaultLogo = dSlotMc.find('.defaultLogo');
                var dArrows = dSlotMc.find('.arrows');
                var dBarBtn = dSlotMc.find('.barBlock');
                var dPlayFancy = dSlotMc.find('.playFancy');

                if (dSelf.hasClass('disabled')) {
                    return false;
                }

                dSelf.addClass('disabled');

                if (dDefaultLogo.length > 0) {
                    dDefaultLogo.slideDown(400, function () {
                        dDefaultLogo.remove();
                    });
                    //isFirstTurn = false;
                }

                if (dArrows.is(":visible")) {
                    dArrows.fadeOut();
                }

                dBarBtn.animate({ 'height': '200' }, 500, 'easeInOutElastic')
                        .animate({ 'height': '310' }, 1800, 'easeOutBounce');

                dRedBall.animate({ 'width': '120' }, 500, 'easeInOutElastic')
                        .animate({ 'width': '80' }, 1800, 'easeOutBounce', function () {
                    dRedBall.removeClass('disabled');
                });

                //click the true slot button
                dPlayFancy.delay(700).click();

            });

            dSend.on('mission/completeCollectionDone', function () {});
            dSend.on('mission/completeCollectionFail', function () {});

            var shareContent = function(callback) {
                var dFacebookRoot = $('#fb-root');
                var isFeed = !dFacebookRoot.hasClass('processing');

                if (!isFeed) { return false; }
                dFacebookRoot.addClass('processing');

                var shareAnswer = {
                    Description: $.map(resultData, function (item, index) {
                        return item.data('value') || '';
                    }).join(' '),
                    PicURL: shuffle(imagesArr)[0]
                };
                var scroll = $(window).scrollTop();
                FBUtil.after(function (FB) {
                    FB.ui({
                        method: 'feed',
                        message: shareAnswer.Description,
                        name: shareAnswer.Description,
                        link: location.href,
                        caption: '原味千尋　炭燒辣味乳酪絲是重口味秘密的最佳拍檔。你想跟誰說祕密？',
                        description: '跟知心好友無話不談嗎?是否有些話對朋友說不出口?讓粉多進入你的潛意識，挖掘你埋藏已久的小秘密!勇敢對朋友說出秘密，就有機會抽中最強乾杯零食 - 原味千尋炭燒辣味酪絲。原味千尋用16倍的真心，換1份乾杯的感動。',
                        picture: shareAnswer.PicURL
                    },
                    function(response) {
                        if(typeof(callback)=='function'){
                            callback();
                        };

						if ($(window).scrollTop() != scroll && $(window).scrollTop() == 0) {
							$(window).scrollTop(scroll);
						}

                        dFacebookRoot.removeClass('processing');
                    });
                });
                $(window).one('scroll',function () {
                    if ($(window).scrollTop() != scroll) {
                        $(window).scrollTop(scroll);
                    }
                });
            };

            //sumbit button
            dModule.on('click', '.btnFormSubmit.fd-btn-enter', function (e) {
                e.preventDefault();
                var dSelf = $(this);
                var contentGeneratorAnswer = {
                    ResultUrl: null,
                    MimeType: null,
                    Description: $.map(resultData, function (item, index) {
                        return item.data('value') || '';
                    }).join(' '),
                    PicURL: shuffle(imagesArr)[0],
                    Answers: $.map(resultData, function (item, index) {
                        var dSelfBox = item.closest('ul');
                        return {
                            QuestionID: dSelfBox.data('questionid') || '',
                            QType: dSelfBox.data('questiontype') || '',
                            Answer: item.data('value') || '',
                            Question: ''
                        };
                    }),
                    ExecuteType: "ContentGenerator"
                    /*[
                        {
                            QuestionID: 100,
                            QType: 1,
                            Answer: "Amelie Chen",
                            Question: ''
                        }
                    ]*/
                };

				$.event.trigger("mission/completeCollection", contentGeneratorAnswer);

                //shareContent(function(){
                //    $.event.trigger("mission/completeCollection", contentGeneratorAnswer);
                //});

                /*
                var isPublic = dModule.find('#formCheckingRead').is(":checked");
                if (isPublic) {
                    FBUtil.after(function (FB) {
                        FB.api('/me/feed', 'post', {
                            //message:  contentGeneratorAnswer.Description,
                            description: contentGeneratorAnswer.Description,
                            link: location.href,
                            picture: contentGeneratorAnswer.PicURL,
                            name: '原味千尋　炭燒辣味乳酪絲是重口味秘密的最佳拍檔。你想跟誰說祕密？'
                        }, function (response) {
                            if (!response || response.error) {
                                alert('發文失敗！ \n請稍後嘗試或聯繫客服人員。');
                            }
                            $.event.trigger("mission/completeCollection", contentGeneratorAnswer);
                        });
                    });
                } else {
                    $.event.trigger("mission/completeCollection", contentGeneratorAnswer);
                }
                */
            });

            //share button
            dModule.on('click', '.btnFormShare.fd-btn-enter', function (e) {
                e.preventDefault();
                shareContent();
            });

            //slot init
            dSlotBox.jSlots({
                number: 1,
                spinner: '.playFancy',
                easing: 'easeOutSine',
                time: 2000, //轉多久
                loops: 2, //轉幾圈
                onStart: function () {
                    var dSelf = $(this);
                    var dSlot = dSelf.find('ul');
                    var dSlotItem = dSelf.find('li');
                    // 有幾個li就取幾筆，幾個li由初始化那邊決定
                    var randomFriendsArr = getRandomList(dSlotItem.length);
                    // 清空結果
                    resultData = [];
                    // 初始化次數
                    slotCounts = 0;
                    // disabled button
                    dSend.removeClass('fd-btn-enter').addClass('fd-btn-disabled');
                    dShare.removeClass('fd-btn-enter').addClass('fd-btn-disabled');

                    if (dSlot.data('optiontype') == OPTION_FACEBOOK_FRIENDS) {
                        $.each(dSelf.find('li'), function (index, item) {
                            var friendsItem;
                            // 多的li，帶入第一筆資料，讓第一筆與最後一筆都是同一個資料(遵循plugin的方式)'
                            if (index >= randomFriendsArr.length) {
                                friendsItem = randomFriendsArr[0];
                            } else {
                                friendsItem = randomFriendsArr[index];
                            }
                            $(item).data('value', friendsItem.friendName || '')
                                   .data('index', friendsItem.dataIndex || '')
                                   .find('img')
                                   .attr('src', friendsItem.friendoImgUrl || '')
                                   .end()
                                   .find('span.name')
                                   .html(friendsItem.friendName || '');
                        });
                    }
                },
                onWin: function (winCount, winners, finalNumbers) { },
                onEnd: function (finalNumbers) {
                    //this不是ul,是上一層element???
                    var dSelf = $(this);
                    var dSlot = dSelf.find('ul.wheel');
                    var dSlotItem = dSlot.find('li');

                    // 取得選項位置
                    var optionIndex = dModule.find('ul.wheel').index(dSlot);
                    // 取得答案內容
                    resultData[optionIndex] = dSelf.find('li').eq(finalNumbers - 1)

                    slotCounts++;
                    if (slotCounts === dSlotBox.length) {
                        setTab(2);
                        dSend.removeClass('fd-btn-disabled').addClass('fd-btn-enter');
                        dShare.removeClass('fd-btn-disabled').addClass('fd-btn-enter');

                        setTimeout(function () {
                            dShare.click();
                            //shareContent();
                        }, 2000);

                    }
                }
            });
        };

        // run
        FBUtil.after(function (FB) {
            FB.getLoginStatus(function (response) {
                if (response.status === 'connected') {
                    FB.api('/me/friends', function (response) {
                        var friendData = response.data;

                        if (friendData && friendData.length > 0) {
                            $.each(friendData, function (index, value) {
                                if (index <= FRIENDS_FACEBOOK_MAX) {
                                    friendsArr.push({
                                        dataIndex: index,
                                        friendName: value.name,
                                        friendID: value.id,
                                        friendoImgUrl: 'http://graph.facebook.com/' + value.id + '/picture?width=140&height=140'
                                    });
                                } else {
                                    return false;
                                }
                            });
                        }
                        else {
                            friendsArr.push({
                                dataIndex: 0,
                                friendName: '原味千尋',
                                friendID: 0,
                                friendoImgUrl: 'http://friendoprod.blob.core.windows.net/ipage/mission/collecttemplate/daintiest/images/logo_daintiest.png'
                            });
                        }

                        // init slot
                        initSlotMc();
                    });
                } else {
                    //(friendo not login) or (fb not login)
                    FB.login(function () { }, { scope: 'public_profile,email,user_friends,publish_actions,publish_stream' });
                }
            });
        });

        //scroll this block to top
        $('body,html').animate({ scrollTop: dModule.offset().top });

    },
    secretGeneratorResult: function (dModule) {
        var dModule = $(dModule);
        var dBtnGoLettory = dModule.find('.btnGoLettory');

        dBtnGoLettory.click(function (e) {
            e.preventDefault();

            if ($('a[name="missionLottery"]')) {
                $('body,html').animate({ scrollTop: $('a[name="missionLottery"]').offset().top - 50 });
			}
        });

    },
    secretGeneratorSummary: function (dModule) {
        var dModule = $(dModule);
        // slot
        var dSlotMc = dModule.find('.slotMc');
        var dRedBall = dSlotMc.find('.redBall');
        var dSlotBox = dSlotMc.find('ul.wheel');

        //var dSend = dModule.find('.btnFormSubmit');
        var dShare = dModule.find('.btnFormShare');
        var dTabs = dModule.find('.stepList');
        // setting data
        var friendsArr = [];
        var imagesArr = JSON.parse(dModule.find('input.imageData').val() || [])
        // 從facebook取回來的朋友上限
        var FRIENDS_FACEBOOK_MAX = 300;
        // { 1: 文字, 2: 朋友 }
        var OPTION_TEXT = '1';
        var OPTION_FACEBOOK_FRIENDS = '2';
        // 要拉霸的朋友數量
        var FRIENDO_SLOT_MAX = 50;
        // 要存檔的資料
        var resultData = [];
        // jSlots的結束方式與我們不一樣，要自己控制結束。
        var slotCounts = 0;

        // function
        // get Friendos random list for next friends slot
        var getRandomList = function (maxLength) {
            //進來的list長度跟朋友清單的總長度相較之下 取小值
            var maxCount = Math.min(maxLength, friendsArr.length);
            var shuffleFriendList = friendsArr.slice();
            shuffle(shuffleFriendList);
            var randomFriendsDatas = shuffleFriendList.slice(0, maxCount);
            return randomFriendsDatas;
        };

        //洗牌
        var shuffle = function (array) {
            var counter = array.length, temp, index;

            // While there are elements in the array
            while (counter > 0) {
                // Pick a random index
                index = Math.floor(Math.random() * counter);

                // Decrease counter by 1
                counter--;

                // And swap the last element with it
                temp = array[counter];
                array[counter] = array[index];
                array[index] = temp;
            }

            return array;
        };

        var setTab = function (stepNumber) {
            dTabs.find('li').removeClass('active');
            dTabs.find('.step' + stepNumber).addClass('active');
        };

        var initSlotMc = function () {

            // 呼叫 jSlots 前，要先初始化 li 個數
            $.each(dSlotBox.filter('[data-optiontype="' + OPTION_FACEBOOK_FRIENDS + '"]'), function (index, item) {
                var dSelf = $(this);
                var len = Math.min(FRIENDO_SLOT_MAX, friendsArr.length);
                var i;
                var slotHtml = '';
                dSelf.empty();
                for (i = 0; i < len; i++) {
                    slotHtml += '

';
                }
                dSelf.append(slotHtml);
            });

            //bind slotbar animate and link true button
            dRedBall.click(function (e) {
                var dSelf = $(this);
                var dSlotMc = dSelf.closest('.slotMc');
                var dDefaultLogo = dSlotMc.find('.defaultLogo');
                var dArrows = dSlotMc.find('.arrows');
                var dBarBtn = dSlotMc.find('.barBlock');
                var dPlayFancy = dSlotMc.find('.playFancy');

                if (dSelf.hasClass('disabled')) {
                    return false;
                }

                dSelf.addClass('disabled');

                if (dDefaultLogo.length > 0) {
                    dDefaultLogo.slideDown(400, function () {
                        dDefaultLogo.remove();
                    });
                    //isFirstTurn = false;
                }

                if (dArrows.is(":visible")) {
                    dArrows.fadeOut();
                }

                dBarBtn.animate({ 'height': '200' }, 500, 'easeInOutElastic')
                        .animate({ 'height': '310' }, 1800, 'easeOutBounce');

                dRedBall.animate({ 'width': '120' }, 500, 'easeInOutElastic')
                        .animate({ 'width': '80' }, 1800, 'easeOutBounce', function () {
                    dRedBall.removeClass('disabled');
                });

                //click the true slot button
                dPlayFancy.delay(700).click();

            });

            var shareContent = function(callback) {
                var dFacebookRoot = $('#fb-root');
                var isFeed = !dFacebookRoot.hasClass('processing');

                if (!isFeed) { return false; }
                dFacebookRoot.addClass('processing');

                var shareAnswer = {
                    Description: $.map(resultData, function (item, index) {
                        return item.data('value') || '';
                    }).join(' '),
                    PicURL: shuffle(imagesArr)[0]
                };
                var scroll = $(window).scrollTop();
                FBUtil.after(function (FB) {
                    FB.ui({
                        method: 'feed',
                        message: shareAnswer.Description,
                        name: shareAnswer.Description,
                        link: location.href,
                        caption: '原味千尋　炭燒辣味乳酪絲是重口味秘密的最佳拍檔。你想跟誰說祕密？',
                        description: '跟知心好友無話不談嗎?是否有些話對朋友說不出口?讓粉多進入你的潛意識，挖掘你埋藏已久的小秘密!勇敢對朋友說出秘密，就有機會抽中最強乾杯零食 - 原味千尋炭燒辣味酪絲。原味千尋用16倍的真心，換1份乾杯的感動。',
                        picture: shareAnswer.PicURL
                    },
                    function(response) {
                        if (typeof(callback)=='function') {
                            callback();
                        };

						if ($(window).scrollTop() != scroll && $(window).scrollTop() == 0) {
							$(window).scrollTop(scroll);
						}

                        dFacebookRoot.removeClass('processing');
                    });
                });
                $(window).one('scroll',function () {
                    if ($(window).scrollTop() != scroll) {
                        $(window).scrollTop(scroll);
                    }
                });
            };

            //share button
            dModule.on('click', '.btnFormShare.fd-btn-enter', function (e) {
                e.preventDefault();
                shareContent();
            });

            //slot init
            dSlotBox.jSlots({
                number: 1,
                spinner: '.playFancy',
                easing: 'easeOutSine',
                time: 2000, //轉多久
                loops: 2, //轉幾圈
                onStart: function () {
                    var dSelf = $(this);
                    var dSlot = dSelf.find('ul');
                    var dSlotItem = dSelf.find('li');
                    // 有幾個li就取幾筆，幾個li由初始化那邊決定
                    var randomFriendsArr = getRandomList(dSlotItem.length);
                    // 清空結果
                    resultData = [];
                    // 初始化次數
                    slotCounts = 0;
                    // disabled button
                    //dSend.removeClass('fd-btn-enter').addClass('fd-btn-disabled');
                    dShare.removeClass('fd-btn-enter').addClass('fd-btn-disabled');

                    if (dSlot.data('optiontype') == OPTION_FACEBOOK_FRIENDS) {
                        $.each(dSelf.find('li'), function (index, item) {
                            var friendsItem;
                            // 多的li，帶入第一筆資料，讓第一筆與最後一筆都是同一個資料(遵循plugin的方式)'
                            if (index >= randomFriendsArr.length) {
                                friendsItem = randomFriendsArr[0];
                            } else {
                                friendsItem = randomFriendsArr[index];
                            }
                            $(item).data('value', friendsItem.friendName || '')
                                   .data('index', friendsItem.dataIndex || '')
                                   .find('img')
                                   .attr('src', friendsItem.friendoImgUrl || '')
                                   .end()
                                   .find('span.name')
                                   .html(friendsItem.friendName || '');
                        });
                    }
                },
                onWin: function (winCount, winners, finalNumbers) { },
                onEnd: function (finalNumbers) {
                    //this不是ul,是上一層element???
                    var dSelf = $(this);
                    var dSlot = dSelf.find('ul.wheel');
                    var dSlotItem = dSlot.find('li');

                    // 取得選項位置
                    var optionIndex = dModule.find('ul.wheel').index(dSlot);
                    // 取得答案內容
                    resultData[optionIndex] = dSelf.find('li').eq(finalNumbers - 1)

                    slotCounts++;
                    if (slotCounts === dSlotBox.length) {
                        setTab(2);
                        //dSend.removeClass('fd-btn-disabled').addClass('fd-btn-enter');
                        dShare.removeClass('fd-btn-disabled').addClass('fd-btn-enter');

                        setTimeout(function () {
                            dShare.click();
                            //shareContent();
                        }, 2000);

                    }
                }
            });
        };

        // run
        FBUtil.after(function (FB) {
            FB.getLoginStatus(function (response) {
                if (response.status === 'connected') {
                    FB.api('/me/friends', function (response) {
                        var friendData = response.data;

                        if (friendData && friendData.length > 0) {
                            $.each(friendData, function (index, value) {
                                if (index <= FRIENDS_FACEBOOK_MAX) {
                                    friendsArr.push({
                                        dataIndex: index,
                                        friendName: value.name,
                                        friendID: value.id,
                                        friendoImgUrl: 'http://graph.facebook.com/' + value.id + '/picture?width=140&height=140'
                                    });
                                } else {
                                    return false;
                                }
                            });
                        }
                        else {
                            friendsArr.push({
                                dataIndex: 0,
                                friendName: '原味千尋',
                                friendID: 0,
                                friendoImgUrl: 'http://friendoprod.blob.core.windows.net/ipage/mission/collecttemplate/daintiest/images/logo_daintiest.png'
                            });
                        }

                        // init slot
                        initSlotMc();
                    });
                } else {
                    //(friendo not login) or (fb not login)
                    FB.login(function () { }, { scope: 'public_profile,email,user_friends,publish_actions,publish_stream' });
                }
            });
        });
    }
};

(function () {
    var doWhileExist = function (ModuleID, objFunction) {
        var dTarget = document.getElementById(ModuleID);
        if (dTarget) {
            objFunction(dTarget);
        }
    };
    $.when(
        $.getScript('http://friendoprod.blob.core.windows.net/ipage/mission/content_generator/daintiest/lib/jquery.easing.1.3.js'),
        $.getScript('http://friendoprod.blob.core.windows.net/ipage/mission/content_generator/daintiest/lib/jquery.jSlots.custom.js')
    ).done(function () {
        //place your code here, the scripts are all loaded
        doWhileExist('suitID_secretGeneratorExecution', missionDaintiest.module.secretGeneratorExecution);
        doWhileExist('suitID_secretGeneratorResult', missionDaintiest.module.secretGeneratorResult);
        doWhileExist('suitID_secretGeneratorSummary', missionDaintiest.module.secretGeneratorSummary);
    }).fail(function () {
        alert('任務腳本載入失敗！ \n請稍後嘗試或聯繫客服人員。');
    });
})();<commit_msg>cookie handling, facebook api handleing<commit_after><|endoftext|>


<commit_before>/* @flow */
import React, { PropTypes } from 'react';
import { Text } from 'react-native';
import styles from '../styles'

function NavTitle({ style, children }: Object): React.Element {
  return (
    <Text style={[styles.navBarTitleText, style]}>
      {children}
    </Text>
  )
}

NavTitle.propTypes = {
  style: Text.propTypes.style,
  children: PropTypes.node,
}

NavTitle.defaultProps = {
  style: {},
}

export { NavTitle }
<commit_msg>Add accessibilty trait header to NavTitle<commit_after>/* @flow */
import React, { PropTypes } from 'react';
import { Text } from 'react-native';
import styles from '../styles'

function NavTitle({ style, children }: Object): React.Element {
  return (
    <Text accessibilityTraits="header" style={[styles.navBarTitleText, style]}>
      {children}
    </Text>
  )
}

NavTitle.propTypes = {
  style: Text.propTypes.style,
  children: PropTypes.node,
}

NavTitle.defaultProps = {
  style: {},
}

export { NavTitle }
<|endoftext|>


<commit_before>/*
 * gfw.js - resource.js
 * Copyright(c) 2011 Aaron Hedges <aaron@dashron.com>
 * MIT Licensed
 */

"use strict";

var url_module = require('url');
var http_module = require('http');
var path_module = require('path');

var accept_header_component = require('./accept_header');
var view_component = require('./view');
var View = view_component.View;
var Router = require('./router').RegexRouter;

var _resources = {};
var _resource_dir = path_module.normalize(__dirname + '/../resources');

/**
 * Set the default directory  to load resources from
 *
 * @param {String} directory
 */
var set_resource_dir = exports.directory = function (directory) {
	_resource_dir = path_module.normalize(directory);
};

/**
 * Free up the memory of all resources built within this module
 */
var clear = exports.clear = function () {
	_resources = {};
};

/**
 * Constructs a resource.
 * The name provided is how the resource finds it's definition.
 * If the resource has already been constructed, it will return that one.
 *
 * @todo : document all the description properties
 * @param {Object} description
 */
var Resource = exports.Resource = function Resource (name, description) {
	if (typeof _resources[name.toLowerCase()] === "object") {
		return _resources[name.toLowerCase()];
	}

	var key = null;

	this.name = name;
	this.directory = path_module.normalize(__dirname + '/../resources/' + this.name);
	this.template_dir = this.directory + '/templates/';
	this.config = description.config;
	this.router = description.router;
	this.resources = {};
	this.onRequest = description.onRequest;

	for (key in description.properties) {
		this[key] = description.properties[key];
	}

	for (key in description.dependencies) {
		this.resources[key] = description.dependencies[key];
	}

	if (typeof description.construct === "function") {
		description.construct.call(this);
	}

	_resources[name] = this;
};

Resource.prototype.name = '';
Resource.prototype.config = null;
Resource.prototype.directory = '';
Resource.prototype.template_dir = '';

/**
 * Locates the code best associated with the provided uri_bundle, and writes any necessary data to the view
 *
 * @param  {Object} uri_bundle
 * @param  {View} view
 */
Resource.prototype.request = function (uri_bundle, view) {
	var key = null;
	var template_dir = this.template_dir;
	var route_resource = this;
	var _self = this;

	// Allow direct urls for shorthand. Assume a GET request in this case
	if (typeof uri_bundle === "string") {
		uri_bundle = {
			uri : uri_bundle,
			method : 'GET'
		};
	}

	// clean up the success path, and have processRoute return a promise
	this.processRoute(uri_bundle, function (route, route_resource) {
		// If the template provided is actually a server response, we need to build the very first view
		if (view instanceof http_module.ServerResponse) {
			var response = view;

			view = new View();

			if (typeof route.options !== "object") {
				route.options = {};
			}
			// The default content type will be text/html
			if (!Array.isArray(route.options.modes)) {
				route.options.modes = ["text/html"];
			}

			view.setContentType(accept_header_component.getContentType(uri_bundle.headers.accept, route.options.modes));
			view.setResponse(response);
		}

		// assume that we want to load templates directly from this route, no matter the data provided
		view.dir = route_resource.template_dir;

		// route, allowing this to point to the original resource, and provide some helper utils
		if (typeof route[uri_bundle.method] === "function") {
			// Route to the proper method
			process.nextTick(function() {
				if (typeof route_resource.onRequest === "function") {
					route_resource.onRequest(uri_bundle, view, route[uri_bundle.method], route_resource);
				} else if (typeof this.onRequest === "function") {
					this.onRequest(uri_bundle, view, route[uri_bundle.method], route_resource);
				} else {
					route[uri_bundle.method].call(route_resource, uri_bundle, view);
				}
			});
		} else if (typeof route['default'] === "function") {
			// Allow default routes in case the method is not explicitly stated
			process.nextTick(function() {
				if (typeof route_resource.onRequest === "function") {
					route_resource.onRequest(uri_bundle, view, route.default, route_resource);
				} else if (typeof this.onRequest === "function") {
					this.onRequest(uri_bundle, view, route.default, route_resource);
				} else {
					route.default.call(route_resource, uri_bundle, view);
				}
			});
		} else {
			// Handle the unsupportedMethod http response, and provide the allowed methods
			var keys = [];
			['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'].forEach(function (option) {
				if (typeof route[option] === "function") {
					keys.push(option);
				}
			});
			view.statusUnsupportedMethod(keys);
		}
	}, function () {
		// Yes, dying horribly is intended here. This bit of code should never be executed
		// The ideal technique would be for your root resource to have an unmatched_route property
		// that handles all 404's
		throw new Error('route not found :' + uri_bundle.uri + ' [' + _self.name + ']');
	});
};

/**
 * Locates the appropriate route for the provided uri_bundle, and provides it to the sucess callback.
 * If not found, the failure callback is called with no parameters
 *
 * @param  {Object} uri_bundle
 * @param {Function} success takes two parameters, the route, and the resource associated with the route
 * @param {Function} failure
 * @return {Objectt}
 */
Resource.prototype.processRoute = function (uri_bundle, success, failure) {
	var route = this.router.getRoute(uri_bundle);
	var key = null;

	if (!route) {
		// attempt each child, see if you can find a proper route
		for (key in this.resources) {
			// we don't want to fail immediately, so we ignore the fail scenario here. fail should only be called on teh top most resource
			if (this.resources[key].processRoute(uri_bundle, success)) {
				return true;
			}
		}
	}

	if (!route) {
		route = this.router.getDefaultRoute(uri_bundle);
	}

	// todo: should these be called on next tick?
	if (route) {
		success(route, this);
		return true;
	}

	if (failure) {
		failure();
	}

	return false;
};
<commit_msg>set default http method<commit_after>/*
 * gfw.js - resource.js
 * Copyright(c) 2011 Aaron Hedges <aaron@dashron.com>
 * MIT Licensed
 */

"use strict";

var url_module = require('url');
var http_module = require('http');
var path_module = require('path');

var accept_header_component = require('./accept_header');
var view_component = require('./view');
var View = view_component.View;
var Router = require('./router').RegexRouter;

var _resources = {};
var _resource_dir = path_module.normalize(__dirname + '/../resources');

/**
 * Set the default directory  to load resources from
 *
 * @param {String} directory
 */
var set_resource_dir = exports.directory = function (directory) {
	_resource_dir = path_module.normalize(directory);
};

/**
 * Free up the memory of all resources built within this module
 */
var clear = exports.clear = function () {
	_resources = {};
};

/**
 * Constructs a resource.
 * The name provided is how the resource finds it's definition.
 * If the resource has already been constructed, it will return that one.
 *
 * @todo : document all the description properties
 * @param {Object} description
 */
var Resource = exports.Resource = function Resource (name, description) {
	if (typeof _resources[name.toLowerCase()] === "object") {
		return _resources[name.toLowerCase()];
	}

	var key = null;

	this.name = name;
	this.directory = path_module.normalize(__dirname + '/../resources/' + this.name);
	this.template_dir = this.directory + '/templates/';
	this.config = description.config;
	this.router = description.router;
	this.resources = {};
	this.onRequest = description.onRequest;

	for (key in description.properties) {
		this[key] = description.properties[key];
	}

	for (key in description.dependencies) {
		this.resources[key] = description.dependencies[key];
	}

	if (typeof description.construct === "function") {
		description.construct.call(this);
	}

	_resources[name] = this;
};

Resource.prototype.name = '';
Resource.prototype.config = null;
Resource.prototype.directory = '';
Resource.prototype.template_dir = '';

/**
 * Locates the code best associated with the provided uri_bundle, and writes any necessary data to the view
 *
 * @param  {Object} uri_bundle
 * @param  {View} view
 */
Resource.prototype.request = function (uri_bundle, view) {
	var key = null;
	var template_dir = this.template_dir;
	var route_resource = this;
	var _self = this;

	// Allow direct urls for shorthand. Assume a GET request in this case
	if (typeof uri_bundle === "string") {
		uri_bundle = {
			uri : uri_bundle,
			method : 'GET'
		};
	}

	if (typeof uri_bundle.method != "string") {
		uri_bundle.method = "GET";
	}

	// clean up the success path, and have processRoute return a promise
	this.processRoute(uri_bundle, function (route, route_resource) {
		// If the template provided is actually a server response, we need to build the very first view
		if (view instanceof http_module.ServerResponse) {
			var response = view;

			view = new View();

			if (typeof route.options !== "object") {
				route.options = {};
			}
			// The default content type will be text/html
			if (!Array.isArray(route.options.modes)) {
				route.options.modes = ["text/html"];
			}

			view.setContentType(accept_header_component.getContentType(uri_bundle.headers.accept, route.options.modes));
			view.setResponse(response);
		}

		// assume that we want to load templates directly from this route, no matter the data provided
		view.dir = route_resource.template_dir;

		// route, allowing this to point to the original resource, and provide some helper utils
		if (typeof route[uri_bundle.method] === "function") {
			// Route to the proper method
			process.nextTick(function() {
				if (typeof route_resource.onRequest === "function") {
					route_resource.onRequest(uri_bundle, view, route[uri_bundle.method], route_resource);
				} else if (typeof this.onRequest === "function") {
					this.onRequest(uri_bundle, view, route[uri_bundle.method], route_resource);
				} else {
					route[uri_bundle.method].call(route_resource, uri_bundle, view);
				}
			});
		} else if (typeof route['default'] === "function") {
			// Allow default routes in case the method is not explicitly stated
			process.nextTick(function() {
				if (typeof route_resource.onRequest === "function") {
					route_resource.onRequest(uri_bundle, view, route.default, route_resource);
				} else if (typeof this.onRequest === "function") {
					this.onRequest(uri_bundle, view, route.default, route_resource);
				} else {
					route.default.call(route_resource, uri_bundle, view);
				}
			});
		} else {
			// Handle the unsupportedMethod http response, and provide the allowed methods
			var keys = [];
			['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'].forEach(function (option) {
				if (typeof route[option] === "function") {
					keys.push(option);
				}
			});
			view.statusUnsupportedMethod(keys);
		}
	}, function () {
		// Yes, dying horribly is intended here. This bit of code should never be executed
		// The ideal technique would be for your root resource to have an unmatched_route property
		// that handles all 404's
		throw new Error('route not found :' + uri_bundle.uri + ' [' + _self.name + ']');
	});
};

/**
 * Locates the appropriate route for the provided uri_bundle, and provides it to the sucess callback.
 * If not found, the failure callback is called with no parameters
 *
 * @param  {Object} uri_bundle
 * @param {Function} success takes two parameters, the route, and the resource associated with the route
 * @param {Function} failure
 * @return {Objectt}
 */
Resource.prototype.processRoute = function (uri_bundle, success, failure) {
	var route = this.router.getRoute(uri_bundle);
	var key = null;

	if (!route) {
		// attempt each child, see if you can find a proper route
		for (key in this.resources) {
			// we don't want to fail immediately, so we ignore the fail scenario here. fail should only be called on teh top most resource
			if (this.resources[key].processRoute(uri_bundle, success)) {
				return true;
			}
		}
	}

	if (!route) {
		route = this.router.getDefaultRoute(uri_bundle);
	}

	// todo: should these be called on next tick?
	if (route) {
		success(route, this);
		return true;
	}

	if (failure) {
		failure();
	}

	return false;
};
<|endoftext|>


<commit_before>describe("go.components.views", function() {
  var testHelpers = go.testHelpers,
      oneElExists = testHelpers.oneElExists;

  var views = go.components.views;

  beforeEach(function() {
    $('body').append("

");
  });

  afterEach(function() {
    $('#dummy').remove();
  });

  describe(".LabelView", function() {
    var LabelView = views.LabelView;

    var label;

    beforeEach(function() {
      $('#dummy')
        .width(200)
        .height(300)
        .css('position', 'absolute')
        .offset({left: 50, top: 100});

      label = new LabelView({
        my: 'right bottom',
        at: 'left top',
        of: $('#dummy'),
        text: 'foo'
      });
    });

    describe(".render", function() {
      it("should attach the label to the element", function() {
        label.render();
        assert(oneElExists('#dummy .label'));
      });

      it("should position the label at the given position", function() {
        label.render();
        assert.deepEqual(
          $('#dummy .label').offset(),
          {left: 21, top: 73});
      });

      it("should render the given text", function() {
        label.render();
        assert.equal($('#dummy .label').text(), 'foo');
      });
    });
  });
});
<commit_msg>Fix inconsistent go.components.views.LabelView test<commit_after>describe("go.components.views", function() {
  var testHelpers = go.testHelpers,
      oneElExists = testHelpers.oneElExists;

  var views = go.components.views;

  beforeEach(function() {
    $('body').append("

");
  });

  afterEach(function() {
    $('#dummy').remove();
  });

  describe(".LabelView", function() {
    var LabelView = views.LabelView;

    var label;

    var assertOffset = function(el, expectedOffset) {
      var offset = $(el).offset();
      assert.closeTo(offset.top, expectedOffset.top, 1);
      assert.closeTo(offset.left, expectedOffset.left, 1);
    };

    beforeEach(function() {
      $('#dummy')
        .width(200)
        .height(300)
        .css('position', 'absolute')
        .offset({left: 50, top: 100});

      label = new LabelView({
        my: 'right bottom',
        at: 'left top',
        of: $('#dummy'),
        text: 'foo'
      });
    });

    describe(".render", function() {
      it("should attach the label to the element", function() {
        label.render();
        assert(oneElExists('#dummy .label'));
      });

      it("should position the label at the given position", function() {
        label.render();
        assertOffset('#dummy .label', {left: 21, top: 73});
      });

      it("should render the given text", function() {
        label.render();
        assert.equal($('#dummy .label').text(), 'foo');
      });
    });
  });
});
<|endoftext|>