<commit_before>if(typeof langs == "undefined") {
    var langs = {};
}

//Lithuanian language
langs.lt = {
    'ikaeasy_link': 'https://www.facebook.com/ikariam.easy',
    'movements':{
        all: 'all',
        transport: 'gabenti prekes',
        plunder: 'plėšti',
//I hope lithuanian letters ąčęėįšųū will work. :)
        blockade: 'blokada',
        deployarmy: 'dislokuoti karius',
        deployfleet: 'dislokuoti laivyną',
        defend: 'ginti miestą',
        defend_port: 'ginti uostą',
        occupy: 'okupuoti',
        trade: 'trade'
    },
    'char_day': 'd',
    'char_hour': 'h',
    'char_minute': 'm',
    'char_second': 's',
    'WillBeFullIn': 'Be filled in',
    'per_day': "Per dieną",
    'per_week': "Per savaitę",
    'per_month': "Per mėnesį",
    'left': "liko",
    'hours': "valandų(os)",
    'days': "dienų(dienos)",
    'months': "mėnesiai(uo)",
    'years': "metai",
    'MessagePlaceholder': "Click CTRL + Enter to send message",
    'ConfirmDowngrade': "Patvirtink griovimą",
    'Circular_message': "Siųsti masinę žinutę",
    'Ally_members': "Aljansas",
    'Attack_All': "Visi",
    'Attack_Half': "Pusė",
    'Attack_Nothing': "Nulis",
    'Required': "Reikalinga:",
    'Next_Level': "Kitam lygiui:",
    'Messages': "Žinutės",
    'Agora': "Agora",
    'Treaty': "Sutartis",
    'Alliance': "Forumas",
    'Transport': "Gabenti prekes!",
    'Send_General_Log': "Siųsti ataskaitą aljansui",
    'Send_General_Log_Header': "Alijanso narių atakos",

    'options': {
        'transport': {
            'header': 'Transportavimo tipai',
            'original': 'GameForge',
            'ikaeasy': 'IkaEasy'
        },
        'movement_tabs': {
            'header': 'Movement tabs',
            'enable': 'active',
            'disable': 'unactive'
        },
        'gold_per_hour': {
            'header': 'Gold сonsumption',
            'enable': 'active',
            'disable': 'unactive'
        },
        'island_ap': {
            'header': 'Action points on island view',
            'enable': 'active',
            'disable': 'unactive'
        },
        'development': {
            'overview': "IkaEasy is non-commercial, OpenSource project. Want to help? Contribute on GitHub, report about bugs and suggest features in Facebook.
Developing Ikariam Easy takes lot of time: weekends, evenings and of course nights. You can say thanks and help me stay awake by buying me coffee :)

 Switch to IkaEasy V2 and get updates earlier!"
        }
    },

    'ikalogs': {
        'save_log': 'Išsaugoti ataskaitą',
        'analyze': 'Analizuoti',
        'each': 'Kiekvieną',
        'help_bw': 'Atskirti kiekvieną raunda kableliu, arba raundų diapozoną brūkšniu.',

        'get_info': 'Gaunama info',
        'get_round': 'Gaunama ataskaita',
        'saving': 'Saugoma ataskaita',

        'saving_success': 'Ataskaitos saugojimas atliktas sėkmingai.',
        'open_report': 'Atidaryti ataskaitą',

        'saving_failed': 'Ataskaitos išsaugoti nepavyko',
        'repeat': 'Bandyti dar',

        'auth': {
            'not_logged': 'Tu neprisijungęs prie Ikalogs'
        },

        'types': {
            'full': 'Pilna ataskaita',
            'short': 'Trumpa ataskaita',
            'last': 'Paskutinis raundas',
            'each': 'Kas N raundas',
            'between': 'Nurodyti raundai'
        }
    },

    'Transporter': 'Gabentojas'
};
<commit_msg>translation fixes: lt<commit_after>if(typeof langs == "undefined") {
    var langs = {};
}

//Lithuanian language
langs.lt = {
    'ikaeasy_link': 'https://www.facebook.com/ikariam.easy',
    'movements':{
        all: 'all',
        transport: 'gabenti prekes',
        plunder: 'plėšti',
//I hope lithuanian letters ąčęėįšųū will work. :)
        blockade: 'blokada',
        deployarmy: 'dislokuoti karius',
        deployfleet: 'dislokuoti laivyną',
        defend: 'ginti miestą',
        defend_port: 'ginti uostą',
        occupy: 'okupuoti',
        trade: 'trade'
    },
    'char_day': 'd',
    'char_hour': 'h',
    'char_minute': 'm',
    'char_second': 's',
    'WillBeFullIn': 'Be filled in',
    'per_day': "Per dieną",
    'per_week': "Per savaitę",
    'per_month': "Per mėnesį",
    'left': "liko",
    'hours': "valandų(os)",
    'days': "dienų(dienos)",
    'months': "mėnesiai(uo)",
    'years': "metai",
    'MessagePlaceholder': "Click CTRL + Enter to send message",
    'ConfirmDowngrade': "Patvirtink griovimą",
    'Circular_message': "Siųsti masinę žinutę",
    'Ally_members': "Aljansas",
    'Attack_All': "Visi",
    'Attack_Half': "Pusė",
    'Attack_Nothing': "Nulis",
    'Required': "Reikalinga:",
    'Next_Level': "Kitam lygiui:",
    'Messages': "Žinutės",
    'Agora': "Agora",
    'Treaty': "Sutartis",
    'Alliance': "Forumas",
    'Transport': "Gabenti prekes!",
    'Send_General_Log': "Siųsti ataskaitą aljansui",
    'Send_General_Log_Header': "Aljanso narių atakos",

    'options': {
        'transport': {
            'header': 'Transportavimo tipai',
            'original': 'GameForge',
            'ikaeasy': 'IkaEasy'
        },
        'movement_tabs': {
            'header': 'Movement tabs',
            'enable': 'active',
            'disable': 'unactive'
        },
        'gold_per_hour': {
            'header': 'Gold сonsumption',
            'enable': 'active',
            'disable': 'unactive'
        },
        'island_ap': {
            'header': 'Action points on island view',
            'enable': 'active',
            'disable': 'unactive'
        },
        'development': {
            'overview': "IkaEasy is non-commercial, OpenSource project. Want to help? Contribute on GitHub, report about bugs and suggest features in Facebook.
Developing Ikariam Easy takes lot of time: weekends, evenings and of course nights. You can say thanks and help me stay awake by buying me coffee :)

 Switch to IkaEasy V2 and get updates earlier!"
        }
    },

    'ikalogs': {
        'save_log': 'Išsaugoti ataskaitą',
        'analyze': 'Analizuoti',
        'each': 'Kiekvieną',
        'help_bw': 'Atskirti kiekvieną raunda kableliu, arba raundų diapozoną brūkšniu.',

        'get_info': 'Gaunama info',
        'get_round': 'Gaunama ataskaita',
        'saving': 'Saugoma ataskaita',

        'saving_success': 'Ataskaitos saugojimas atliktas sėkmingai.',
        'open_report': 'Atidaryti ataskaitą',

        'saving_failed': 'Ataskaitos išsaugoti nepavyko',
        'repeat': 'Bandyti dar',

        'auth': {
            'not_logged': 'Tu neprisijungęs prie Ikalogs'
        },

        'types': {
            'full': 'Pilna ataskaita',
            'short': 'Trumpa ataskaita',
            'last': 'Paskutinis raundas',
            'each': 'Kas N raundas',
            'between': 'Nurodyti raundai'
        }
    },

    'Transporter': 'Gabentojas'
};
<|endoftext|>


<commit_before>const express = require( "express" );
const request = require( "request" );
const bodyParser = require( "body-parser" );
const config = require( "config" );
const dotenv = require( "dotenv" );
const path = require( "path" );

const app = express();

dotenv.config( { path: path.join( __dirname, "../.env" ) } );
dotenv.load();

app.set( "port", process.env.PORT || 1337 );
// app.set( "view engine", "ejs" );
app.use( bodyParser.urlencoded( { extended: false } ) );
app.use( bodyParser.json() );
app.use( express.static( "public" ) );

const APP_SECRET = ( process.env.APP_SECRET ) ?
  process.env.APP_SECRET :
  config.get( "appSecret" );

// Arbitrary value used to validate a webhook
const VALIDATION_TOKEN = ( process.env.VALIDATION_TOKEN ) ?
  ( process.env.VALIDATION_TOKEN ) :
  config.get( "validationToken" );

// Generate a page access token for your page from the App Dashboard
const PAGE_ACCESS_TOKEN = ( process.env.PAGE_ACCESS_TOKEN ) ?
  ( process.env.PAGE_ACCESS_TOKEN ) :
  config.get( "pageAccessToken" );

// URL where the app is running (include protocol). Used to point to scripts and
// assets located at this address.
const SERVER_URL = ( process.env.SERVER_URL ) ?
  ( process.env.SERVER_URL ) :
  config.get( "serverURL" );

if ( !( APP_SECRET && VALIDATION_TOKEN && PAGE_ACCESS_TOKEN && SERVER_URL ) ) {
    console.error( "Missing config values" );
    process.exit( 1 );
}

// TODO: Error handling if not 200
app.get( "/", ( req, res ) => {
    res.status( 200 ).send( "This is a Fb chatbot." );
} );

app.get( "/fb_webhook", ( req, res ) => {
    if ( req.query[ "hub.mode" ] === "subscribe" &&
      req.query[ "hub.verify_token" ] === VALIDATION_TOKEN ) {
        console.log( "Validating webhook" );
        res.status( 200 ).send( req.query[ "hub.challenge" ] );
    } else {
        console.error( "Failed validation. Make sure the validation tokens match." );
        res.sendStatus( 403 );
    }
} );

app.post( "/fb_webhook", ( req, res ) => {
    const data = req.body;

    if ( data.object === "page" ) {
        data.entry.forEach( ( pageEntry ) => {
            pageEntry.messaging.forEach( ( messagingEvent ) => {
                if ( messagingEvent.message ) {
                    receivedMessage( messagingEvent );
                } else if ( messagingEvent.delivery ) {
                    receivedDeliveryConfirmation( messagingEvent );
                } else if ( messagingEvent.postback ) {
                    receivedPostback( messagingEvent );
                } else if ( messagingEvent.read ) {
                    receivedMessageRead( messagingEvent );
                } else {
                    console.log( "Webhook received unknown messagingEvent: ", messagingEvent );
                }
            } );
        } );

        res.sendStatus( 200 ); // send back a 200 within 20 seconds.
    }
} );

const verifyRequestSignature = ( req, res, buf ) => {
    const signature = req.headers[ "x-hub-signature" ];

    if ( !signature ) {
        console.error( "Couldn't validate the signature." );
    } else {
        const elements = signature.split( "=" );
        const signatureHash = elements[ 1 ];

        const expectedHash = crypto.createHmac( "sha1", APP_SECRET )
                        .update( buf )
                        .digest( "hex" );

        if ( signatureHash !== expectedHash ) {
            throw new Error( "Couldn't validate the request signature." );
        }
    }
};

// TODO: Seperate auth in different folder so can import
app.use( bodyParser.json( { verify: verifyRequestSignature } ) );

/*
 * Message Event
 *
 * This event is called when a message is sent to your page. The 'message'
 * object format can vary depending on the kind of message that was received.
 * Read more at https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-received
 *
 * For this example, we're going to echo any text that we get. If we get some
 * special keywords ('button', 'generic', 'receipt'), then we'll send back
 * examples of those bubbles to illustrate the special message bubbles we've
 * created. If we receive a message with an attachment (image, video, audio),
 * then we'll simply confirm that we've received the attachment.
 *
 */
function receivedMessage( event ) {
    const senderID = event.sender.id;
    const recipientID = event.recipient.id;
    const timeOfMessage = event.timestamp;
    const message = event.message;

    console.log( "Received message for user %d and page %d at %d with message:",
    senderID, recipientID, timeOfMessage );

    const isEcho = message.is_echo;
    const messageId = message.mid;
    const appId = message.app_id;
    const metadata = message.metadata;

  // You may get a text or attachment but not both
    const messageText = message.text;
    const messageAttachments = message.attachments;
    const quickReply = message.quick_reply;

    if ( isEcho ) {
    // Just logging message echoes to console
        console.log( "Received echo for message %s and app %d with metadata %s",
        messageId, appId, metadata );
        return;
    } else if ( quickReply ) {
        const quickReplyPayload = quickReply.payload;
        console.log( "Quick reply for message %s with payload %s",
        messageId, quickReplyPayload );

        sendTextMessage( senderID, "Quick reply tapped" );
        return;
    }

    if ( messageText ) {
    // If we receive a text message, check to see if it matches any special
    // keywords and send back the corresponding example. Otherwise, just echo
    // the text we received.
        switch ( messageText ) {
    //   case 'image':
    //     sendImageMessage(senderID);
    //     break;

    //   case 'gif':
    //     sendGifMessage(senderID);
    //     break;

    //   case 'audio':
    //     sendAudioMessage(senderID);
    //     break;

    //   case 'video':
    //     sendVideoMessage(senderID);
    //     break;

    //   case 'file':
    //     sendFileMessage(senderID);
    //     break;

    //   case 'button':
    //     sendButtonMessage(senderID);
    //     break;

    //   case 'generic':
    //     sendGenericMessage(senderID);
    //     break;

    //   case 'receipt':
    //     sendReceiptMessage(senderID);
    //     break;

    //   case 'quick reply':
    //     sendQuickReply(senderID);
    //     break;

    //   case 'read receipt':
    //     sendReadReceipt(senderID);
    //     break;

    //   case 'typing on':
    //     sendTypingOn(senderID);
    //     break;

    //   case 'typing off':
    //     sendTypingOff(senderID);
    //     break;

        default:
            sendTextMessage( senderID, messageText );
        }
    } else if ( messageAttachments ) {
        sendTextMessage( senderID, "Message with attachment received" );
    }
}

/*
 * Delivery Confirmation Event
 *
 * This event is sent to confirm the delivery of a message. Read more about
 * these fields at https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-delivered
 *
 */
function receivedDeliveryConfirmation( event ) {
    // const senderID = event.sender.id;
    // const recipientID = event.recipient.id;
    const delivery = event.delivery;
    const messageIDs = delivery.mids;
    const watermark = delivery.watermark;
    // const sequenceNumber = delivery.seq;

    if ( messageIDs ) {
        messageIDs.forEach( ( messageID ) => {
            console.log( "Received delivery confirmation for message ID: %s",
        messageID );
        } );
    }

    console.log( "All message before %d were delivered.", watermark );
}

/*
 * Postback Event
 *
 * This event is called when a postback is tapped on a Structured Message.
 * https://developers.facebook.com/docs/messenger-platform/webhook-reference/postback-received
 *
 */
function receivedPostback( event ) {
    const senderID = event.sender.id;
    const recipientID = event.recipient.id;
    const timeOfPostback = event.timestamp;

  // The 'payload' param is a developer-defined field which is set in a postback
  // button for Structured Messages.
    const payload = event.postback.payload;

    console.log( "Received postback for user %d and page %d with payload '%s' " +
    "at %d", senderID, recipientID, payload, timeOfPostback );

  // When a postback is called, we'll send a message back to the sender to
  // let them know it was successful
    sendTextMessage( senderID, "Postback called" );
}

/*
 * Message Read Event
 *
 * This event is called when a previously-sent message has been read.
 * https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-read
 *
 */
function receivedMessageRead( event ) {
    // const senderID = event.sender.id;
    // const recipientID = event.recipient.id;

  // All messages before watermark (a timestamp) or sequence have been seen.
    const watermark = event.read.watermark;
    const sequenceNumber = event.read.seq;

    console.log( "Received message read event for watermark %d and sequence " +
    "number %d", watermark, sequenceNumber );
}

function sendTextMessage( recipientId, messageText ) {
    const messageData = {
        recipient: {
            id: recipientId,
        },
        message: {
            text: messageText,
            metadata: "DEVELOPER_DEFINED_METADATA",
        },
    };

    callSendAPI( messageData );
}

function callSendAPI( messageData ) {
    request( {
        uri: "https://graph.facebook.com/v2.9/me/messages",
        qs: { access_token: PAGE_ACCESS_TOKEN },
        method: "POST",
        json: messageData,

    }, ( error, response, body ) => {
        if ( !error && response.statusCode === 200 ) {
            const recipientId = body.recipient_id;
            const messageId = body.message_id;

            if ( messageId ) {
                console.log( "Successfully sent message with id %s to recipient %s",
              messageId, recipientId );
            } else {
                console.log( "Successfully called Send API for recipient %s",
              recipientId );
            }
        } else {
            console.error( "Failed calling Send API",
            response.statusCode, response.statusMessage, body.error );
        }
    } );
}

app.listen( app.get( "port" ), () => {
    console.log( "Fb chatbot is running on port", app.get( "port" ) );
} );

module.exports = app;
<commit_msg>(fix) Test server messages<commit_after>const express = require( "express" );
const request = require( "request" );
const bodyParser = require( "body-parser" );
const config = require( "config" );
const dotenv = require( "dotenv" );
const path = require( "path" );

const app = express();

dotenv.config( { path: path.join( __dirname, "../.env" ) } );
dotenv.load();

app.set( "port", process.env.PORT || 1337 );
// app.set( "view engine", "ejs" );
app.use( bodyParser.urlencoded( { extended: false } ) );
app.use( bodyParser.json() );
app.use( express.static( "public" ) );

const APP_SECRET = ( process.env.APP_SECRET ) ?
  process.env.APP_SECRET :
  config.get( "appSecret" );

// Arbitrary value used to validate a webhook
const VALIDATION_TOKEN = ( process.env.VALIDATION_TOKEN ) ?
  ( process.env.VALIDATION_TOKEN ) :
  config.get( "validationToken" );

// Generate a page access token for your page from the App Dashboard
const PAGE_ACCESS_TOKEN = ( process.env.PAGE_ACCESS_TOKEN ) ?
  ( process.env.PAGE_ACCESS_TOKEN ) :
  config.get( "pageAccessToken" );

// URL where the app is running (include protocol). Used to point to scripts and
// assets located at this address.
const SERVER_URL = ( process.env.SERVER_URL ) ?
  ( process.env.SERVER_URL ) :
  config.get( "serverURL" );

if ( !( APP_SECRET && VALIDATION_TOKEN && PAGE_ACCESS_TOKEN && SERVER_URL ) ) {
    console.error( "Missing config values" );
    process.exit( 1 );
}

// TODO: Error handling if not 200
app.get( "/", ( req, res ) => {
    res.status( 200 ).send( "This is a Fb chatbot." );
} );

app.get( "/fb_webhook", ( req, res ) => {
    if ( req.query[ "hub.mode" ] === "subscribe" &&
      req.query[ "hub.verify_token" ] === VALIDATION_TOKEN ) {
        console.log( "Validating webhook" );
        res.status( 200 ).send( req.query[ "hub.challenge" ] );
    } else {
        console.error( "Failed validation. Make sure the validation tokens match." );
        res.sendStatus( 403 );
    }
} );

app.post( "/fb_webhook", ( req, res ) => {
    const data = req.body;
    console.log('whats req body here?', data);

    if ( data.object === "page" ) {
        data.entry.forEach( ( pageEntry ) => {
            pageEntry.messaging.forEach( ( messagingEvent ) => {
                console.log( "what is msgevent.msg?", messagingEvent.message );
                if ( messagingEvent.message ) {
                    receivedMessage( messagingEvent );
                } else if ( messagingEvent.delivery ) {
                    receivedDeliveryConfirmation( messagingEvent );
                } else if ( messagingEvent.postback ) {
                    receivedPostback( messagingEvent );
                } else if ( messagingEvent.read ) {
                    receivedMessageRead( messagingEvent );
                } else {
                    console.log( "Webhook received unknown messagingEvent: ", messagingEvent );
                }
            } );
        } );

        res.sendStatus( 200 ); // send back a 200 within 20 seconds.
    }
} );

const verifyRequestSignature = ( req, res, buf ) => {
    const signature = req.headers[ "x-hub-signature" ];

    if ( !signature ) {
        console.error( "Couldn't validate the signature." );
    } else {
        const elements = signature.split( "=" );
        const signatureHash = elements[ 1 ];

        const expectedHash = crypto.createHmac( "sha1", APP_SECRET )
                        .update( buf )
                        .digest( "hex" );

        if ( signatureHash !== expectedHash ) {
            throw new Error( "Couldn't validate the request signature." );
        }
    }
};

// TODO: Seperate auth in different folder so can import
app.use( bodyParser.json( { verify: verifyRequestSignature } ) );

/*
 * Message Event
 *
 * This event is called when a message is sent to your page. The 'message'
 * object format can vary depending on the kind of message that was received.
 * Read more at https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-received
 *
 * For this example, we're going to echo any text that we get. If we get some
 * special keywords ('button', 'generic', 'receipt'), then we'll send back
 * examples of those bubbles to illustrate the special message bubbles we've
 * created. If we receive a message with an attachment (image, video, audio),
 * then we'll simply confirm that we've received the attachment.
 *
 */
function receivedMessage( event ) {
    const senderID = event.sender.id;
    const recipientID = event.recipient.id;
    const timeOfMessage = event.timestamp;
    const message = event.message;

    console.log( "Received message for user %d and page %d at %d with message:",
    senderID, recipientID, timeOfMessage );
    console.log( 'what is message received?', message );
    const isEcho = message.is_echo;
    const messageId = message.mid;
    const appId = message.app_id;
    const metadata = message.metadata;

  // You may get a text or attachment but not both
    const messageText = message.text;
    const messageAttachments = message.attachments;
    const quickReply = message.quick_reply;

    if ( isEcho ) {
    // Just logging message echoes to console
        console.log( "Received echo for message %s and app %d with metadata %s",
        messageId, appId, metadata );
        return;
    } else if ( quickReply ) {
        const quickReplyPayload = quickReply.payload;
        console.log( "Quick reply for message %s with payload %s",
        messageId, quickReplyPayload );

        sendTextMessage( senderID, "Quick reply tapped" );
        return;
    }

    if ( messageText ) {
    // If we receive a text message, check to see if it matches any special
    // keywords and send back the corresponding example. Otherwise, just echo
    // the text we received.
        switch ( messageText ) {
    //   case 'image':
    //     sendImageMessage(senderID);
    //     break;

    //   case 'gif':
    //     sendGifMessage(senderID);
    //     break;

    //   case 'audio':
    //     sendAudioMessage(senderID);
    //     break;

    //   case 'video':
    //     sendVideoMessage(senderID);
    //     break;

    //   case 'file':
    //     sendFileMessage(senderID);
    //     break;

    //   case 'button':
    //     sendButtonMessage(senderID);
    //     break;

    //   case 'generic':
    //     sendGenericMessage(senderID);
    //     break;

    //   case 'receipt':
    //     sendReceiptMessage(senderID);
    //     break;

    //   case 'quick reply':
    //     sendQuickReply(senderID);
    //     break;

    //   case 'read receipt':
    //     sendReadReceipt(senderID);
    //     break;

    //   case 'typing on':
    //     sendTypingOn(senderID);
    //     break;

    //   case 'typing off':
    //     sendTypingOff(senderID);
    //     break;

        default:
            sendTextMessage( senderID, messageText );
        }
    } else if ( messageAttachments ) {
        sendTextMessage( senderID, "Message with attachment received" );
    }
}

/*
 * Delivery Confirmation Event
 *
 * This event is sent to confirm the delivery of a message. Read more about
 * these fields at https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-delivered
 *
 */
function receivedDeliveryConfirmation( event ) {
    // const senderID = event.sender.id;
    // const recipientID = event.recipient.id;
    const delivery = event.delivery;
    const messageIDs = delivery.mids;
    const watermark = delivery.watermark;
    // const sequenceNumber = delivery.seq;

    if ( messageIDs ) {
        messageIDs.forEach( ( messageID ) => {
            console.log( "Received delivery confirmation for message ID: %s",
        messageID );
        } );
    }

    console.log( "All message before %d were delivered.", watermark );
}

/*
 * Postback Event
 *
 * This event is called when a postback is tapped on a Structured Message.
 * https://developers.facebook.com/docs/messenger-platform/webhook-reference/postback-received
 *
 */
function receivedPostback( event ) {
    const senderID = event.sender.id;
    const recipientID = event.recipient.id;
    const timeOfPostback = event.timestamp;

  // The 'payload' param is a developer-defined field which is set in a postback
  // button for Structured Messages.
    const payload = event.postback.payload;

    console.log( "Received postback for user %d and page %d with payload '%s' " +
    "at %d", senderID, recipientID, payload, timeOfPostback );

  // When a postback is called, we'll send a message back to the sender to
  // let them know it was successful
    sendTextMessage( senderID, "Postback called" );
}

/*
 * Message Read Event
 *
 * This event is called when a previously-sent message has been read.
 * https://developers.facebook.com/docs/messenger-platform/webhook-reference/message-read
 *
 */
function receivedMessageRead( event ) {
    // const senderID = event.sender.id;
    // const recipientID = event.recipient.id;

  // All messages before watermark (a timestamp) or sequence have been seen.
    const watermark = event.read.watermark;
    const sequenceNumber = event.read.seq;

    console.log( "Received message read event for watermark %d and sequence " +
    "number %d", watermark, sequenceNumber );
}

function sendTextMessage( recipientId, messageText ) {
    const messageData = {
        recipient: {
            id: recipientId,
        },
        message: {
            text: messageText,
            metadata: "DEVELOPER_DEFINED_METADATA",
        },
    };

    callSendAPI( messageData );
}

function callSendAPI( messageData ) {
    request( {
        uri: "https://graph.facebook.com/v2.9/me/messages",
        qs: { access_token: PAGE_ACCESS_TOKEN },
        method: "POST",
        json: messageData,

    }, ( error, response, body ) => {
        if ( !error && response.statusCode === 200 ) {
            const recipientId = body.recipient_id;
            const messageId = body.message_id;

            if ( messageId ) {
                console.log( "Successfully sent message with id %s to recipient %s",
              messageId, recipientId );
            } else {
                console.log( "Successfully called Send API for recipient %s",
              recipientId );
            }
        } else {
            console.error( "Failed calling Send API",
            response.statusCode, response.statusMessage, body.error );
        }
    } );
}

app.listen( app.get( "port" ), () => {
    console.log( "Fb chatbot is running on port", app.get( "port" ) );
} );

module.exports = app;
<|endoftext|>


<commit_before>import React from 'react';
import expect from 'expect';
import { shallow, mount } from 'enzyme';
import { spy } from 'sinon';
import { assert } from 'chai';
import { jsdom } from 'jsdom';
import { Footer } from '../index';

describe('Footer', () => {
  const moreLinks = [{ text: 'One', href: 'one'}, { text: 'Two', href: 'two'}];

  describe('Footer tests', function () {
    beforeEach(function () {
      const testLinks = [{ text: 'First', href: 'first'}, { text: 'Second', href: 'second'}];
      this.wrapper = shallow(<Footer links={testLinks} />);
    });

    it('should render the Footer', function () {
      expect(this.wrapper.node.type).toEqual('footer');
    });

    it('has the correct Copyright text + year', function () {
      const year = new Date().getFullYear();
      expect(this.wrapper.node.props.children[1].props.children)
      .toEqual(['Copyright © ',year,' Pearson Education Inc. All Rights Reserved.']);
    });

    it('correctly passes the light prop', function () {
      const lightProp = shallow(<Footer links={moreLinks} light />);
      expect(this.wrapper.instance().props.light).toEqual(false);
      expect(lightProp.instance().props.light).toEqual(true);
    });

    it('calls componentDidMount()', function () {
      const document = jsdom('');
      Object.keys(document.defaultView).forEach((property) => {
        if (typeof global[property] === 'undefined') {
          global[property] = document.defaultView[property];
        }
      });
      const componentDidMountSpy = spy(Footer.prototype, 'componentDidMount');
      const wrapper = mount(<Footer links={moreLinks} />);

      assert.ok(Footer.prototype.componentDidMount.calledOnce);

      componentDidMountSpy.restore();
    });

  });
});
<commit_msg>fix: Footer unit test<commit_after>import React from 'react';
import expect from 'expect';
import { shallow, mount } from 'enzyme';
import { spy } from 'sinon';
import { assert } from 'chai';
import { jsdom } from 'jsdom';
import { Footer } from '../index';

describe('Footer', () => {
  const moreLinks = [{ text: 'One', href: 'one'}, { text: 'Two', href: 'two'}];

  describe('Footer tests', function () {
    beforeEach(function () {
      const testLinks = [{ text: 'First', href: 'first'}, { text: 'Second', href: 'second'}];
      this.wrapper = shallow(<Footer links={testLinks} copyrightText="CR text"  />);
    });

    it('should render the Footer', function () {
      expect(this.wrapper.node.type).toEqual('footer');
    });

    it('has the correct Copyright text + year', function () {
      const year = new Date().getFullYear();
      expect(this.wrapper.node.props.children[1].props.children)
      .toEqual(['Copyright © ',year,' ', 'CR text']);
    });

    it('correctly passes the light prop', function () {
      const lightProp = shallow(<Footer links={moreLinks} light />);
      expect(this.wrapper.instance().props.light).toEqual(false);
      expect(lightProp.instance().props.light).toEqual(true);
    });

    it('calls componentDidMount()', function () {
      const document = jsdom('');
      Object.keys(document.defaultView).forEach((property) => {
        if (typeof global[property] === 'undefined') {
          global[property] = document.defaultView[property];
        }
      });
      const componentDidMountSpy = spy(Footer.prototype, 'componentDidMount');
      const wrapper = mount(<Footer links={moreLinks} />);

      assert.ok(Footer.prototype.componentDidMount.calledOnce);

      componentDidMountSpy.restore();
    });

  });
});
<|endoftext|>


<commit_before>export default vood.Obj({
	////-----------------------------------------------------------------------------------------
	// Increment of the upcoming controller-ids
	id: 0,
	////-----------------------------------------------------------------------------------------
	// Existing classes collection
	list: {},
	////-----------------------------------------------------------------------------------------
	// Controller Instances
	anons: {},
	////-----------------------------------------------------------------------------------------
	// Ids of the controller, which need a init() call
	inits: [],
	////-----------------------------------------------------------------------------------------
	// Flag if the garbagecollection should be enabled
	garbageCollection: true,
	////-----------------------------------------------------------------------------------------
	// Flag if the garbagecollection should be enabled
	systemLoaded: false,
	////-----------------------------------------------------------------------------------------
	// Init adds needed runloop jobs (garbage collection, and async calling of controller-inits)
	init: function(){
		if( this.garbageCollection !== false ){
			this.addJob( {callback: this.garbage} );
		}
		this.addJob( {callback: this.callInits} );
	},
	////-----------------------------------------------------------------------------------------
	// Creates the controller-instance of the class, returns the html
	create: function( path, content, opt ){
		var id = ++this.id;
		this.inits.push( id );
		this.anons[ id ]                 = this.getEntity( path );
		this.anons[ id ]._meta.uid       = id;
		if( content ){
			// @TODO logic is propably wrong
			_.merge( this.anons[ id ].content, content );
		}
		this.anons[ id ].view            = vood.viewHelper.create( path, opt );
		this.anons[ id ].view.controller = this.anons[ id ];
		vood.utilHelper.safeCall( this.anons[ id ], 'construct' );
		vood.utilHelper.safeCall( this.anons[ id ].view, 'construct' );
		var html                            = this.anons[ id ].view._compileComplete();
		return {uid: id, html: html};
	},
	////-----------------------------------------------------------------------------------------
	// Is a runloop jobs, for calling the init of new controllers, needs to be called after instanciating
	callInits: function( force ){
		var result = [];
		var found  = false;
		for( var i = 0; i < vood.controllerHelper.inits.length; i++ ){
			var id = vood.controllerHelper.inits[ i ];
			found = true;
			if( vood.controllerHelper.controllerExists( id )) {
				if( ! vood.controllerHelper.anons[ id ]._loadModel( id )) {
					vood.utilHelper.safeCall( vood.controllerHelper.anons[ id ], 'init' );
					vood.utilHelper.safeCall( vood.controllerHelper.anons[ id ].view, 'init' );
				} else {
					// If modelloading is not finished, then we want to keep the id
					result.push( id );
				}
			}
		}
		if( found && result.length === 0 && !vood.controllerHelper.systemLoaded){ // Finish should only be called when inits where called, and models are finished
			vood.controllerHelper.systemLoaded = true;
			vood.helperHelper.callInits();
		}
		vood.controllerHelper.inits = result;
	},
	////-----------------------------------------------------------------------------------------
	// Checks if controller exists, when it doesnt, it warns the console
	controllerExists: function( id ) {
		if( vood.controllerHelper.anons[ id ] ) {
			return true;
		}
		console.error( 'Controller does not exist', id );
	},
	////-----------------------------------------------------------------------------------------
	// Returns the class
	getEntity: function( path ){
		if( !this.list[ path ] ){
			console.log( 'Controller ' + path + ' does not exist' );
			vood.Controller( path, {_meta: { pseudo: true , path: path}} );
		}
		return _.cloneDeep( this.list[ path ] );
	},
	////-----------------------------------------------------------------------------------------
	// returns instances of fitting controllers
	get: function( path ) {
		return this._iterate( path );
	},
	////-----------------------------------------------------------------------------------------
	// Calls the matching controllers with the function
	call: function( path, call, args ) {
		return this._iterate( path, call, args );
	},
	////-----------------------------------------------------------------------------------------
	// Metafunction for getting and calling controllers, please only use this function with get/call. the api mostlikely changes
	_iterate: function( path, call, args ){
		var id = window.parseInt( path, 10 );
		if( isNaN( id ) || !this.anons[ id ] ){ // Check is not necessary, could be done with iteration as well, but this is faster at large scale
			var result = [];
			for( var i in this.anons ){
				if( this.anons.hasOwnProperty( i ) ){
					// path can either be the namespace, or the uid
					if( this.anons[ i ]._meta.path == path || path == '*' || this.anons[ i ]._meta.uid == path ){
						if( call ) {
							var value = vood.utilHelper.safeCall( this.anons[ i ], call, args );
							result.push( value );
						} else {
							result.push( this.anons[ i ] );
						}
					}
				}
			}
			return result;
		} else {
			if( call ) {
				return [ vood.utilHelper.safeCall( this.anons[ id ], call, args ) ];
			} else {
				return [ this.anons[ id ]];
			}
		}
	},
	////-----------------------------------------------------------------------------------------
	// Checks if the instanciated controllers are represented in the dom
	// @TODO implementation
	garbage: function(){

	}
});<commit_msg>[vood] added @each as a wildcard<commit_after>export default vood.Obj({
	////-----------------------------------------------------------------------------------------
	// Increment of the upcoming controller-ids
	id: 0,
	////-----------------------------------------------------------------------------------------
	// Existing classes collection
	list: {},
	////-----------------------------------------------------------------------------------------
	// Controller Instances
	anons: {},
	////-----------------------------------------------------------------------------------------
	// Ids of the controller, which need a init() call
	inits: [],
	////-----------------------------------------------------------------------------------------
	// Flag if the garbagecollection should be enabled
	garbageCollection: true,
	////-----------------------------------------------------------------------------------------
	// Flag if the garbagecollection should be enabled
	systemLoaded: false,
	////-----------------------------------------------------------------------------------------
	// Init adds needed runloop jobs (garbage collection, and async calling of controller-inits)
	init: function(){
		if( this.garbageCollection !== false ){
			this.addJob( {callback: this.garbage} );
		}
		this.addJob( {callback: this.callInits} );
	},
	////-----------------------------------------------------------------------------------------
	// Creates the controller-instance of the class, returns the html
	create: function( path, content, opt ){
		var id = ++this.id;
		this.inits.push( id );
		this.anons[ id ]                 = this.getEntity( path );
		this.anons[ id ]._meta.uid       = id;
		if( content ){
			// @TODO logic is propably wrong
			_.merge( this.anons[ id ].content, content );
		}
		this.anons[ id ].view            = vood.viewHelper.create( path, opt );
		this.anons[ id ].view.controller = this.anons[ id ];
		vood.utilHelper.safeCall( this.anons[ id ], 'construct' );
		vood.utilHelper.safeCall( this.anons[ id ].view, 'construct' );
		var html                            = this.anons[ id ].view._compileComplete();
		return {uid: id, html: html};
	},
	////-----------------------------------------------------------------------------------------
	// Is a runloop jobs, for calling the init of new controllers, needs to be called after instanciating
	callInits: function( force ){
		var result = [];
		var found  = false;
		for( var i = 0; i < vood.controllerHelper.inits.length; i++ ){
			var id = vood.controllerHelper.inits[ i ];
			found = true;
			if( vood.controllerHelper.controllerExists( id )) {
				if( ! vood.controllerHelper.anons[ id ]._loadModel( id )) {
					vood.utilHelper.safeCall( vood.controllerHelper.anons[ id ], 'init' );
					vood.utilHelper.safeCall( vood.controllerHelper.anons[ id ].view, 'init' );
				} else {
					// If modelloading is not finished, then we want to keep the id
					result.push( id );
				}
			}
		}
		if( found && result.length === 0 && !vood.controllerHelper.systemLoaded){ // Finish should only be called when inits where called, and models are finished
			vood.controllerHelper.systemLoaded = true;
			vood.helperHelper.callInits();
		}
		vood.controllerHelper.inits = result;
	},
	////-----------------------------------------------------------------------------------------
	// Checks if controller exists, when it doesnt, it warns the console
	controllerExists: function( id ) {
		if( vood.controllerHelper.anons[ id ] ) {
			return true;
		}
		console.error( 'Controller does not exist', id );
	},
	////-----------------------------------------------------------------------------------------
	// Returns the class
	getEntity: function( path ){
		if( !this.list[ path ] ){
			console.log( 'Controller ' + path + ' does not exist' );
			vood.Controller( path, {_meta: { pseudo: true , path: path}} );
		}
		return _.cloneDeep( this.list[ path ] );
	},
	////-----------------------------------------------------------------------------------------
	// returns instances of fitting controllers
	get: function( path ) {
		return this._iterate( path );
	},
	////-----------------------------------------------------------------------------------------
	// Calls the matching controllers with the function
	call: function( path, call, args ) {
		return this._iterate( path, call, args );
	},
	////-----------------------------------------------------------------------------------------
	// Metafunction for getting and calling controllers, please only use this function with get/call. the api mostlikely changes
	_iterate: function( path, call, args ){
		var id = window.parseInt( path, 10 );
		if( isNaN( id ) || !this.anons[ id ] ){ // Check is not necessary, could be done with iteration as well, but this is faster at large scale
			var result = [];
			for( var i in this.anons ){
				if( this.anons.hasOwnProperty( i ) ){
					// path can either be the namespace, or the uid
					if( this.anons[ i ]._meta.path == path || path == '@each' || path == '*' || this.anons[ i ]._meta.uid == path ){
						if( call ) {
							var value = vood.utilHelper.safeCall( this.anons[ i ], call, args );
							result.push( value );
						} else {
							result.push( this.anons[ i ] );
						}
					}
				}
			}
			return result;
		} else {
			if( call ) {
				return [ vood.utilHelper.safeCall( this.anons[ id ], call, args ) ];
			} else {
				return [ this.anons[ id ]];
			}
		}
	},
	////-----------------------------------------------------------------------------------------
	// Checks if the instanciated controllers are represented in the dom
	// @TODO implementation
	garbage: function(){

	}
});<|endoftext|>


<commit_before>var jira = {};
function jiraSyncError(message, statusCode, errorText) {
    this.message = message;
    this.name = "SyncError";
    this.statusCode = statusCode;
    this.errorText = errorText;
}
jiraSyncError.prototype = Object.create(Error.prototype);

yasoon.app.load("com.yasoon.jira", new function () { //jshint ignore:line
	var self = this;
	jira = this;
	jira.CONST_PULL_RESULTS = 25;

	jira.data = {
		ownUser: null,
		projects: null,
		issueTypes: null
	};

	jira.firstTime = true;
	var startSync = new Date();
	var currentPage = 1;

	this.lifecycle = function(action, oldVersion, newVersion) {
		if (action === yasoon.lifecycle.Upgrade) {
			if (newVersion == "0.6.1") {
				var oAuthServices = yasoon.app.getOAuthServices();
				if (oAuthServices.length > 0) {
					var authedServices = oAuthServices.filter(function (element) { return !!element.accessToken; });
					if (authedServices.length > 0) {
						var data = yasoon.setting.getAppParameter('settings');
						if (data) {
							data = JSON.parse(data);
							data.currentService = authedServices[0].serviceName;
							yasoon.setting.setAppParameter('settings', JSON.stringify(data));
						}
					}
				}
			}
		}
	};

	this.init = function () {
		jira.settings = new JiraSettingController();
		jira.notifications = new JiraNotificationController();
		jira.ribbons = new JiraRibbonController();
		jira.contacts = new JiraContactController();
		jira.icons = new JiraIconController();
		jira.queue = new jiraSyncQueue();
		jira.issues = new JiraIssueController();

		yasoon.addHook(yasoon.setting.HookCreateRibbon, jira.ribbons.createRibbon);
		yasoon.addHook(yasoon.notification.HookRenderNotificationAsync, jira.notifications.renderNotification);
		yasoon.addHook(yasoon.feed.HookCreateUserComment, jira.notifications.addComment);
		yasoon.addHook(yasoon.setting.HookRenderSettingsContainer, jira.settings.renderSettingsContainer);
		yasoon.addHook(yasoon.setting.HookSaveSettings, jira.settings.saveSettings);

		yasoon.feed.addFilter([
			{
				name: 'By Project',
				jsonPath: 'fields.project.id',
				label: function (name, id) {
					if (jira.data.projects) {
						var proj = $.grep(jira.data.projects, function (p) { return p.id === id; })[0];
						if (proj) {
							return proj.name;
						}
					}
					return id;
				}
			},
			{
				name: 'By Type',
				jsonPath: 'fields.issuetype.id',
				label: function (name, id) {
					if (jira.data.issueTypes) {
						var issueType = $.grep(jira.data.issueTypes, function (i) { return i.id === id; })[0];
						if (issueType) {
							return issueType.name;
						}
					}
					return id;
				}
			},
			{
				name: 'By Reporter',
				jsonPath: 'fields.reporter.emailAddress',
				label: function (name, id) {
					//if (jira.data.issueTypes) {
					//	var issueType = $.grep(jira.data.issueTypes, function (i) { return i.id === id; })[0];
					//	if (issueType) {
					//		return issueType.name;
					//	}
					//}
					return id;
				}
			}
		]);
		yasoon.on("sync", jira.sync);
		yasoon.app.on("oAuthSuccess", jira.sync);
		yasoon.periodicCallback(300, jira.sync);
	};

	this.sync = function () {

		if (!jira.settings.currentService || !yasoon.app.isOAuthed(jira.settings.currentService)) {
			return;
		}

		return jira.queue.add(self.syncData);
	};

	this.syncData = function () {
	    var currentTs = new Date().getTime();
	    var oldTs = jira.settings.lastSync.getTime() - 1000;

	    jiraLog('Sync starts: ' + new Date().toISOString());
	    return self.initData()
        .then(function () {
            jira.issues.refreshBuffer();
            return self.syncStream('/activity?streams=update-date+BETWEEN+' + oldTs + '+' + currentTs);
        })
        .then(function () {
            return jira.notifications.processChildren();
        })
        .then(function () {
            return jira.notifications.processCommentEdits();
        })
        .then(function () {
            jira.settings.setLastSync(startSync);
            jiraLog('Sync done: ' + new Date().toISOString());
        })
        .catch(jiraSyncError, function (error) {
            jiraLog('Sync Error:', error);
        })
        .catch(function (e) {
            yasoon.util.log(e.message, yasoon.util.severity.error, getStackTrace(e));
        });
	};

	//Handle Sync Event
	this.syncStream = function (url, maxResults, currentPage) {
	    //Defaults
	    maxResults = maxResults || jira.CONST_PULL_RESULTS;
	    currentPage = currentPage || 0;

        //Add maxResults to URL
	    if (url.indexOf('?') === -1)
	        url += '?maxResults=' + maxResults;
	    else
			url += '&maxResults=' + maxResults;

		jiraLog('Get Activity Stream');

		return jiraGet(url)
        .then(function (xmlData) {
            var jsonPage = jiraXmlToJson(new DOMParser().parseFromString(xmlData, "text/xml"));
            jiraLog('page:', jsonPage);
            if (jsonPage.feed && jsonPage.feed.entry) {
                //Adjust Data. If it's only 1 entry it's an object instead of array
                if (!$.isArray(jsonPage.feed.entry)) {
                    jsonPage.feed.entry = [jsonPage.feed.entry];
                }
                return jsonPage.feed.entry;
            }
            return [];
        })
        .each(function (feedEntry, index) {
            jiraLog('Item #' + index + ':', feedEntry);
            //Only for jira!
            if (feedEntry['atlassian:application'] && feedEntry['atlassian:application']['#text'].toLowerCase().indexOf('jira') > -1) {
            	var notif = jira.notifications.createNotification(feedEntry);
				if(notif)
					return notif.save();
            }
        })
        .then(function (entries) {
            //Determine if paging is required
            if (entries && entries.length > 0) {
                var lastObj = entries[entries.length - 1];
                var lastObjDate = new Date(lastObj.updated['#text']);

                if (entries.length == maxResults && jira.settings.lastSync < lastObjDate && currentPage < 2) {
                    return self.syncStream('/activity?streams=update-date+BEFORE+' + (lastObjDate.getTime() - 2000), maxResults, currentPage + 1);
                }
            }
        });

	};

	this.initData = function () {
        //First Get Own User Data
	    jiraLog('Get Own Data');
	    if (jira.firstTime) {
	        return jiraGet('/rest/api/2/myself')
            .then(function (ownUserData) {
                jira.data.ownUser = JSON.parse(ownUserData);

            })
            .then(function () {
                //Second get all projects
                jiraLog('Get Projects');
                return jiraGet('/rest/api/2/project')
                .then(function (projectData) {
                    var projects = JSON.parse(projectData);
                    jira.data.projects = [];
                    return projects;
                })
                .each(function (project) {
                    //Get detailed information for each project
                    return jiraGet('/rest/api/2/project/' + project.id)
                    .then(function (singleProject) {
                        var proj = JSON.parse(singleProject);
                        jira.data.projects.push(proj);
                    });
                });
            })
            .then(function () {
                //Third get all issue types
                jiraLog('Get Issuetypes');
                return jiraGet('/rest/api/2/issuetype')
                .then(function (issueTypes) {
                    jira.data.issueTypes = JSON.parse(issueTypes);
                    jira.settings.updateData();
                    jira.firstTime = false;
                });
            });
	    } else {
	        return Promise.resolve();
	    }
	};

	this.handleError = function (data, statusCode, result, errorText, cbkParam) {
	    self.handleErrorSoft(data, statusCode, result, errorText, cbkParam);
	    jira.SyncInProcess = false;
	};

	this.handleErrorSoft = function (data, statusCode, result, errorText, cbkParam) {
	    console.log(statusCode + ' || ' + errorText + ' || ' + result + ' || ' + data);

	    //Detect if oAuth token has become invalid
	    if (statusCode == 401 && result == 'oauth_problem=token_rejected') {
	        yasoon.app.invalidateOAuthToken(jira.settings.currentService);
	    }

	    if (cbkParam && cbkParam.fail) {
	        cbkParam.fail();
	    }
	};

}); //jshint ignore:line
//@ sourceURL=http://Jira/jira.js<commit_msg>add sync server activity log entry<commit_after>var jira = {};
function jiraSyncError(message, statusCode, errorText) {
    this.message = message;
    this.name = "SyncError";
    this.statusCode = statusCode;
    this.errorText = errorText;
}
jiraSyncError.prototype = Object.create(Error.prototype);

yasoon.app.load("com.yasoon.jira", new function () { //jshint ignore:line
	var self = this;
	jira = this;
	jira.CONST_PULL_RESULTS = 25;

	jira.data = {
		ownUser: null,
		projects: null,
		issueTypes: null
	};

	jira.firstTime = true;
	jira.firstSyncedNotifications = 0;
	var startSync = new Date();
	var currentPage = 1;

	this.lifecycle = function(action, oldVersion, newVersion) {
		if (action === yasoon.lifecycle.Upgrade) {
			if (newVersion == "0.6.1") {
				var oAuthServices = yasoon.app.getOAuthServices();
				if (oAuthServices.length > 0) {
					var authedServices = oAuthServices.filter(function (element) { return !!element.accessToken; });
					if (authedServices.length > 0) {
						var data = yasoon.setting.getAppParameter('settings');
						if (data) {
							data = JSON.parse(data);
							data.currentService = authedServices[0].serviceName;
							yasoon.setting.setAppParameter('settings', JSON.stringify(data));
						}
					}
				}
			}
		}
	};

	this.init = function () {
		jira.settings = new JiraSettingController();
		jira.notifications = new JiraNotificationController();
		jira.ribbons = new JiraRibbonController();
		jira.contacts = new JiraContactController();
		jira.icons = new JiraIconController();
		jira.queue = new jiraSyncQueue();
		jira.issues = new JiraIssueController();

		yasoon.addHook(yasoon.setting.HookCreateRibbon, jira.ribbons.createRibbon);
		yasoon.addHook(yasoon.notification.HookRenderNotificationAsync, jira.notifications.renderNotification);
		yasoon.addHook(yasoon.feed.HookCreateUserComment, jira.notifications.addComment);
		yasoon.addHook(yasoon.setting.HookRenderSettingsContainer, jira.settings.renderSettingsContainer);
		yasoon.addHook(yasoon.setting.HookSaveSettings, jira.settings.saveSettings);

		yasoon.feed.addFilter([
			{
				name: 'By Project',
				jsonPath: 'fields.project.id',
				label: function (name, id) {
					if (jira.data.projects) {
						var proj = $.grep(jira.data.projects, function (p) { return p.id === id; })[0];
						if (proj) {
							return proj.name;
						}
					}
					return id;
				}
			},
			{
				name: 'By Type',
				jsonPath: 'fields.issuetype.id',
				label: function (name, id) {
					if (jira.data.issueTypes) {
						var issueType = $.grep(jira.data.issueTypes, function (i) { return i.id === id; })[0];
						if (issueType) {
							return issueType.name;
						}
					}
					return id;
				}
			},
			{
				name: 'By Reporter',
				jsonPath: 'fields.reporter.emailAddress',
				label: function (name, id) {
					//if (jira.data.issueTypes) {
					//	var issueType = $.grep(jira.data.issueTypes, function (i) { return i.id === id; })[0];
					//	if (issueType) {
					//		return issueType.name;
					//	}
					//}
					return id;
				}
			}
		]);
		yasoon.on("sync", jira.sync);
		yasoon.app.on("oAuthSuccess", jira.sync);
		yasoon.periodicCallback(300, jira.sync);
	};

	this.sync = function () {

		if (!jira.settings.currentService || !yasoon.app.isOAuthed(jira.settings.currentService)) {
			return;
		}

		return jira.queue.add(self.syncData);
	};

	this.syncData = function () {
	    var currentTs = new Date().getTime();
	    var oldTs = jira.settings.lastSync.getTime() - 1000;

	    jiraLog('Sync starts: ' + new Date().toISOString());
	    return self.initData()
        .then(function () {
            jira.issues.refreshBuffer();
            return self.syncStream('/activity?streams=update-date+BETWEEN+' + oldTs + '+' + currentTs);
        })
        .then(function () {
            return jira.notifications.processChildren();
        })
        .then(function () {
            return jira.notifications.processCommentEdits();
        })
        .then(function () {
            jira.settings.setLastSync(startSync);
            jiraLog('Sync done: ' + new Date().toISOString());
			if(jira.firstTime) {
				yasoon.util.logActivity('syncCompleted', JSON.stringify({
					app: 'com.yasoon.jira',
					count: jira.firstSyncedNotifications
				}));
			}
        })
        .catch(jiraSyncError, function (error) {
            jiraLog('Sync Error:', error);
        })
        .catch(function (e) {
            yasoon.util.log(e.message, yasoon.util.severity.error, getStackTrace(e));
        });
	};

	//Handle Sync Event
	this.syncStream = function (url, maxResults, currentPage) {
	    //Defaults
	    maxResults = maxResults || jira.CONST_PULL_RESULTS;
	    currentPage = currentPage || 0;

        //Add maxResults to URL
	    if (url.indexOf('?') === -1)
	        url += '?maxResults=' + maxResults;
	    else
			url += '&maxResults=' + maxResults;

		jiraLog('Get Activity Stream');

		return jiraGet(url)
        .then(function (xmlData) {
            var jsonPage = jiraXmlToJson(new DOMParser().parseFromString(xmlData, "text/xml"));
            jiraLog('page:', jsonPage);
            if (jsonPage.feed && jsonPage.feed.entry) {
                //Adjust Data. If it's only 1 entry it's an object instead of array
                if (!$.isArray(jsonPage.feed.entry)) {
                    jsonPage.feed.entry = [jsonPage.feed.entry];
                }
                return jsonPage.feed.entry;
            }
            return [];
        })
        .each(function (feedEntry, index) {
            jiraLog('Item #' + index + ':', feedEntry);
            //Only for jira!
            if (feedEntry['atlassian:application'] && feedEntry['atlassian:application']['#text'].toLowerCase().indexOf('jira') > -1) {
            	var notif = jira.notifications.createNotification(feedEntry);

				if(jira.firstTime)
					jira.firstSyncedNotifications++;

				if(notif)
					return notif.save();
            }
        })
        .then(function (entries) {
            //Determine if paging is required
            if (entries && entries.length > 0) {
                var lastObj = entries[entries.length - 1];
                var lastObjDate = new Date(lastObj.updated['#text']);

                if (entries.length == maxResults && jira.settings.lastSync < lastObjDate && currentPage < 2) {
                    return self.syncStream('/activity?streams=update-date+BEFORE+' + (lastObjDate.getTime() - 2000), maxResults, currentPage + 1);
                }
            }
        });

	};

	this.initData = function () {
        //First Get Own User Data
	    jiraLog('Get Own Data');
	    if (jira.firstTime) {
	        return jiraGet('/rest/api/2/myself')
            .then(function (ownUserData) {
                jira.data.ownUser = JSON.parse(ownUserData);
            })
            .then(function () {
                //Second get all projects
                jiraLog('Get Projects');
                return jiraGet('/rest/api/2/project')
                .then(function (projectData) {
                    var projects = JSON.parse(projectData);
                    jira.data.projects = [];
                    return projects;
                })
                .each(function (project) {
                    //Get detailed information for each project
                    return jiraGet('/rest/api/2/project/' + project.id)
                    .then(function (singleProject) {
                        var proj = JSON.parse(singleProject);
                        jira.data.projects.push(proj);
                    });
                });
            })
            .then(function () {
                //Third get all issue types
                jiraLog('Get Issuetypes');
                return jiraGet('/rest/api/2/issuetype')
                .then(function (issueTypes) {
                    jira.data.issueTypes = JSON.parse(issueTypes);
                    jira.settings.updateData();
                    jira.firstTime = false;
                });
            });
	    } else {
	        return Promise.resolve();
	    }
	};

	this.handleError = function (data, statusCode, result, errorText, cbkParam) {
	    self.handleErrorSoft(data, statusCode, result, errorText, cbkParam);
	    jira.SyncInProcess = false;
	};

	this.handleErrorSoft = function (data, statusCode, result, errorText, cbkParam) {
	    console.log(statusCode + ' || ' + errorText + ' || ' + result + ' || ' + data);

	    //Detect if oAuth token has become invalid
	    if (statusCode == 401 && result == 'oauth_problem=token_rejected') {
	        yasoon.app.invalidateOAuthToken(jira.settings.currentService);
	    }

	    if (cbkParam && cbkParam.fail) {
	        cbkParam.fail();
	    }
	};

}); //jshint ignore:line
//@ sourceURL=http://Jira/jira.js<|endoftext|>


<commit_before>/* @flow */
/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

import { getLocation } from '../language';
import type { Node } from '../language/ast';
import type { Source } from '../language/source';

export class GraphQLError extends Error {
  message: string;
  stack: string;
  nodes: ?Array<Node>;
  source: Source;
  positions: Array<number>;
  locations: any;
  path: Array<string | number>;
  originalError: ?Error;

  constructor(
    message: string,
    // A flow bug keeps us from declaring nodes as an array of Node
    nodes?: Array<any/* Node */>,
    stack?: ?string,
    source?: Source,
    positions?: Array<number>
  ) {
    super(message);
    this.message = message;

    Object.defineProperty(this, 'stack', { value: stack || message });
    Object.defineProperty(this, 'nodes', { value: nodes });

    // Note: flow does not yet know about Object.defineProperty with `get`.
    Object.defineProperty(this, 'source', ({
      get() {
        if (source) {
          return source;
        }
        if (nodes && nodes.length > 0) {
          const node = nodes[0];
          return node && node.loc && node.loc.source;
        }
      }
    }: any));

    Object.defineProperty(this, 'positions', ({
      get() {
        if (positions) {
          return positions;
        }
        if (nodes) {
          const nodePositions = nodes.map(node => node.loc && node.loc.start);
          if (nodePositions.some(p => p)) {
            return nodePositions;
          }
        }
      }
    }: any));

    Object.defineProperty(this, 'locations', ({
      get() {
        if (this.positions && this.source) {
          return this.positions.map(pos => getLocation(this.source, pos));
        }
      }
    }: any));
  }
}
<commit_msg>Make GraphQLError#stack a writable property. (#442)<commit_after>/* @flow */
/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

import { getLocation } from '../language';
import type { Node } from '../language/ast';
import type { Source } from '../language/source';

export class GraphQLError extends Error {
  message: string;
  stack: string;
  nodes: ?Array<Node>;
  source: Source;
  positions: Array<number>;
  locations: any;
  path: Array<string | number>;
  originalError: ?Error;

  constructor(
    message: string,
    // A flow bug keeps us from declaring nodes as an array of Node
    nodes?: Array<any/* Node */>,
    stack?: ?string,
    source?: Source,
    positions?: Array<number>
  ) {
    super(message);
    this.message = message;

    Object.defineProperty(this, 'stack', {
      value: stack || message,
      // Note: stack should not really be writable, but some libraries (such
      // as bluebird) use Error brand-checking which specifically looks to see
      // if stack is a writable property.
      writable: true,
    });
    Object.defineProperty(this, 'nodes', { value: nodes });

    // Note: flow does not yet know about Object.defineProperty with `get`.
    Object.defineProperty(this, 'source', ({
      get() {
        if (source) {
          return source;
        }
        if (nodes && nodes.length > 0) {
          const node = nodes[0];
          return node && node.loc && node.loc.source;
        }
      }
    }: any));

    Object.defineProperty(this, 'positions', ({
      get() {
        if (positions) {
          return positions;
        }
        if (nodes) {
          const nodePositions = nodes.map(node => node.loc && node.loc.start);
          if (nodePositions.some(p => p)) {
            return nodePositions;
          }
        }
      }
    }: any));

    Object.defineProperty(this, 'locations', ({
      get() {
        if (this.positions && this.source) {
          return this.positions.map(pos => getLocation(this.source, pos));
        }
      }
    }: any));
  }
}
<|endoftext|>


<commit_before>const fs = require('fs');

let settings = {};

function main()
{
    let args = getArguments();
    if (args.length < 1)
    {
        printHelp();
    }
    else
    {
        if (args[0] == '-h' || args[0] == '-help' || args[0] == '--help')
        {
            printHelp();
        }
        else if (args[0] == '-V' || args[0] == '-version' || args[0] == '--version')
        {
            printVersion();
        }
        else if (args[0] == '-g')
        {
            generateSettings();
        }
        else
        {
            let file = readFile(args[0]);
            if (file != null)
            {
                parseSettings(file);
                parsePages();

                // create page output folder if it doesn't exist.
                if (!folderExists(settings.output_path + '/' + settings.output_pages_path))
                {
                    createFolder(settings.output_path + '/' + settings.output_pages_path);
                }

                for (let index in settings.pages)
                {
                    let page = settings.pages[index];
                    let template = getTemplate(page.template);
                    let html = generateHTML(template, page);

                    // insert each page into the 'default' template.
                    let defaultTemplate = getTemplate(settings.default);
                    let defaultPage = page;
                    defaultPage.content = html;
                    let finalHtml = generateHTML(defaultTemplate, defaultPage);

                    writeFile(settings.output_path + '/' + settings.output_pages_path + '/' + page.name + '.html', finalHtml);
                }

                let index = getIndex();
                let indexHtml = generateHTML(index);
                writeFile(settings.output_path + '/index.html', indexHtml);
            }
        }
    }
}

//
// Get the command line arguments.
//
function getArguments()
{
    return process.argv.slice(2);
}

//
// Read in a utf8-encoded file.
//
function readFile(path)
{
    let data = '';
    try
    {
        data = fs.readFileSync(path, 'utf8');
        return data;
    }
    catch (err)
    {
        if (err.errno == -2)
        {
            //@@TODO: throw a proper error.
            throw "File not found: " + path;
        }
        else
        {
            console.log(err);
        }
    }
}

//
// Write out a utf8-encoded file.
//
function writeFile(path, data)
{
    fs.writeFileSync(path, data);
}

//
// Get the names of files in a folder
//
function getFilenames(path)
{
    try {
        return fs.readdirSync(path);
    }
    catch (err) {
        if (err.errno == -2)
        {
            //@@TODO: throw a proper error.
            throw "Path not found: " + path;
        }
        else
        {
            console.log(err);
        }
    }
}

//
// Check folder exists.
//
function folderExists(path)
{
    return fs.existsSync(path);
}

//
// Create folder.
//
function createFolder(path)
{
    fs.mkdirSync(path);
}

//
// Generate a settings JSON file.
//
function generateSettings()
{
    let json = {};
    json.input_path = "";
    json.output_path = "";
    json.input_pages_path = "";
    json.output_pages_path = "";
    json.templates = [{name: "default", path:""}];
    json.title = "";
    json.default = "default";

    let str = JSON.stringify(json, null, 4);
    writeFile("./settings.json", str);
}

//
// Parse the settings JSON file.
//
function parseSettings(data)
{
    let json = JSON.parse(data);
    const properties = {
        // paths
        input_path: ".",
        output_path: ".",
        input_pages_path: "./pages",
        output_pages_path: "./pages",

        templates: [
            // name:
            // path:
        ],

        // site settings
        base_url: "/",
        title: null,
        default: "default",

        // optimization options
        client_markdown: true,

    };

    for (property in properties)
    {
        if (json[property])
        {
            settings[property] = json[property];
        }
        else
        {
            settings[property] = properties[property];
        }
    }
}

//
// Parse the pages.
// Return page objects, which have metadata properties and a content property.
//
function parsePages()
{
    let filenames = getFilenames(settings.input_path + '/' + settings.input_pages_path);
    let pages = [];
    let pageObjs = [];

    for (file in filenames)
    {
        pages.push(readFile(settings.input_path + '/' + settings.input_pages_path + '/' + filenames[file]));
    }

    for (index in pages)
    {
        let page = pages[index];
        let pageObj = {};
        pageObj.name = filenames[index];

        //@@TODO: transform page contents

        // extract the metadata from the front of the file
        let pos = 0;
        let cur = page.charAt(pos);
        let metadata;

        function advance()
        {
            pos++;
            cur = page.charAt(pos);
        }

        while (cur != null && cur != '')
        {
            if (cur == '-')
            {
                if (page.charAt(pos + 1) == '-' &&
                    page.charAt(pos + 2) == '-')
                {
                    pos++;
                    cur = page.charAt(pos);
                    metadata = '';

                    while (cur != '-' ||
                        page.charAt(pos + 1) != '-' ||
                        page.charAt(pos + 2) != '-')
                    {
                        metadata += cur;
                        advance();
                    }
                    // move past the '---'
                    advance();
                    advance();
                    advance();
                    advance();
                    // chop off the metadata
                    page = page.slice(pos);
                    page = page.trimLeft();

                    // trim the front:
                    metadata = metadata.slice(2);
                    if (metadata.charAt(0) == '\n' || metadata.charAt(0) == '\r')
                    {
                        metadata = metadata.slice(1);
                    }

                    break;
                }
            }
            else if (cur != '\n' && cur != ' ' && cur != '\t' && cur != '\r')
            {
                //@@TODO: Emit a better error (and stop program?)
                console.log("no valid YAML, page " + index);
                break;
            }

            advance();
        }

        let dataObj = {};
        if (metadata)
        {
            dataObj = parseMetadata(metadata);
        }

        // defaults
        const properties = {
            name: pageObj.name,
            title: null,
            template: null,
            // ...
        };

        // copy the dataObj properties into the pageObj.
        for (property in properties)
        {
            if (dataObj[property])
            {
                pageObj[property] = dataObj[property];
            }
            else
            {
                pageObj[property] = properties[property];
            }
        }

        pageObj.content = page;
        pageObj.path = '/' + settings.output_pages_path + '/' + pageObj.name + '.html';

        pageObjs.push(pageObj);
    }
    settings.pages = pageObjs;
}

//
// Parse YAML metadata.
//
function parseMetadata(metadata)
{
    let lines = metadata.split('\n');
    let dataObj = {};
    for (i in lines)
    {
        let line = lines[i];
        line = line.trim();
        if (line == '') continue; // skip blank lines
        let separator = line.indexOf(':');
        let k = line.slice(0, separator);
        let v = line.substr(separator + 1);
        k = k.trim();
        v = v.trim();
        dataObj[k] = v;
    }
    return dataObj;
}

//
// Get template information by name.
//
function getTemplate(name)
{
    let template = settings.templates.find(function (e) {
        return e.name == name;
    });

    if (template != null)
    {
        template.html = readFile(settings.input_path + '/' + template.path);
    }
    else
    {
        //@@TODO: throw error and exit if template not found.
        throw 'No template called "' + name + '" found.';
    }

    return template;
}

//
// Get index template.
//
function getIndex()
{
    return {
        name: "index",
        path: settings.input_path + "/index.html",
        html: readFile(settings.input_path + '/index.html')
    };
}

//
// Get page information by name.
//
function getPage(name)
{
    let page = settings.pages.find(function (e) {
        return e.name == name;
    });

    if (page != null)
    {
        return page;
    }
    else
    {
        //@@TODO: throw error and exit if page not found.
        throw 'No page called ' + name + ' found.';
    }
}

//
// Generate HTML.
// A basic templating engine.
//
function generateHTML(template, page)
{
    site = settings; // alias global settings.

    let html = template.html;
    let output = '';

    //@@TODO: throw proper error.
    if (html.length == 0) throw "Template " + template.name + " has no content.";

    // include a page
    function include(name)
    {
        return getPage(name).content;
    }

    output = eval('`' + html + '`');

    return output;
}

function printHelp()
{
    console.log("Usage: node ossg.js [-g | -V | -h] [file]");
    console.log("Oscar's static site generator. Compile templates & pages into a website.");
}

function printVersion()
{
    console.log("OSSG: v0.0.1");
}

main();<commit_msg>Allow custom filenames for -g switch<commit_after>const fs = require('fs');

let settings = {};

function main()
{
    let args = getArguments();
    if (args.length < 1)
    {
        printHelp();
    }
    else
    {
        if (args[0] == '-h' || args[0] == '-help' || args[0] == '--help')
        {
            printHelp();
        }
        else if (args[0] == '-V' || args[0] == '-version' || args[0] == '--version')
        {
            printVersion();
        }
        else if (args[0] == '-g')
        {
            generateSettings(args[1]);
        }
        else
        {
            let file = readFile(args[0]);
            if (file != null)
            {
                parseSettings(file);
                parsePages();

                // create page output folder if it doesn't exist.
                if (!folderExists(settings.output_path + '/' + settings.output_pages_path))
                {
                    createFolder(settings.output_path + '/' + settings.output_pages_path);
                }

                for (let index in settings.pages)
                {
                    let page = settings.pages[index];
                    let template = getTemplate(page.template);
                    let html = generateHTML(template, page);

                    // insert each page into the 'default' template.
                    let defaultTemplate = getTemplate(settings.default);
                    let defaultPage = page;
                    defaultPage.content = html;
                    let finalHtml = generateHTML(defaultTemplate, defaultPage);

                    writeFile(settings.output_path + '/' + settings.output_pages_path + '/' + page.name + '.html', finalHtml);
                }

                let index = getIndex();
                let indexHtml = generateHTML(index);
                writeFile(settings.output_path + '/index.html', indexHtml);
            }
        }
    }
}

//
// Get the command line arguments.
//
function getArguments()
{
    return process.argv.slice(2);
}

//
// Read in a utf8-encoded file.
//
function readFile(path)
{
    let data = '';
    try
    {
        data = fs.readFileSync(path, 'utf8');
        return data;
    }
    catch (err)
    {
        if (err.errno == -2)
        {
            //@@TODO: throw a proper error.
            throw "File not found: " + path;
        }
        else
        {
            console.log(err);
        }
    }
}

//
// Write out a utf8-encoded file.
//
function writeFile(path, data)
{
    fs.writeFileSync(path, data);
}

//
// Get the names of files in a folder
//
function getFilenames(path)
{
    try {
        return fs.readdirSync(path);
    }
    catch (err) {
        if (err.errno == -2)
        {
            //@@TODO: throw a proper error.
            throw "Path not found: " + path;
        }
        else
        {
            console.log(err);
        }
    }
}

//
// Check folder exists.
//
function folderExists(path)
{
    return fs.existsSync(path);
}

//
// Create folder.
//
function createFolder(path)
{
    fs.mkdirSync(path);
}

//
// Generate a settings JSON file.
//
function generateSettings(name)
{
    let json = {};
    json.input_path = "";
    json.output_path = "";
    json.input_pages_path = "";
    json.output_pages_path = "";
    json.templates = [{name: "default", path:""}];
    json.title = "";
    json.default = "default";

    let str = JSON.stringify(json, null, 4);
    if (name == null)
    {
        writeFile("./settings.json", str);
    }
    else
    {
        writeFile("./" + name, str);
    }
}

//
// Parse the settings JSON file.
//
function parseSettings(data)
{
    let json = JSON.parse(data);
    const properties = {
        // paths
        input_path: ".",
        output_path: ".",
        input_pages_path: "./pages",
        output_pages_path: "./pages",

        templates: [
            // name:
            // path:
        ],

        // site settings
        base_url: "/",
        title: null,
        default: "default",

        // optimization options
        client_markdown: true,

    };

    for (property in properties)
    {
        if (json[property])
        {
            settings[property] = json[property];
        }
        else
        {
            settings[property] = properties[property];
        }
    }
}

//
// Parse the pages.
// Return page objects, which have metadata properties and a content property.
//
function parsePages()
{
    let filenames = getFilenames(settings.input_path + '/' + settings.input_pages_path);
    let pages = [];
    let pageObjs = [];

    for (file in filenames)
    {
        pages.push(readFile(settings.input_path + '/' + settings.input_pages_path + '/' + filenames[file]));
    }

    for (index in pages)
    {
        let page = pages[index];
        let pageObj = {};
        pageObj.name = filenames[index];

        //@@TODO: transform page contents

        // extract the metadata from the front of the file
        let pos = 0;
        let cur = page.charAt(pos);
        let metadata;

        function advance()
        {
            pos++;
            cur = page.charAt(pos);
        }

        while (cur != null && cur != '')
        {
            if (cur == '-')
            {
                if (page.charAt(pos + 1) == '-' &&
                    page.charAt(pos + 2) == '-')
                {
                    pos++;
                    cur = page.charAt(pos);
                    metadata = '';

                    while (cur != '-' ||
                        page.charAt(pos + 1) != '-' ||
                        page.charAt(pos + 2) != '-')
                    {
                        metadata += cur;
                        advance();
                    }
                    // move past the '---'
                    advance();
                    advance();
                    advance();
                    advance();
                    // chop off the metadata
                    page = page.slice(pos);
                    page = page.trimLeft();

                    // trim the front:
                    metadata = metadata.slice(2);
                    if (metadata.charAt(0) == '\n' || metadata.charAt(0) == '\r')
                    {
                        metadata = metadata.slice(1);
                    }

                    break;
                }
            }
            else if (cur != '\n' && cur != ' ' && cur != '\t' && cur != '\r')
            {
                //@@TODO: Emit a better error (and stop program?)
                console.log("no valid YAML, page " + index);
                break;
            }

            advance();
        }

        let dataObj = {};
        if (metadata)
        {
            dataObj = parseMetadata(metadata);
        }

        // defaults
        const properties = {
            name: pageObj.name,
            title: null,
            template: null,
            // ...
        };

        // copy the dataObj properties into the pageObj.
        for (property in dataObj)
        {
            if (dataObj[property])
            {
                pageObj[property] = dataObj[property];
            }
            else
            {
                pageObj[property] = properties[property];
            }
        }

        pageObj.content = page;
        pageObj.path = '/' + settings.output_pages_path + '/' + pageObj.name + '.html';

        pageObjs.push(pageObj);
    }
    settings.pages = pageObjs;
}

//
// Parse YAML metadata.
//
function parseMetadata(metadata)
{
    let lines = metadata.split('\n');
    let dataObj = {};
    for (i in lines)
    {
        let line = lines[i];
        line = line.trim();
        if (line == '') continue; // skip blank lines
        let separator = line.indexOf(':');
        let k = line.slice(0, separator);
        let v = line.substr(separator + 1);
        k = k.trim();
        v = v.trim();
        dataObj[k] = v;
    }
    return dataObj;
}

//
// Get template information by name.
//
function getTemplate(name)
{
    let template = settings.templates.find(function (e) {
        return e.name == name;
    });

    if (template != null)
    {
        template.html = readFile(settings.input_path + '/' + template.path);
    }
    else
    {
        //@@TODO: throw error and exit if template not found.
        throw 'No template called "' + name + '" found.';
    }

    return template;
}

//
// Get index template.
//
function getIndex()
{
    return {
        name: "index",
        path: settings.input_path + "/index.html",
        html: readFile(settings.input_path + '/index.html')
    };
}

//
// Get page information by name.
//
function getPage(name)
{
    let page = settings.pages.find(function (e) {
        return e.name == name;
    });

    if (page != null)
    {
        return page;
    }
    else
    {
        //@@TODO: throw error and exit if page not found.
        throw 'No page called ' + name + ' found.';
    }
}

//
// Generate HTML.
// A basic templating engine.
//
function generateHTML(template, page)
{
    site = settings; // alias global settings.

    let html = template.html;
    let output = '';

    //@@TODO: throw proper error.
    if (html.length == 0) throw "Template " + template.name + " has no content.";

    // include a page
    function include(name)
    {
        return getPage(name).content;
    }

    output = eval('`' + html + '`');

    return output;
}

function printHelp()
{
    console.log("Usage: node ossg.js [-g | -V | -h] [file]");
    console.log("Oscar's static site generator. Compile templates & pages into a website.");
}

function printVersion()
{
    console.log("OSSG: v0.0.1");
}

main();<|endoftext|>