/* Minification failed. Returning unminified contents.
(21719,8-9): run-time error JS1010: Expected identifier: .
(21719,8-9): run-time error JS1195: Expected expression: .
(21733,8-9): run-time error JS1010: Expected identifier: .
(21733,8-9): run-time error JS1195: Expected expression: .
(21772,8-9): run-time error JS1010: Expected identifier: .
(21772,8-9): run-time error JS1195: Expected expression: .
(21842,8-9): run-time error JS1010: Expected identifier: .
(21842,8-9): run-time error JS1195: Expected expression: .
(21880,8-9): run-time error JS1010: Expected identifier: .
(21880,8-9): run-time error JS1195: Expected expression: .
(22170,8-9): run-time error JS1010: Expected identifier: .
(22170,8-9): run-time error JS1195: Expected expression: .
(22221,8-9): run-time error JS1010: Expected identifier: .
(22221,8-9): run-time error JS1195: Expected expression: .
(22890,8-9): run-time error JS1010: Expected identifier: .
(22890,8-9): run-time error JS1195: Expected expression: .
(22900,8-9): run-time error JS1010: Expected identifier: .
(22900,8-9): run-time error JS1195: Expected expression: .
(22966,11-12): run-time error JS1010: Expected identifier: .
(22966,11-12): run-time error JS1195: Expected expression: .
(23818,9-10): run-time error JS1010: Expected identifier: .
(23818,9-10): run-time error JS1195: Expected expression: .
(23832,8-9): run-time error JS1010: Expected identifier: .
(23832,8-9): run-time error JS1195: Expected expression: .
(24652,8-9): run-time error JS1010: Expected identifier: .
(24652,8-9): run-time error JS1195: Expected expression: .
(24842,10-11): run-time error JS1010: Expected identifier: =
(24842,10-11): run-time error JS1195: Expected expression: =
(24844,10-11): run-time error JS1010: Expected identifier: =
(24844,10-11): run-time error JS1195: Expected expression: =
(24847,8-9): run-time error JS1010: Expected identifier: .
(24847,8-9): run-time error JS1195: Expected expression: .
(24964,8-9): run-time error JS1010: Expected identifier: .
(24964,8-9): run-time error JS1195: Expected expression: .
(25103,8-9): run-time error JS1010: Expected identifier: .
(25103,8-9): run-time error JS1195: Expected expression: .
(25302,8-9): run-time error JS1010: Expected identifier: .
(25302,8-9): run-time error JS1195: Expected expression: .
(25336,8-9): run-time error JS1010: Expected identifier: .
(25336,8-9): run-time error JS1195: Expected expression: .
(25391,8-9): run-time error JS1010: Expected identifier: .
(25391,8-9): run-time error JS1195: Expected expression: .
(25469,11-12): run-time error JS1010: Expected identifier: .
(25469,11-12): run-time error JS1195: Expected expression: .
(26658,9-10): run-time error JS1010: Expected identifier: .
(26658,9-10): run-time error JS1195: Expected expression: .
(26671,8-9): run-time error JS1010: Expected identifier: .
(26671,8-9): run-time error JS1195: Expected expression: .
(26679,8-9): run-time error JS1010: Expected identifier: .
(26679,8-9): run-time error JS1195: Expected expression: .
(27498,8-9): run-time error JS1010: Expected identifier: .
(27498,8-9): run-time error JS1195: Expected expression: .
(27538,8-9): run-time error JS1010: Expected identifier: .
(27538,8-9): run-time error JS1195: Expected expression: .
(27578,8-9): run-time error JS1010: Expected identifier: .
(27578,8-9): run-time error JS1195: Expected expression: .
(27710,8-9): run-time error JS1010: Expected identifier: .
(27710,8-9): run-time error JS1195: Expected expression: .
(28005,8-9): run-time error JS1010: Expected identifier: .
(28005,8-9): run-time error JS1195: Expected expression: .
(28665,8-9): run-time error JS1010: Expected identifier: .
(28665,8-9): run-time error JS1195: Expected expression: .
(29841,11-12): run-time error JS1010: Expected identifier: .
(29841,11-12): run-time error JS1195: Expected expression: .
(30565,8-9): run-time error JS1010: Expected identifier: .
(30565,8-9): run-time error JS1195: Expected expression: .
(30674,10-11): run-time error JS1010: Expected identifier: =
(30674,10-11): run-time error JS1195: Expected expression: =
(30676,10-11): run-time error JS1010: Expected identifier: =
(30676,10-11): run-time error JS1195: Expected expression: =
(30758,8-9): run-time error JS1010: Expected identifier: .
(30758,8-9): run-time error JS1195: Expected expression: .
(30953,10-11): run-time error JS1010: Expected identifier: =
(30953,10-11): run-time error JS1195: Expected expression: =
(30955,10-11): run-time error JS1010: Expected identifier: =
(30955,10-11): run-time error JS1195: Expected expression: =
(30958,8-9): run-time error JS1010: Expected identifier: .
(30958,8-9): run-time error JS1195: Expected expression: .
(31196,8-9): run-time error JS1010: Expected identifier: .
(31196,8-9): run-time error JS1195: Expected expression: .
(31247,8-9): run-time error JS1010: Expected identifier: .
(31247,8-9): run-time error JS1195: Expected expression: .
(31512,8-9): run-time error JS1010: Expected identifier: .
(31512,8-9): run-time error JS1195: Expected expression: .
(31575,8-9): run-time error JS1010: Expected identifier: .
(31575,8-9): run-time error JS1195: Expected expression: .
(31644,9-10): run-time error JS1010: Expected identifier: .
(31644,9-10): run-time error JS1195: Expected expression: .
(31656,8-9): run-time error JS1010: Expected identifier: .
(31656,8-9): run-time error JS1195: Expected expression: .
(31677,8-9): run-time error JS1010: Expected identifier: .
(31677,8-9): run-time error JS1195: Expected expression: .
(31689,8-9): run-time error JS1010: Expected identifier: .
(31689,8-9): run-time error JS1195: Expected expression: .
(31702,8-9): run-time error JS1010: Expected identifier: .
(31702,8-9): run-time error JS1195: Expected expression: .
(31734,8-9): run-time error JS1010: Expected identifier: .
(31734,8-9): run-time error JS1195: Expected expression: .
(31756,8-9): run-time error JS1010: Expected identifier: .
(31756,8-9): run-time error JS1195: Expected expression: .
(31769,8-9): run-time error JS1010: Expected identifier: .
(31769,8-9): run-time error JS1195: Expected expression: .
(31785,8-9): run-time error JS1010: Expected identifier: .
(31785,8-9): run-time error JS1195: Expected expression: .
(31820,10-11): run-time error JS1010: Expected identifier: =
(31820,10-11): run-time error JS1195: Expected expression: =
(31822,10-11): run-time error JS1010: Expected identifier: =
(31822,10-11): run-time error JS1195: Expected expression: =
(31825,8-9): run-time error JS1010: Expected identifier: .
(31825,8-9): run-time error JS1195: Expected expression: .
(31898,8-9): run-time error JS1010: Expected identifier: .
(31898,8-9): run-time error JS1195: Expected expression: .
(31912,8-9): run-time error JS1010: Expected identifier: .
(31912,8-9): run-time error JS1195: Expected expression: .
(31937,10-11): run-time error JS1010: Expected identifier: =
(31937,10-11): run-time error JS1195: Expected expression: =
(31939,10-11): run-time error JS1010: Expected identifier: =
(31939,10-11): run-time error JS1195: Expected expression: =
(31942,8-9): run-time error JS1010: Expected identifier: .
(31942,8-9): run-time error JS1195: Expected expression: .
(32195,10-11): run-time error JS1010: Expected identifier: =
(32195,10-11): run-time error JS1195: Expected expression: =
(32197,10-11): run-time error JS1010: Expected identifier: =
(32197,10-11): run-time error JS1195: Expected expression: =
(32200,8-9): run-time error JS1010: Expected identifier: .
(32200,8-9): run-time error JS1195: Expected expression: .
(32249,8-9): run-time error JS1010: Expected identifier: .
(32249,8-9): run-time error JS1195: Expected expression: .
(32297,8-9): run-time error JS1010: Expected identifier: .
(32297,8-9): run-time error JS1195: Expected expression: .
(33516,10-11): run-time error JS1010: Expected identifier: =
(33516,10-11): run-time error JS1195: Expected expression: =
(33518,10-11): run-time error JS1010: Expected identifier: =
(33518,10-11): run-time error JS1195: Expected expression: =
(33521,8-9): run-time error JS1010: Expected identifier: .
(33521,8-9): run-time error JS1195: Expected expression: .
(33543,8-9): run-time error JS1010: Expected identifier: .
(33543,8-9): run-time error JS1195: Expected expression: .
(33573,8-9): run-time error JS1010: Expected identifier: .
(33573,8-9): run-time error JS1195: Expected expression: .
(33611,8-9): run-time error JS1010: Expected identifier: .
(33611,8-9): run-time error JS1195: Expected expression: .
(33646,8-9): run-time error JS1010: Expected identifier: .
(33646,8-9): run-time error JS1195: Expected expression: .
(33726,10-11): run-time error JS1010: Expected identifier: =
(33726,10-11): run-time error JS1195: Expected expression: =
(33728,10-11): run-time error JS1010: Expected identifier: =
(33728,10-11): run-time error JS1195: Expected expression: =
(33733,8-9): run-time error JS1010: Expected identifier: .
(33733,8-9): run-time error JS1195: Expected expression: .
(33753,10-11): run-time error JS1010: Expected identifier: =
(33753,10-11): run-time error JS1195: Expected expression: =
(33755,10-11): run-time error JS1010: Expected identifier: =
(33755,10-11): run-time error JS1195: Expected expression: =
(33758,8-9): run-time error JS1010: Expected identifier: .
(33758,8-9): run-time error JS1195: Expected expression: .
(35639,16-17): run-time error JS1010: Expected identifier: =
(35639,16-17): run-time error JS1195: Expected expression: =
(35641,16-17): run-time error JS1010: Expected identifier: =
(35641,16-17): run-time error JS1195: Expected expression: =
(35644,11-12): run-time error JS1010: Expected identifier: .
(35644,11-12): run-time error JS1195: Expected expression: .
(37031,10-11): run-time error JS1010: Expected identifier: =
(37031,10-11): run-time error JS1195: Expected expression: =
(37033,10-11): run-time error JS1010: Expected identifier: =
(37033,10-11): run-time error JS1195: Expected expression: =
(37036,8-9): run-time error JS1010: Expected identifier: .
(37036,8-9): run-time error JS1195: Expected expression: .
(37269,10-11): run-time error JS1010: Expected identifier: =
(37269,10-11): run-time error JS1195: Expected expression: =
(37271,10-11): run-time error JS1010: Expected identifier: =
(37271,10-11): run-time error JS1195: Expected expression: =
(37274,8-9): run-time error JS1010: Expected identifier: .
(37274,8-9): run-time error JS1195: Expected expression: .
(37326,10-11): run-time error JS1010: Expected identifier: =
(37326,10-11): run-time error JS1195: Expected expression: =
(37328,10-11): run-time error JS1010: Expected identifier: =
(37328,10-11): run-time error JS1195: Expected expression: =
(37331,8-9): run-time error JS1010: Expected identifier: .
(37331,8-9): run-time error JS1195: Expected expression: .
(37390,8-9): run-time error JS1010: Expected identifier: .
(37390,8-9): run-time error JS1195: Expected expression: .
(37396,8-9): run-time error JS1010: Expected identifier: .
(37396,8-9): run-time error JS1195: Expected expression: .
(37569,8-9): run-time error JS1010: Expected identifier: .
(37569,8-9): run-time error JS1195: Expected expression: .
(38086,8-9): run-time error JS1010: Expected identifier: .
(38086,8-9): run-time error JS1195: Expected expression: .
(38147,8-9): run-time error JS1010: Expected identifier: .
(38147,8-9): run-time error JS1195: Expected expression: .
(38240,8-9): run-time error JS1010: Expected identifier: .
(38240,8-9): run-time error JS1195: Expected expression: .
(38407,8-9): run-time error JS1010: Expected identifier: .
(38407,8-9): run-time error JS1195: Expected expression: .
(38494,8-9): run-time error JS1010: Expected identifier: .
(38494,8-9): run-time error JS1195: Expected expression: .
(38550,8-9): run-time error JS1010: Expected identifier: .
(38550,8-9): run-time error JS1195: Expected expression: .
(38665,8-9): run-time error JS1010: Expected identifier: .
(38665,8-9): run-time error JS1195: Expected expression: .
(38706,8-9): run-time error JS1010: Expected identifier: .
(38706,8-9): run-time error JS1195: Expected expression: .
(38738,8-9): run-time error JS1010: Expected identifier: .
(38738,8-9): run-time error JS1195: Expected expression: .
(38791,8-9): run-time error JS1010: Expected identifier: .
(38791,8-9): run-time error JS1195: Expected expression: .
(38853,8-9): run-time error JS1010: Expected identifier: .
(38853,8-9): run-time error JS1195: Expected expression: .
(38971,8-9): run-time error JS1010: Expected identifier: .
(38971,8-9): run-time error JS1195: Expected expression: .
(39594,8-9): run-time error JS1010: Expected identifier: .
(39594,8-9): run-time error JS1195: Expected expression: .
(39647,8-9): run-time error JS1010: Expected identifier: .
(39647,8-9): run-time error JS1195: Expected expression: .
(39720,8-9): run-time error JS1010: Expected identifier: .
(39720,8-9): run-time error JS1195: Expected expression: .
(39776,8-9): run-time error JS1010: Expected identifier: .
(39776,8-9): run-time error JS1195: Expected expression: .
(39824,8-9): run-time error JS1010: Expected identifier: .
(39824,8-9): run-time error JS1195: Expected expression: .
(39954,8-9): run-time error JS1010: Expected identifier: .
(39954,8-9): run-time error JS1195: Expected expression: .
(40080,8-9): run-time error JS1010: Expected identifier: .
(40080,8-9): run-time error JS1195: Expected expression: .
(40215,8-9): run-time error JS1010: Expected identifier: .
(40215,8-9): run-time error JS1195: Expected expression: .
(40278,8-9): run-time error JS1010: Expected identifier: .
(40278,8-9): run-time error JS1195: Expected expression: .
(40435,8-9): run-time error JS1010: Expected identifier: .
(40435,8-9): run-time error JS1195: Expected expression: .
(40465,8-9): run-time error JS1010: Expected identifier: .
(40465,8-9): run-time error JS1195: Expected expression: .
(40593,8-9): run-time error JS1010: Expected identifier: .
(40593,8-9): run-time error JS1195: Expected expression: .
(40623,8-9): run-time error JS1010: Expected identifier: .
(40623,8-9): run-time error JS1195: Expected expression: .
(40645,8-9): run-time error JS1010: Expected identifier: .
(40645,8-9): run-time error JS1195: Expected expression: .
(40681,8-9): run-time error JS1010: Expected identifier: .
(40681,8-9): run-time error JS1195: Expected expression: .
(40726,8-9): run-time error JS1010: Expected identifier: .
(40726,8-9): run-time error JS1195: Expected expression: .
(41005,8-9): run-time error JS1010: Expected identifier: .
(41005,8-9): run-time error JS1195: Expected expression: .
(41245,8-9): run-time error JS1010: Expected identifier: .
(41245,8-9): run-time error JS1195: Expected expression: .
(41429,8-9): run-time error JS1010: Expected identifier: .
(41429,8-9): run-time error JS1195: Expected expression: .
(41436,8-9): run-time error JS1010: Expected identifier: .
(41436,8-9): run-time error JS1195: Expected expression: .
(41443,8-9): run-time error JS1010: Expected identifier: .
(41443,8-9): run-time error JS1195: Expected expression: .
(41512,8-9): run-time error JS1010: Expected identifier: .
(41512,8-9): run-time error JS1195: Expected expression: .
(41607,10-11): run-time error JS1010: Expected identifier: =
(41607,10-11): run-time error JS1195: Expected expression: =
(41609,10-11): run-time error JS1010: Expected identifier: =
(41609,10-11): run-time error JS1195: Expected expression: =
(41615,8-9): run-time error JS1010: Expected identifier: .
(41615,8-9): run-time error JS1195: Expected expression: .
(41911,8-9): run-time error JS1010: Expected identifier: .
(41911,8-9): run-time error JS1195: Expected expression: .
(41921,8-9): run-time error JS1010: Expected identifier: .
(41921,8-9): run-time error JS1195: Expected expression: .
(41986,8-9): run-time error JS1010: Expected identifier: .
(41986,8-9): run-time error JS1195: Expected expression: .
(42129,8-9): run-time error JS1010: Expected identifier: .
(42129,8-9): run-time error JS1195: Expected expression: .
(42251,8-9): run-time error JS1010: Expected identifier: .
(42251,8-9): run-time error JS1195: Expected expression: .
(42350,8-9): run-time error JS1010: Expected identifier: .
(42350,8-9): run-time error JS1195: Expected expression: .
(42416,8-9): run-time error JS1010: Expected identifier: .
(42416,8-9): run-time error JS1195: Expected expression: .
(43034,8-9): run-time error JS1010: Expected identifier: .
(43034,8-9): run-time error JS1195: Expected expression: .
(43057,8-9): run-time error JS1010: Expected identifier: .
(43057,8-9): run-time error JS1195: Expected expression: .
(43744,8-9): run-time error JS1010: Expected identifier: .
(43744,8-9): run-time error JS1195: Expected expression: .
(44373,8-9): run-time error JS1010: Expected identifier: .
(44373,8-9): run-time error JS1195: Expected expression: .
(44434,8-9): run-time error JS1010: Expected identifier: .
(44434,8-9): run-time error JS1195: Expected expression: .
(44540,8-9): run-time error JS1010: Expected identifier: .
(44540,8-9): run-time error JS1195: Expected expression: .
(44657,8-9): run-time error JS1010: Expected identifier: .
(44657,8-9): run-time error JS1195: Expected expression: .
(44690,8-9): run-time error JS1010: Expected identifier: .
(44690,8-9): run-time error JS1195: Expected expression: .
(45039,8-9): run-time error JS1010: Expected identifier: .
(45039,8-9): run-time error JS1195: Expected expression: .
(45101,8-9): run-time error JS1010: Expected identifier: .
(45101,8-9): run-time error JS1195: Expected expression: .
(45225,8-9): run-time error JS1010: Expected identifier: .
(45225,8-9): run-time error JS1195: Expected expression: .
(45531,8-9): run-time error JS1010: Expected identifier: .
(45531,8-9): run-time error JS1195: Expected expression: .
(45842,8-9): run-time error JS1010: Expected identifier: .
(45842,8-9): run-time error JS1195: Expected expression: .
(45869,8-9): run-time error JS1010: Expected identifier: .
(45869,8-9): run-time error JS1195: Expected expression: .
(46038,8-9): run-time error JS1010: Expected identifier: .
(46038,8-9): run-time error JS1195: Expected expression: .
(46197,11-12): run-time error JS1010: Expected identifier: .
(46197,11-12): run-time error JS1195: Expected expression: .
(46230,8-9): run-time error JS1010: Expected identifier: .
(46230,8-9): run-time error JS1195: Expected expression: .
(46502,8-9): run-time error JS1010: Expected identifier: .
(46502,8-9): run-time error JS1195: Expected expression: .
(46696,8-9): run-time error JS1010: Expected identifier: .
(46696,8-9): run-time error JS1195: Expected expression: .
(46832,8-9): run-time error JS1010: Expected identifier: .
(46832,8-9): run-time error JS1195: Expected expression: .
(47021,8-9): run-time error JS1010: Expected identifier: .
(47021,8-9): run-time error JS1195: Expected expression: .
(47035,8-9): run-time error JS1010: Expected identifier: .
(47035,8-9): run-time error JS1195: Expected expression: .
(47069,10-11): run-time error JS1010: Expected identifier: =
(47069,10-11): run-time error JS1195: Expected expression: =
(47071,10-11): run-time error JS1010: Expected identifier: =
(47071,10-11): run-time error JS1195: Expected expression: =
(47074,8-9): run-time error JS1010: Expected identifier: .
(47074,8-9): run-time error JS1195: Expected expression: .
(47167,10-11): run-time error JS1010: Expected identifier: =
(47167,10-11): run-time error JS1195: Expected expression: =
(47169,10-11): run-time error JS1010: Expected identifier: =
(47169,10-11): run-time error JS1195: Expected expression: =
(47172,8-9): run-time error JS1010: Expected identifier: .
(47172,8-9): run-time error JS1195: Expected expression: .
(47373,8-9): run-time error JS1010: Expected identifier: .
(47373,8-9): run-time error JS1195: Expected expression: .
(47447,8-9): run-time error JS1010: Expected identifier: .
(47447,8-9): run-time error JS1195: Expected expression: .
(47473,8-9): run-time error JS1010: Expected identifier: .
(47473,8-9): run-time error JS1195: Expected expression: .
(47556,8-9): run-time error JS1010: Expected identifier: .
(47556,8-9): run-time error JS1195: Expected expression: .
(47608,8-9): run-time error JS1010: Expected identifier: .
(47608,8-9): run-time error JS1195: Expected expression: .
(47786,8-9): run-time error JS1010: Expected identifier: .
(47786,8-9): run-time error JS1195: Expected expression: .
(47926,8-9): run-time error JS1010: Expected identifier: .
(47926,8-9): run-time error JS1195: Expected expression: .
(47971,8-9): run-time error JS1010: Expected identifier: .
(47971,8-9): run-time error JS1195: Expected expression: .
(48005,8-9): run-time error JS1010: Expected identifier: .
(48005,8-9): run-time error JS1195: Expected expression: .
(48036,8-9): run-time error JS1010: Expected identifier: .
(48036,8-9): run-time error JS1195: Expected expression: .
(48077,8-9): run-time error JS1010: Expected identifier: .
(48077,8-9): run-time error JS1195: Expected expression: .
(48090,8-9): run-time error JS1010: Expected identifier: .
(48090,8-9): run-time error JS1195: Expected expression: .
(48103,8-9): run-time error JS1010: Expected identifier: .
(48103,8-9): run-time error JS1195: Expected expression: .
(48309,8-9): run-time error JS1010: Expected identifier: .
(48309,8-9): run-time error JS1195: Expected expression: .
(48310,8-9): run-time error JS1010: Expected identifier: .
(48310,8-9): run-time error JS1195: Expected expression: .
(48311,8-9): run-time error JS1010: Expected identifier: .
(48311,8-9): run-time error JS1195: Expected expression: .
(48321,11-12): run-time error JS1010: Expected identifier: .
(48321,11-12): run-time error JS1195: Expected expression: .
(48348,8-9): run-time error JS1010: Expected identifier: .
(48348,8-9): run-time error JS1195: Expected expression: .
(48417,8-9): run-time error JS1010: Expected identifier: .
(48417,8-9): run-time error JS1195: Expected expression: .
(48441,8-9): run-time error JS1010: Expected identifier: .
(48441,8-9): run-time error JS1195: Expected expression: .
(48462,8-9): run-time error JS1010: Expected identifier: .
(48462,8-9): run-time error JS1195: Expected expression: .
(48487,8-9): run-time error JS1010: Expected identifier: .
(48487,8-9): run-time error JS1195: Expected expression: .
(48505,8-9): run-time error JS1010: Expected identifier: .
(48505,8-9): run-time error JS1195: Expected expression: .
(48615,8-9): run-time error JS1010: Expected identifier: .
(48615,8-9): run-time error JS1195: Expected expression: .
(48616,8-9): run-time error JS1010: Expected identifier: .
(48616,8-9): run-time error JS1195: Expected expression: .
(48729,8-9): run-time error JS1010: Expected identifier: .
(48729,8-9): run-time error JS1195: Expected expression: .
(48814,8-9): run-time error JS1010: Expected identifier: .
(48814,8-9): run-time error JS1195: Expected expression: .
(48880,8-9): run-time error JS1010: Expected identifier: .
(48880,8-9): run-time error JS1195: Expected expression: .
(48952,8-9): run-time error JS1010: Expected identifier: .
(48952,8-9): run-time error JS1195: Expected expression: .
(49036,8-9): run-time error JS1010: Expected identifier: .
(49036,8-9): run-time error JS1195: Expected expression: .
(49069,8-9): run-time error JS1010: Expected identifier: .
(49069,8-9): run-time error JS1195: Expected expression: .
(49114,10-11): run-time error JS1010: Expected identifier: =
(49114,10-11): run-time error JS1195: Expected expression: =
(49116,10-11): run-time error JS1010: Expected identifier: =
(49116,10-11): run-time error JS1195: Expected expression: =
(49119,8-9): run-time error JS1010: Expected identifier: .
(49119,8-9): run-time error JS1195: Expected expression: .
(49157,8-9): run-time error JS1010: Expected identifier: .
(49157,8-9): run-time error JS1195: Expected expression: .
(49205,8-9): run-time error JS1010: Expected identifier: .
(49205,8-9): run-time error JS1195: Expected expression: .
(49254,8-9): run-time error JS1010: Expected identifier: .
(49254,8-9): run-time error JS1195: Expected expression: .
(49374,8-9): run-time error JS1010: Expected identifier: .
(49374,8-9): run-time error JS1195: Expected expression: .
(49502,8-9): run-time error JS1010: Expected identifier: .
(49502,8-9): run-time error JS1195: Expected expression: .
(49565,8-9): run-time error JS1010: Expected identifier: .
(49565,8-9): run-time error JS1195: Expected expression: .
(49611,8-9): run-time error JS1010: Expected identifier: .
(49611,8-9): run-time error JS1195: Expected expression: .
(49797,8-9): run-time error JS1010: Expected identifier: .
(49797,8-9): run-time error JS1195: Expected expression: .
(49883,8-9): run-time error JS1010: Expected identifier: .
(49883,8-9): run-time error JS1195: Expected expression: .
(49933,8-9): run-time error JS1010: Expected identifier: .
(49933,8-9): run-time error JS1195: Expected expression: .
(49960,8-9): run-time error JS1010: Expected identifier: .
(49960,8-9): run-time error JS1195: Expected expression: .
(49993,8-9): run-time error JS1010: Expected identifier: .
(49993,8-9): run-time error JS1195: Expected expression: .
(50024,8-9): run-time error JS1010: Expected identifier: .
(50024,8-9): run-time error JS1195: Expected expression: .
(50233,8-9): run-time error JS1010: Expected identifier: .
(50233,8-9): run-time error JS1195: Expected expression: .
(50342,8-9): run-time error JS1010: Expected identifier: .
(50342,8-9): run-time error JS1195: Expected expression: .
(50389,8-9): run-time error JS1010: Expected identifier: .
(50389,8-9): run-time error JS1195: Expected expression: .
(50417,8-9): run-time error JS1010: Expected identifier: .
(50417,8-9): run-time error JS1195: Expected expression: .
(50545,8-9): run-time error JS1010: Expected identifier: .
(50545,8-9): run-time error JS1195: Expected expression: .
(50676,8-9): run-time error JS1010: Expected identifier: .
(50676,8-9): run-time error JS1195: Expected expression: .
(50740,8-9): run-time error JS1010: Expected identifier: .
(50740,8-9): run-time error JS1195: Expected expression: .
(50786,8-9): run-time error JS1010: Expected identifier: .
(50786,8-9): run-time error JS1195: Expected expression: .
(50829,8-9): run-time error JS1010: Expected identifier: .
(50829,8-9): run-time error JS1195: Expected expression: .
(50868,8-9): run-time error JS1010: Expected identifier: .
(50868,8-9): run-time error JS1195: Expected expression: .
(50894,8-9): run-time error JS1010: Expected identifier: .
(50894,8-9): run-time error JS1195: Expected expression: .
(51798,8-9): run-time error JS1010: Expected identifier: .
(51798,8-9): run-time error JS1195: Expected expression: .
(51853,8-9): run-time error JS1010: Expected identifier: .
(51853,8-9): run-time error JS1195: Expected expression: .
(51890,8-9): run-time error JS1010: Expected identifier: .
(51890,8-9): run-time error JS1195: Expected expression: .
(51918,8-9): run-time error JS1010: Expected identifier: .
(51918,8-9): run-time error JS1195: Expected expression: .
(51950,8-9): run-time error JS1010: Expected identifier: .
(51950,8-9): run-time error JS1195: Expected expression: .
(51967,8-9): run-time error JS1010: Expected identifier: .
(51967,8-9): run-time error JS1195: Expected expression: .
(51984,8-9): run-time error JS1010: Expected identifier: .
(51984,8-9): run-time error JS1195: Expected expression: .
(52004,8-9): run-time error JS1010: Expected identifier: .
(52004,8-9): run-time error JS1195: Expected expression: .
(52031,8-9): run-time error JS1010: Expected identifier: .
(52031,8-9): run-time error JS1195: Expected expression: .
(52128,8-9): run-time error JS1010: Expected identifier: .
(52128,8-9): run-time error JS1195: Expected expression: .
(52180,8-9): run-time error JS1010: Expected identifier: .
(52180,8-9): run-time error JS1195: Expected expression: .
(52192,8-9): run-time error JS1010: Expected identifier: .
(52192,8-9): run-time error JS1195: Expected expression: .
(52254,8-9): run-time error JS1010: Expected identifier: .
(52254,8-9): run-time error JS1195: Expected expression: .
(52283,8-9): run-time error JS1010: Expected identifier: .
(52283,8-9): run-time error JS1195: Expected expression: .
(52309,8-9): run-time error JS1010: Expected identifier: .
(52309,8-9): run-time error JS1195: Expected expression: .
(52320,8-9): run-time error JS1010: Expected identifier: .
(52320,8-9): run-time error JS1195: Expected expression: .
(52347,8-9): run-time error JS1010: Expected identifier: .
(52347,8-9): run-time error JS1195: Expected expression: .
(52371,8-9): run-time error JS1010: Expected identifier: .
(52371,8-9): run-time error JS1195: Expected expression: .
(52383,8-9): run-time error JS1010: Expected identifier: .
(52383,8-9): run-time error JS1195: Expected expression: .
(52414,8-9): run-time error JS1010: Expected identifier: .
(52414,8-9): run-time error JS1195: Expected expression: .
(52443,16-17): run-time error JS1010: Expected identifier: =
(52443,16-17): run-time error JS1195: Expected expression: =
(52445,16-17): run-time error JS1010: Expected identifier: =
(52445,16-17): run-time error JS1195: Expected expression: =
(52449,11-12): run-time error JS1010: Expected identifier: .
(52449,11-12): run-time error JS1195: Expected expression: .
(52457,11-12): run-time error JS1010: Expected identifier: .
(52457,11-12): run-time error JS1195: Expected expression: .
(52464,11-12): run-time error JS1010: Expected identifier: .
(52464,11-12): run-time error JS1195: Expected expression: .
(52773,10-11): run-time error JS1010: Expected identifier: =
(52773,10-11): run-time error JS1195: Expected expression: =
(52775,10-11): run-time error JS1010: Expected identifier: =
(52775,10-11): run-time error JS1195: Expected expression: =
(52782,8-9): run-time error JS1010: Expected identifier: .
(52782,8-9): run-time error JS1195: Expected expression: .
(52791,8-9): run-time error JS1010: Expected identifier: .
(52791,8-9): run-time error JS1195: Expected expression: .
(53156,10-11): run-time error JS1010: Expected identifier: =
(53156,10-11): run-time error JS1195: Expected expression: =
(53158,10-11): run-time error JS1010: Expected identifier: =
(53158,10-11): run-time error JS1195: Expected expression: =
(53161,8-9): run-time error JS1010: Expected identifier: .
(53161,8-9): run-time error JS1195: Expected expression: .
(53170,8-9): run-time error JS1010: Expected identifier: .
(53170,8-9): run-time error JS1195: Expected expression: .
(53275,10-11): run-time error JS1010: Expected identifier: =
(53275,10-11): run-time error JS1195: Expected expression: =
(53277,10-11): run-time error JS1010: Expected identifier: =
(53277,10-11): run-time error JS1195: Expected expression: =
(53280,8-9): run-time error JS1010: Expected identifier: .
(53280,8-9): run-time error JS1195: Expected expression: .
(53288,8-9): run-time error JS1010: Expected identifier: .
(53288,8-9): run-time error JS1195: Expected expression: .
(53395,10-11): run-time error JS1010: Expected identifier: =
(53395,10-11): run-time error JS1195: Expected expression: =
(53397,10-11): run-time error JS1010: Expected identifier: =
(53397,10-11): run-time error JS1195: Expected expression: =
(53400,8-9): run-time error JS1010: Expected identifier: .
(53400,8-9): run-time error JS1195: Expected expression: .
(53408,8-9): run-time error JS1010: Expected identifier: .
(53408,8-9): run-time error JS1195: Expected expression: .
(53551,8-9): run-time error JS1010: Expected identifier: .
(53551,8-9): run-time error JS1195: Expected expression: .
(53583,10-11): run-time error JS1010: Expected identifier: =
(53583,10-11): run-time error JS1195: Expected expression: =
(53585,10-11): run-time error JS1010: Expected identifier: =
(53585,10-11): run-time error JS1195: Expected expression: =
(53589,8-9): run-time error JS1010: Expected identifier: .
(53589,8-9): run-time error JS1195: Expected expression: .
(53682,16-17): run-time error JS1010: Expected identifier: =
(53682,16-17): run-time error JS1195: Expected expression: =
(53684,16-17): run-time error JS1010: Expected identifier: =
(53684,16-17): run-time error JS1195: Expected expression: =
(53688,11-12): run-time error JS1010: Expected identifier: .
(53688,11-12): run-time error JS1195: Expected expression: .
(54572,10-11): run-time error JS1010: Expected identifier: =
(54572,10-11): run-time error JS1195: Expected expression: =
(54574,10-11): run-time error JS1010: Expected identifier: =
(54574,10-11): run-time error JS1195: Expected expression: =
(54577,8-9): run-time error JS1010: Expected identifier: .
(54577,8-9): run-time error JS1195: Expected expression: .
(54600,8-9): run-time error JS1010: Expected identifier: .
(54600,8-9): run-time error JS1195: Expected expression: .
(55727,12-13): run-time error JS1010: Expected identifier: =
(55727,12-13): run-time error JS1195: Expected expression: =
(55729,11-12): run-time error JS1010: Expected identifier: .
(55729,11-12): run-time error JS1195: Expected expression: .
(55996,8-9): run-time error JS1010: Expected identifier: .
(55996,8-9): run-time error JS1195: Expected expression: .
(56288,8-9): run-time error JS1010: Expected identifier: .
(56288,8-9): run-time error JS1195: Expected expression: .
(56385,8-9): run-time error JS1010: Expected identifier: .
(56385,8-9): run-time error JS1195: Expected expression: .
(56438,8-9): run-time error JS1010: Expected identifier: .
(56438,8-9): run-time error JS1195: Expected expression: .
(56551,8-9): run-time error JS1010: Expected identifier: .
(56551,8-9): run-time error JS1195: Expected expression: .
(56595,8-9): run-time error JS1010: Expected identifier: .
(56595,8-9): run-time error JS1195: Expected expression: .
(56653,8-9): run-time error JS1010: Expected identifier: .
(56653,8-9): run-time error JS1195: Expected expression: .
(56664,8-9): run-time error JS1010: Expected identifier: .
(56664,8-9): run-time error JS1195: Expected expression: .
(56691,9-10): run-time error JS1010: Expected identifier: =
(56691,9-10): run-time error JS1195: Expected expression: =
(56694,8-9): run-time error JS1010: Expected identifier: .
(56694,8-9): run-time error JS1195: Expected expression: .
(57116,10-11): run-time error JS1010: Expected identifier: =
(57116,10-11): run-time error JS1195: Expected expression: =
(57118,10-11): run-time error JS1010: Expected identifier: =
(57118,10-11): run-time error JS1195: Expected expression: =
(57121,8-9): run-time error JS1010: Expected identifier: .
(57121,8-9): run-time error JS1195: Expected expression: .
(57224,8-9): run-time error JS1010: Expected identifier: .
(57224,8-9): run-time error JS1195: Expected expression: .
(57316,8-9): run-time error JS1010: Expected identifier: .
(57316,8-9): run-time error JS1195: Expected expression: .
(57433,7-8): run-time error JS1010: Expected identifier: .
(57433,7-8): run-time error JS1195: Expected expression: .
(57633,7-8): run-time error JS1010: Expected identifier: .
(57633,7-8): run-time error JS1195: Expected expression: .
(57819,10-11): run-time error JS1010: Expected identifier: =
(57819,10-11): run-time error JS1195: Expected expression: =
(57821,10-11): run-time error JS1010: Expected identifier: =
(57821,10-11): run-time error JS1195: Expected expression: =
(57824,8-9): run-time error JS1010: Expected identifier: .
(57824,8-9): run-time error JS1195: Expected expression: .
(58033,10-11): run-time error JS1010: Expected identifier: =
(58033,10-11): run-time error JS1195: Expected expression: =
(58035,10-11): run-time error JS1010: Expected identifier: =
(58035,10-11): run-time error JS1195: Expected expression: =
(58038,8-9): run-time error JS1010: Expected identifier: .
(58038,8-9): run-time error JS1195: Expected expression: .
(58099,9-10): run-time error JS1010: Expected identifier: =
(58099,9-10): run-time error JS1195: Expected expression: =
(58100,8-9): run-time error JS1010: Expected identifier: .
(58100,8-9): run-time error JS1195: Expected expression: .
(58288,8-9): run-time error JS1010: Expected identifier: .
(58288,8-9): run-time error JS1195: Expected expression: .
(58586,8-9): run-time error JS1010: Expected identifier: .
(58586,8-9): run-time error JS1195: Expected expression: .
(58746,8-9): run-time error JS1010: Expected identifier: .
(58746,8-9): run-time error JS1195: Expected expression: .
(58784,8-9): run-time error JS1010: Expected identifier: .
(58784,8-9): run-time error JS1195: Expected expression: .
(58988,8-9): run-time error JS1010: Expected identifier: .
(58988,8-9): run-time error JS1195: Expected expression: .
(59046,15-16): run-time error JS1010: Expected identifier: .
(59046,15-16): run-time error JS1195: Expected expression: .
(59074,11-12): run-time error JS1010: Expected identifier: .
(59074,11-12): run-time error JS1195: Expected expression: .
(59367,9-10): run-time error JS1010: Expected identifier: =
(59367,9-10): run-time error JS1195: Expected expression: =
(59368,8-9): run-time error JS1010: Expected identifier: .
(59368,8-9): run-time error JS1195: Expected expression: .
(59419,9-10): run-time error JS1010: Expected identifier: .
(59419,9-10): run-time error JS1195: Expected expression: .
(59528,8-9): run-time error JS1010: Expected identifier: .
(59528,8-9): run-time error JS1195: Expected expression: .
(60916,8-9): run-time error JS1010: Expected identifier: .
(60916,8-9): run-time error JS1195: Expected expression: .
(60946,8-9): run-time error JS1010: Expected identifier: .
(60946,8-9): run-time error JS1195: Expected expression: .
(61034,8-9): run-time error JS1010: Expected identifier: .
(61034,8-9): run-time error JS1195: Expected expression: .
(61162,8-9): run-time error JS1010: Expected identifier: .
(61162,8-9): run-time error JS1195: Expected expression: .
(61212,8-9): run-time error JS1010: Expected identifier: .
(61212,8-9): run-time error JS1195: Expected expression: .
(61713,10-11): run-time error JS1010: Expected identifier: =
(61713,10-11): run-time error JS1195: Expected expression: =
(61715,10-11): run-time error JS1010: Expected identifier: =
(61715,10-11): run-time error JS1195: Expected expression: =
(61718,8-9): run-time error JS1010: Expected identifier: .
(61718,8-9): run-time error JS1195: Expected expression: .
(61763,9-10): run-time error JS1010: Expected identifier: .
(61763,9-10): run-time error JS1195: Expected expression: .
(61788,2-3): run-time error JS1010: Expected identifier: .
(61788,2-3): run-time error JS1195: Expected expression: .
(62022,8-9): run-time error JS1010: Expected identifier: .
(62022,8-9): run-time error JS1195: Expected expression: .
(62048,8-9): run-time error JS1010: Expected identifier: .
(62048,8-9): run-time error JS1195: Expected expression: .
(62165,9-10): run-time error JS1010: Expected identifier: .
(62165,9-10): run-time error JS1195: Expected expression: .
(62172,8-9): run-time error JS1010: Expected identifier: .
(62172,8-9): run-time error JS1195: Expected expression: .
(62223,16-17): run-time error JS1010: Expected identifier: =
(62223,16-17): run-time error JS1195: Expected expression: =
(62225,16-17): run-time error JS1010: Expected identifier: =
(62225,16-17): run-time error JS1195: Expected expression: =
(62228,11-12): run-time error JS1010: Expected identifier: .
(62228,11-12): run-time error JS1195: Expected expression: .
(62475,9-10): run-time error JS1010: Expected identifier: =
(62475,9-10): run-time error JS1195: Expected expression: =
(62476,8-9): run-time error JS1010: Expected identifier: .
(62476,8-9): run-time error JS1195: Expected expression: .
(62518,8-9): run-time error JS1010: Expected identifier: .
(62518,8-9): run-time error JS1195: Expected expression: .
 */
var device_t_id_element = document.getElementById('device_TransactionId');
if (device_t_id_element != null)
{
    var a_hostUrl = HA.Accertify_Host_Url;
    var a_sid = HA.Accertify_Sid;
    var d = new Date().getTime();
    var a_tid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,
    function (c) {
        var r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
    device_t_id_element.value = a_tid;

    window._cc = window._cc || [];
    _cc.push(['ci', {
        'sid': a_sid,
        'tid': a_tid
    }]);
    _cc.push(['run', ('https:' == document.location.protocol ? 'https://'
        : 'http://') + a_hostUrl]);
    (function () {
        var c = document.createElement('script');
        c.type = 'text/javascript';
        c.async = true;
        c.src = ('https:' == document.location.protocol ? 'https://' :
            'http://') + a_hostUrl + '/cc.js?sid=' + a_sid + '&ts=' + (new
                Date()).getTime();
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(c, s);
    })();
}
;
(function (angular) {

	'use strict';

	//utility for reading cookie
	function readCookie(name) {
		name += '=';
		for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--) {
			if (!ca[i].indexOf(name)) {
				return ca[i].replace(name, '');
			}
		}
	}

	var lang = readCookie('website#lang');
	var language = 'en-us';

	if (lang != null && lang !== 'en') {
		language = lang;
	}

	// NEP: TODO: Find an alternative to this:
	// require.config({
	//     paths: {
	//         'angular-locale': '../lib/angular/i18n/ha-angular-locale_' + language,

	var appDeps = ((window.HA != null) && (window.HA.hawaiianAppDeps != null)) ? window.HA.hawaiianAppDeps : [];
	var app = angular.module('hawaiianApp', [
		'ng.shims.placeholder',
		'ngSanitize',
		'oc.lazyLoad',
		'haConfigModule',
		'haUtilsModule',
		'haGlobalsModule',
		'haName',
		'haJson',
		'ngPromiseExtras',
		'haDataModule',
		'haScrollModule',
		'haDraggableModule',
		'haRevealOnLoadModule',
		'haWhenReadyModule',
		'selectOnClickModule',
		'currencyNoDecimalsFilter',
		'haRoundingFiltersModule',
		'addIconFilter',
		'haErrorsModule',
		'haFootNoteModule',
		'haFileModule',
		'haKeyboardModule',
		'haToggleModule',
		'noClickFocusModule',
		'haWindowEventsModule',
		'haCurrencyModule',
		'haLocalizeNameModule',
		'haLocalizeDateModule',
		'haPasswordStrengthModule',
		'haFormValidationModule',
		'haCarouselModule',
		'haCalendarModule',
		'haCalendar2Module',
		'haCalendarEventsModule',
		'shareWidgetModule',
		'haDateInputModule',
		'haLocationInputModule',
		'haGenericLocationInputModule',
		'haRecentSearchesModule',
		'haHelpAndTipsModule',
		'haBookStickyProgressBarModule',
		'haAlertModule',
		'haDynamicModalModule',
		'haCreditCardTypesModule',
		'haBreadcrumbModule',
		'haAvatarSelectionModule',
		'haCustomDropdownModule',
		'haAvatarModule',
		'haTooltipModule',
		'haGlobalAlertModule',
		'haGlobalMessageModule',
		'haMapNavigatorModule',
		'haErrorPageModule',
		'haProgressBarModule',
		'haStickyBookingWidgetModule',
		'haPassengerCountModule',
		'haCalendarEventsService',
		'haCustomerSelectionsService',
		'haFavoritesService',
		'haHelpAndTipsService',
		'haModalService',
		'haPassengersService',
		'haSearchCacheService',
		'haSegmentService',
		'haUserService',
		'haGlobalHeaderModule',
		'haHeaderSearchModule',
		'exploreHeroModule',
		'exploreMapModule',
		'haBookFlightResultsModule',
		'haBookFlightResultsEndOnEndModule',
		'haGlobalFooterModule',
		'haFlightHopModule',
		'haItineraryModule',
		'haBookingHeroModule',
		'haAccountSignInModule',
		'haAccountRegistrationModule',
		'haReceiptModule',
		'haReceiptEndOnEndModule',
		'haPaymentMethodsModule',
		'haProfileSettingsModule',
		'haPurchaseMilesModule',
		'haCheckoutModule',
		'haTermsConditionsModule',
		'haManageTravelersModule',
		'haCorporateTravelersModule',
		'haMyTripsModule',
		'haCustomDirectivesModule',
		'haMyDashboardModule',
		'haPurchaseConfirmationModule',
		'haInsufficientMilesModule',
		'haBookInsufficientMilesModule',
		'haFlightStatusModule',
		'travelCreditRedemptionModule',
		'haAccountLandingModule',
		'haPrintConfirmationModule',
		'haMyTripsItineraryDetailsModule',
		'haEcDowngradeModalModule',
		'haReshopSelectionModalModule',
		'haMileageStatementModule',
		'haGlobalAlertsModule',
		'haSearchResultsModule',
		'haFeaturedDealsModule',
		'haMainImageContentBlockModule',
		'haCmsChildNavFrontPageModule',
		'haCmsChildNavSubPageModule',
		'haPhotoGalleryModule',
		'haCmsSubpageBannerHeadlineModule',
		'haCmsDetailPageBannerHeadlineModule',
		'haCmsBodyCopyAdModule',
		'haCmsFullWidthPromotionModule',
		'haAdvancedSearchModule',
		'haCmsRichTextEditorModule',
		'haShareMilesModule',
		'haMemberPhoneInfoModule',
		'haContactUsModule',
		'haContactUsTopicsModule',
		'haCashBagModule',
		'haChinaRewardsModule',
		'haMemberPersonalInfoModule',
		'haMemberDobModule',
		'haSecurityQuestionAnswerModule',
		'haPaymentModule',
		'haPaymentCdeModule',
		'haMemberAddressModule',
		'haMemberAddressModule2',
		'haMemberSubscriptionsModule',
		'haCmsBodyCopyWithSidebarModule',
		'haHotelPackagesModule',
		'haMilesMaximizerModule',
		'haDonateMilesModule',
		'haPremierClubModule',
		'haThumbGalleryModule',
		'haLowFareSearchModule',
		'haFlexiblePriceViewModule',
		'haPriceApiModule',
		'haAncillariesModule',
		'haEcertAPI',
		'ui.mask',
		'ui.keypress',
		'ui.bootstrap.pagination',
		'hmTouchEvents',
		'ngTouch',
		'ngAnimate',
		'duScroll',
		'haPerformanceStatsModule',
		'haDateUtilsModule',
		'haBookingFormModule',
		'haDatepickerModule',
		'haEmitErrorModule',
		'haIsWorkingModule',
		'haAirportsModule',
		'haInputName',
		'haRequestPastMilesModule',
		'haSitecoreModule',
		'haScopeModule',
		'haViewModelModule',
		'haRequestPastMilesModule',
		'formValidation',
		'haMemberDiscountsModule',
		'haSecondaryHeaderModule',
		'haGiftCardModule',
		'haFlightScheduleModule',
		'haContactUsModule',
		'haTravelGoalModule',
		'haMyReceiptsModule',
		'HaConfirmWhenDirtyModule',
		'haTripSummaryModule',
		'promoDetailsModule',
		'haNeatFormsModule',
		'haCartrawlerFormsModule',
		'haTrueUpModule',
		'haEcertEtcoModule',
		'haBarclaysModule',
		'ui.bootstrap.tabs',
		'haNitpModule',
		'haMyDashboardModule',
		'ui.bootstrap.accordion',
		'haPersonalMessageModule',
		'haAffiliateModule',
		'haAncillaryUpsellsModule',
		'haAncillaryUpsellsMytripsModule',
		'haPaxModule',
		'haWatchTrailerModule',
		'haAdvanceToNextInputService',
		'haPurchasedAncillariesModule',
		'haNitpDashboardModule',
		// 'haGoogleMapModule',
		'haPremierClubDashboardModule',
		'HaFavoriteModule',
		'haScrollSpyModule',
		'haContextualHelpModule',
		'uinqueFilter',
		'haRegexModule',
		'haFoodlandRegistrationModule',
		'haButtonSpinnerModule',
		'haUpcomingTripsDashboardModule',
		'haPaymentTypesService',
		'haPaymentMethodsAPI',
		'haCountUpModule',
		'haContentModalModule',
		'haDomModalModule',
		'haStoriesModule',
		'haLoadingSpinnerModule',
		'haEventsCalendarModule',
		'focusOnDisplayModule',
		'haShareItineraryModule',
		'haMyAccountAPI',
		'haPaxServiceModule',
		'haStickyModule',
		'haHomepageStoriesAndEventsModule',
		'haAriaLiveModule',
		'haBirthdateModule',
		'haPaxCountryModule',
		'haPaymentTypeMasterPassModule',
		'haElementDirectivesModule',
		'haClearDataModule',
		'haElementDirectivesModule',
		'haItineraryDetailsPaxInfoModule',
		'haRangeModule',
		'haProgressBreadcrumbModule',
		'haVerticalSeatmapModule',
		'haVerticalSeatSelectionModule',
		'haStickyMessageBarModule',
		'haVerticalSeatmapPreviewModule',
		'haVariableSeatmapModule',
		'haVariableSeatSelectionModule',
		'haVariableSeatmapPreviewModule',
		'haPrimaryNavAccountMenuModule',
		'haPrimaryNavAlertsModule',
		'haSigninPromptModule',
		'haKisaTermsModule',
		'haBodyExtensionModule',
		'haSessionTimeoutAPI',
		'haMobileService',
		'haTableReflowModule',
		'haAirportOrAddressInputModule',
        'haHotelsInputModule',
		'haRVFormModule',
		'haRestrictSpacesModule',
        'haInFlightReceiptsModule',
        'haAirportTimezonesModule',
        'haDurationModule',
		'haReAuthServiceModule',
		'haUpgradeModalModule',
		'haNativeAppModalModule',
		'haFormNativeappLinkSmsModule',
		'haPersistentNativeAppBannerModule',
		'haNativeUpsellBannerModule',
		'haDisableOnClickModule',
		'haAncillaryUpsellsServiceModule',
		'haUpsellGridModule',
		'TitleCaseModule',
		'haEncryptionModule',
		'haLaunchDarklyModule',
		'haTimedHideModule',
		'haDealTilesModule',
		'haResponsiveAttributeModule',
		'haPairLinksModule',
		'bookingWidgetSlimModule',
		'haClampModule',
		'haDelayAutoplayModule',
		'haAccordionModule'
	].concat(appDeps));

	app.config([
		'$logProvider',
		'$sceProvider',
		'$anchorScrollProvider',
		'$provide',
		'$ocLazyLoadProvider',
		'uiMask.ConfigProvider',

		function ($logProvider, $sceProvider, $anchorScrollProvider, $provide, $ocLazyLoadProvider, uiMaskConfigProvider) {
			// Enable debug logging for 'local.*' hostnames, or with #debug URL hash...disable for prod.
			var isDebug = (window.location.search.indexOf('debug') >= 0);
			var isLocal = (window.location.hostname.indexOf('local') === 0);
			$logProvider.debugEnabled(isDebug || isLocal);

			// Completely disable SCE to support IE7.
			$sceProvider.enabled(false);

			$anchorScrollProvider.disableAutoScrolling();

			$ocLazyLoadProvider.config({
				debug: (isDebug || isLocal),
				events: false,
				modules: [{
					name: 'haGoogleMapModule',
					files: [
						'/Scripts/app/components/ha-google-map.js'
					]
				}, {
					name: 'haShareButtonModule',
					files: [
						'/Scripts/app/components/ha-share-button.js'
					]
				}, {
					name: 'haDocsModule',
					files: [
						'/Scripts/app/controllers/ha-docs.js'
					]
				}, {
					name: 'haExpertBookingModule',
					files: [
						'/Scripts/app/modules/ha-expert-booking.js',
						'/Scripts/app/services/ha-expert-booking-service.js',
						'/Scripts/app/modules/ha-expert-booking-input.js',
						'/Scripts/app/modules/ha-expert-booking-review.js',
						'/Scripts/app/modules/ha-expert-booking-summary.js',
						'/Scripts/app/modules/ha-expert-booking-table.js',
						'/Scripts/app/modules/ha-expert-booking-payment.js'
					]
				}, {
					name: 'haCartrawlerOneWayFormsModule',
					files: [
						'/Scripts/app/controllers/ha-cartrawler-oneway-forms.js'
					]
				}]
			});

			$provide.decorator('ngModelDirective', ['$delegate', function ($delegate) {
				var ngModel = $delegate[0];
				var controller = ngModel.controller;

				ngModel.controller = ['$scope', '$element', '$attrs', '$injector',
					function (scope, element, attrs, $injector) {
						var name = attrs.name;
						if (name != null && (name.indexOf('{{') > -1)) {
							var $interpolate = $injector.get('$interpolate');
							attrs.$set('name', $interpolate(name)(scope));
						}
						$injector.invoke(controller, this, {
							'$scope': scope,
							'$element': element,
							'$attrs': attrs
						});
					}
				];
				return $delegate;
			}]);

			$provide.decorator('formDirective', ['$delegate', function ($delegate) {
				var form = $delegate[0];
				var controller = form.controller;

				form.controller = ['$scope', '$element', '$attrs', '$injector',
					function (scope, element, attrs, $injector) {
						var nameOrForm = attrs.name || attrs.ngForm;
						if (nameOrForm != null && nameOrForm.indexOf('{{') > -1) {
							var $interpolate = $injector.get('$interpolate');
							attrs.$set('name', $interpolate(nameOrForm)(scope));
						}
						$injector.invoke(controller, this, {
							'$scope': scope,
							'$element': element,
							'$attrs': attrs
						});
					}
				];
				return $delegate;
			}]);

			uiMaskConfigProvider.clearOnBlur(false);
		}
	]);

	app.run([
		'$q',
		'$rootScope',
		'$location',
		'$anchorScroll',
		'haModal',
		'haGlobals',
		'haCitiesSvc',
		'haSecondaryHeaderSvc',
		'haUtils',
        'haMobileSvc',
		'haConfig',

		function ($q, $rootScope, $location, $anchorScroll, haModal, haGlobals, haCitiesSvc, haSecondaryHeaderSvc, haUtils, haMobileSvc, haConfig) {
			if (typeof Promise === 'undefined') {
				//make like ES6 promises (actually copied from Angular 1.4
				window.Promise = function (fn) {
					var d = $q.defer();
					fn(function (v) {
						d.resolve(v);
					}, function (v) {
						d.reject(v);
					});
					return d.promise;
				};
				Promise.resolve = function (val) {
					return new Promise(function (resolve) {
						resolve(val);
					});
				};
				Promise.reject = $q.reject;
				Promise.all = $q.all;
			}

			$rootScope.haModal = haModal;
			$rootScope.haCitiesSvc = haCitiesSvc;
			$rootScope.haSecondaryHeaderSvc = haSecondaryHeaderSvc;

			// if hash, jump to id
			function checkHash() {
				if ($location.hash()) {
					//console.debug('Location Hash:', $location.hash());
					$anchorScroll();
				}
			}

			// on load, check hash
			checkHash();
			// on location change, check hash
			$rootScope.$on('$locationChangeSuccess', function () {
				checkHash();
			});

			haGlobals(['$langCode', '$language', '$currency'], function (langCode, language, currency) {
				haConfig.setLanguageCode(langCode);
				$rootScope.$language = language;
				$rootScope.$currency = currency;
			});

			$rootScope.constructor.prototype.getMediaImage = function (str) {
				return haUtils.getImageFromSiteCoreString(str);
			};

			$rootScope.bccCapture = function (campaignId, cellId, referrerId) {
				return haUtils.bccCapture(campaignId, cellId, referrerId);
			};

			$rootScope.isTargetBcusEligible = function () {
				//if (!url.toLowerCase().indexOf("/book/payment") !== -1)
				return haUtils.isTargetBcusEligible();
			};

			$rootScope.getTemplateUrl = haConfig.getTemplateUrl;
			$rootScope.getRazorTemplateUrl = haConfig.getRazorTemplateUrl;
			$rootScope.getImgUrl = haConfig.getImgUrl;
			$rootScope.csrf = window.tokens;
			$rootScope.moment = moment;

            $rootScope.isMobile = haMobileSvc.determineIsMobile();

            angular.element(window).bind('resize', function () {
                $rootScope.isMobile = haMobileSvc.determineIsMobile();
                $rootScope.$apply();
            });
		}
	]);
})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haMemberAddressModule', []);

	module.directive('haMemberAddress', ['haGlobals', function (haGlobals) {

		var memberAddressController = function ($scope, $timeout) {
			haGlobals(['jsonMemberAddress', 'isDisabled'], function (jsonMemberAddress, isDisabled) {
				$scope.MemberAddress = jsonMemberAddress;
				$scope.isDisabled = isDisabled;
				if ($scope.isDisabled) {
					$timeout(function() {
						$('[ha-member-address]').find('input').prop('disabled', true);
						$('[ha-member-address]').find('select').prop('disabled', true);
					}, 0);
				}
			});
			$scope.displayStateDropDown = true;
			$scope.stateDropDownDataSource = [];
			$scope.displayAddress3 = false;
			$scope.stateColWidth = 3;
			$scope.cityColWidth = 9;
			// constant dependent on font sizing
			var charPerQuarter = 7;

			$scope.$on('AddressInfoDetails', function (event, data) {
				var address = data.AddressInfo;
				$scope.MemberAddress = address;
				countryUpdated($scope.MemberAddress.Country);
			});

			if ($scope.MemberAddress.Country == null) {
				haGlobals('defaultCountry', function (defaultCountry) {
					$scope.MemberAddress.Country = defaultCountry;
				});
			}

			$scope.$watch('stateDropDownDataSource', function (newValue) {
				if (!newValue) {
					return;
				}
				var largestCharCount = 0;
				for (var i = 0; i < newValue.length; i++) {
					var charCount = newValue[i].name.length;
					largestCharCount = charCount > largestCharCount ? charCount : largestCharCount;
				}
				if (largestCharCount > charPerQuarter) {
					$scope.stateColWidth = 6;
					$scope.cityColWidth = 6;
				}
			});

			$scope.$watch('MemberAddress.Country', function (newValue) {
				countryUpdated(newValue);
			});

			function countryUpdated(newValue) {				
				if (newValue) {
					$scope.displayAddress3 = (newValue === 'JPN' || newValue === 'CHN' || newValue === 'TWN' || newValue === 'KOR');
					$scope.displayStateDropDown = false;
					haGlobals('jsonCountryStates', function (jsonCountryStates) {
						var stateSource = $(jsonCountryStates).filter(function () {
							return typeof (this) !== 'undefined' && this.Value === newValue;
						});

						if (stateSource.length > 0 && stateSource[0].IsStatesListAvailable === true) {
							$scope.displayStateDropDown = true;
							$scope.stateDropDownDataSource = [];
							angular.forEach(stateSource[0].States, function (item /* , $index */) {
								$scope.stateDropDownDataSource.push({name: item.Name, value: item.Value});
							});
							if ($scope.MemberAddress.State != null) {
								var newState = $($scope.stateDropDownDataSource).filter(function () {
									return this.value === ($scope.MemberAddress.State.value || $scope.MemberAddress.State);
								}).get(0);

								if (newState) {
									$scope.MemberAddress.State = newState;
								}
								else {
									$scope.MemberAddress.State = '';
								}
							}
						}
						else {
							$scope.displayStateDropDown = false;
							$scope.stateColWidth = 3;
							$scope.cityColWidth = 9;
							if ($scope.MemberAddress.State instanceof Object) {
								$scope.MemberAddress.State = '';
							}
						}
					});

					if ($scope.isDisabled) {
						$timeout(function() {
							$('[ha-member-address]').find('input').prop('disabled', true);
							$('[ha-member-address]').find('select').prop('disabled', true);
						}, 0);
					}
				}
			}
		};

		memberAddressController.$inject = ['$scope', '$timeout'];

		return {
			restrict: 'A',
			scope: true,
			controller: memberAddressController
		};
	}]);

	module.directive('zipCodeCountryCheck', ['haGlobals', 'haUtils', function (haGlobals, haUtils) {
		return {
			require: 'ngModel',
			link: function (scope, elem, attrs, ctrl) {
				elem.on('blur', function (/* evt */) {
					scope.ValidateZipCode();
				});

				scope.ValidateZipCode = function () {
					if (!scope.isValid()) {
						if (elem.val() && elem.val() !== '' && attrs.country && attrs.country !== '') {
							haGlobals('jsonMemberAddress', function (jsonMemberAddress) {
								var zipConfig = $(jsonMemberAddress.CountryStateDropDown).filter(function () {
									return this !== undefined && this.Value.toLowerCase() === attrs.country.toLowerCase();
								});
								if (zipConfig.length) {
									var zipRegex = new RegExp(zipConfig[0].PinCodeRegex);
									if (!zipRegex.test(elem.val())) {
										ctrl.$setValidity('ZipCodeCountryCheck', false);
										// NEP: FIXME: Serious refactoring needed!
										if ((elem.parent().scope() != null) && (elem.parent().scope().invalidate != null)) {
											elem.parent().scope().invalidate();
										}
									}
									else {
										ctrl.$setValidity('ZipCodeCountryCheck', true);
										if ((elem.parent().scope() != null) && (elem.parent().scope().validate != null)) {
											elem.parent().scope().validate();
										}
									}
								}
								else {
									ctrl.$setValidity('ZipCodeCountryCheck', true);
									if ((elem.parent().scope() != null) && (elem.parent().scope().validate != null)) {
										elem.parent().scope().validate();
									}
								}
							});
						}
					}
				};

				var debounceValidateZipCode = haUtils.debounce(function () {
					haUtils.safeApply(scope, function () {
						ctrl.$setValidity('ZipCodeCountryCheck', true);
						scope.ValidateZipCode();
					});
				}, 500, false);

				scope.$watch(attrs.ngModel, debounceValidateZipCode);

				scope.$on('validateForm', function () {
					scope.ValidateZipCode();
				});

				attrs.$observe('country', function (attrValue) {
					if (attrValue) {
						scope.ValidateZipCode();
					}
				});

				scope.isValid = function () {
					if (elem.controller('ngModel').$error) {
						var isError = false;
						var obj = elem.controller('ngModel').$error;
						for (var prop in obj) {
							if (obj.hasOwnProperty(prop)) {
								if (prop !== 'ZipCodeCountryCheck' && obj[prop] === true) {
									isError = true;
									break;
								}
							}
						}
						return isError;
					}
					else {
						return false;
					}
				};
				scope.ValidateZipCode();
			}
		};
	}]);

})(angular);

;
(function (angular) {

    'use strict';

    var module = angular.module('haMemberAddressModule2', ['haUtilsModule', 'haGeoDataModule']);

    module.directive('haMemberAddress2', [
		'$log',
		'$timeout',
		'haUtils',
		'haGlobals',
		'haGeoDataSvc',

		function ($log, $timeout, haUtils, haGlobals, geo) {

		    var link = function ($scope, $el, $attrs) {

		        // Connect to the AddressVM in the host page's VM
		        haUtils.attachPartialVM($scope, $attrs, 'AddressVM');
		    };

		    var ctrl = ['$scope', '$rootScope', function ($scope, $rootScope) {

		        // Pull in FormName for $setPristine()
		        var formName;
		        var namePrefix;
		        haGlobals(['formName', 'namePrefix'], function (f, n) {
		            formName = f;
		            namePrefix = n;
		        });

		        // Fetch list of Countries in non true-up context
		        if (!$scope.countries || $scope.countries.length === 0) {
		            haGlobals('countryData', function (countryData) {
		                $scope.countries = geo.setCountryData(countryData);
		            });
		        }

		        angular.extend($scope, {
		            addressStyle: geo.getAddressStyle,
		            hasPostalCode: geo.hasPostalCode,
		            postalRegex: geo.getPostalCodeRegex,
		            phoneRegex: geo.getPhoneNumberRegex,
		            cityRegex: geo.getCityRegex,
		            getStatesAndCities: function (countryKey, postalCode) {
		                var invalidPostalCodeState = $scope.VM.State;
		                var invalidPostalCode = function (error) {
		                    $log.debug('_invalidPostalCode', error);
		                    // $scope[formName][namePrefix+'ZipCode'].$setValidity('notFound', false);
		                    $scope.stateData = null;
		                    $scope.VM.State = null;
		                    $scope.cityData = null;
		                    $scope.VM.City = null;

		                    // force error message to appear despite form not being submitted yet
		                    // this script is used on different forms with different names (and field names), so we need to check which page we're on
		                    if (geo.hasPostalCode(countryKey)) {
		                        if ($scope.registration) {
		                            $scope.registration['MemberAddress.ZipCode'].$touched = true;
		                        }

		                        if ($scope.PremierClubForm) {
		                            $scope.PremierClubForm['EnrolleeDetails.MemberAddress.ZipCode'].$touched = true;
		                        }

		                        if ($scope.ContactInformationForm) {
		                            $scope.ContactInformationForm['MemberAddress.ZipCode'].$touched = true;
		                        }
		                    }
		                };
		                var memberCityInfo = $scope.VM.City;
		                $scope.lookupSpinner = true;
		                geo.getStateAndCities(countryKey, postalCode).then(function (data) {
		                    $scope.lookupSpinner = false;

		                    if (!data.IsValid) {
		                        invalidPostalCode();
		                    }

		                    var hasStates = (data.States.length > 0);
		                    if (hasStates) {
		                        if ($scope.VM.Country.IsoCode === 'USA') {
		                            haGlobals('_MemberAddress_PostalCode', function (g) {
		                                $timeout(function () {
		                                    $scope[g.formName][g.namePrefix + 'ZipCode'].$setValidity('notFound', true);
		                                }, 800);
		                            });
		                        }

		                        // Populate dropdowns
		                        $scope.stateData = data.States;
		                        $scope.cityData = data.Cities;
		                        $scope.VM.PostalCodeKey = data.PostalCodeKey;

		                        // Pre-Filled or Default State
		                        if ($scope.VM != null && $scope.VM.State != null &&
									typeof $scope.VM.State === 'string' && $scope.VM.State.length > 0) {
		                            $scope.VM.State = lookupByName(data.States, $scope.VM.State);
		                        } else if (data.States != null && data.States.length === 1) {
		                            $scope.VM.State = data.States[0];
		                        }

		                        // If name failed, try by key
		                        if ($scope.VM != null && $scope.VM.State == null && $scope.VM.StateKey) {
		                            $scope.VM.State = lookupByKey(data, $scope.VM.StateKey);
		                        }

		                        // Pre-Filled or Default City
		                        if ($scope.VM != null && $scope.VM.City != null &&
									typeof $scope.VM.City === 'string' && $scope.VM.City.length > 0) {
		                            $scope.VM.City = lookupByName(data.Cities, $scope.VM.City);
		                        } else if (data.Cities != null && data.Cities.length === 1) {
		                            $scope.VM.City = data.Cities[0];
		                        }
		                        else if (data.Cities != null && data.Cities.length > 1) {
		                            $scope.VM.City = lookupByName(data.Cities, memberCityInfo);
		                        }
		                    } else {
		                        if ($scope.VM.Country.IsoCode === 'USA') {
		                            haGlobals('_MemberAddress_PostalCode', function (g) {
		                                $scope[g.formName][g.namePrefix + 'ZipCode'].$setValidity('notFound', false);
		                            });
		                        }

		                        $scope.lookupSpinner = true;
		                        geo.getStates(countryKey).then(function (data) {
		                            $scope.lookupSpinner = false;
		                            // Populate dropdowns
		                            $scope.stateData = data;


		                            // Pre-Filled or Default State
		                            if ($scope.VM != null) {
		                                if ($scope.VM.State != null) {
		                                    $scope.VM.State = lookupByName(data, $scope.VM.State);
		                                }
		                                if ($scope.VM.State == null) {
		                                    $scope.VM.State = lookupByName(data, invalidPostalCodeState);
		                                }
		                                if ($scope.VM.State == null && $scope.VM.StateKey) {
		                                    $scope.VM.State = lookupByKey(data, $scope.VM.StateKey);
		                                }
		                                // Pre-Filled or Default City
		                                if ($scope.VM.City != null) {
		                                    $scope.VM.City = lookupByName(data.Cities, $scope.VM.City);
		                                }
		                            }
		                        }, function (error) {
		                            $log.debug('getStates', error);
		                        });
		                    }
		                    $scope.$emit('$haMemberAddressReady');
		                });
		            },

		            hideStateInput: function () {
		                return ($scope.stateData != null) && ($scope.stateData.length === 0) || geo.noStates();
		            },
		            useCityDropdown: function () {
		                return ($scope.cityData != null) && ($scope.cityData.length > 0);
		            },
		            resetAddress: function (country) {
		                if (country == null) {
		                    return;
		                }

		                // Clear the form, usually on Country change
		                $scope.stateData = null;
		                $scope.cityData = null;

		                if ($scope.VM != null) {
		                    $scope.VM.ZipCode = null;
		                    $scope.VM.State = null;
		                    $scope.VM.City = null;
		                    $scope.VM.Address1 = null;
		                    $scope.VM.Address2 = null;
		                    $scope.VM.Address3 = null;
		                }

		                // Clear $dirty and $error
		                if ($scope[formName] != null) {
		                    $scope[formName].$setPristine();
		                }
		            }
		        });

		        var selectCountryByISO = function (countryISO) {
		            for (var i = 0; i < $scope.countries.length; i++) {
		                if ($scope.countries[i].IsoCode === countryISO) {
		                    $scope.VM.Country = $scope.countries[i];
		                    break;
		                }
		            }
		        };

		        var lookupByName = function (data, name) {   // obj
		            if (data != null && name != null && typeof name === 'string') {
		                for (var i = 0; i < data.length; i++) {
		                    if ((data[i].Name != null && data[i].Name.toLowerCase() === name.toLowerCase()) ||
								(data[i].DisplayName != null && data[i].DisplayName.toLowerCase() === name.toLowerCase())) {
		                        return data[i];
		                    }
		                }
		            }
		            return null;
		        };

		        var lookupByKey = function (data, key) {   // obj
		            if (data != null && key != null && typeof key === 'number') {
		                for (var i = 0; i < data.length; i++) {
		                    if (data[i].Key === key) {
		                        return data[i];
		                    }
		                }
		            }
		            return null;
		        };

		        // Load Defaults
		        $scope.$watch('VM', function (value) {
		            if (value == null) {
		                return;
		            }

		            if ($scope.VM.Country != null && $scope.VM.Country !== '') {
		                selectCountryByISO($scope.VM.Country);
		            } else if ($scope.VM.CountryCode != null) {
		                selectCountryByISO($scope.VM.CountryCode);
		            } else if (($scope.VM.Country == null) || ($scope.VM.Country === '')) {
		                haGlobals('defaultCountryISO', selectCountryByISO);
		            }

		            if ($scope.VM.AccountDetail != null &&
						$scope.VM.AccountDetail.ZipCode != null) {
		                $scope.VM.ZipCode = $scope.VM.AccountDetail.ZipCode;
		            }
		        });

		        // Handle the case where an existing account has a country in the "outlier table"
		        // by defaulting to the default country for the current domain.
		        $scope.$watch('VM.CountryKey', function (value) {
		            if ((value === -1) || (value === -2)) {
		                haGlobals('defaultCountryISO', selectCountryByISO);
		            }
		        });

		        $scope.$watch('VM.Country', function (value) {
		            geo.setActiveCountry(value);
		            if (value == null) {
		                return;
		            }

		            var country = $scope.VM.Country;

		            if (country != null && !geo.hasPostalCode(country.Key)) {
		                $scope.getStatesAndCities(country.Key);
		            }
		            // Tell ha-member-phone-info-2 to change the country code
		            if ($scope.VM.Country.IsoCode != null) {
		                $rootScope.$broadcast('countryChanged', $scope.VM.Country.IsoCode);
		            }
		        });

		        // Load States and (maybe) Cities for Country+PostalCode
		        $scope.$watch('VM.ZipCode', function (value) {
		            if (value == null) {
		                return;
		            }

		            var country = $scope.VM.Country;
		            if (country != null) {
		                $scope.getStatesAndCities(country.Key, value);
		            }
		        });
		    }];

		    return {
		        restrict: 'A',
		        scope: true,
		        link: link,
		        controller: ctrl
		    };
		}
    ]);

})(angular);
;
(function (angular) {

	'use strict';

	var module;
	try {
		module = angular.module('haMemberPhoneInfoModule');
	} catch (e) {
		module = angular.module('haMemberPhoneInfoModule', ['haUtilsModule']);
	}

	module.directive('haMemberPhoneinfo', ['haGlobals', '$window', function (haGlobals, $window) {

		// globals
		var defaultPhoneCountryCode = $window.defaultPhoneCountryCode;

		var memberPhoneInfoController = function ($scope) {

			$scope.showphone2 = false;
			$scope.showphone3 = false;

			$scope.secondPhoneMandatory = false;
			$scope.thirdPhoneMandatory = false;

			var maxPhoneLength = 10;
			var maxPhoneArray = [];
			var minPhoneLength = 0;
			var minPhoneArray = [];
			var countryCodeDropDown = 'not-set';

			haGlobals(['jsonMemberPhoneInfo'], function (jsonMemberPhoneInfo) {
				$scope.PhoneDetails = jsonMemberPhoneInfo.PhoneDetails;

				countryCodeDropDown = jsonMemberPhoneInfo.CountryCodeDropDown;
				maxPhoneArray = jsonMemberPhoneInfo.CountryCodeDropDown.map(function (o) {
					return o.MaxPhoneLength;
				});
				maxPhoneLength = Math.max.apply(this, maxPhoneArray);

				minPhoneArray = $.makeArray($(jsonMemberPhoneInfo.CountryCodeDropDown).filter(function () {
					return typeof (this) !== 'undefined' && this.Value !== '';
				})).map(function (o) {
					return o.MinPhoneLength;
				});

			});

			var sortPhoneDetails = [
				{ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' },
				{ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' },
				{ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' }
			];

			for (var i = 0; i < $scope.PhoneDetails.length; i++) {
				if (+$scope.PhoneDetails[i].Type === 1) {
					sortPhoneDetails[0] = $scope.PhoneDetails[i];
				} else if (+$scope.PhoneDetails[i].Type === 3) {
					sortPhoneDetails[1] = $scope.PhoneDetails[i];
				} else if (+$scope.PhoneDetails[i].Type === 2) {
					sortPhoneDetails[2] = $scope.PhoneDetails[i];
				}
			}

			$scope.PhoneDetails = sortPhoneDetails;

			minPhoneLength = Math.min.apply(this, minPhoneArray);

			$scope.phoneOneMinLength = minPhoneLength;
			$scope.phoneOneMaxLength = maxPhoneLength;

			$scope.phoneSecondMinLength = minPhoneLength;
			$scope.phoneSecondMaxLength = maxPhoneLength;

			$scope.phoneThirdMinLength = minPhoneLength;
			$scope.phoneThirdMaxLength = maxPhoneLength;

			$scope.$watch('PhoneDetails[0].CountryCode', function (newValue) {
				var minAndMaxPhoneLength = $scope.getMaxAndMinPhoneLength(newValue);
				$scope.phoneOneMaxLength = minAndMaxPhoneLength.maxLength;
				$scope.phoneOneMinLength = minAndMaxPhoneLength.minLength;
			});

			$scope.getMaxAndMinPhoneLength = function (newValue) {
				if (newValue && newValue !== '') {
					var countryCode = $(countryCodeDropDown).filter(function () {
						return typeof (this) !== 'undefined' && this.Value === newValue;
					});
					if (countryCode.length) {
						return { minLength: countryCode[0].MinPhoneLength, maxLength: countryCode[0].MaxPhoneLength };
					}
					else {
						return { minLength: minPhoneLength, maxLength: maxPhoneLength };
					}
				}
				else {
					return { minLength: minPhoneLength, maxLength: maxPhoneLength };
				}
			};

			$scope.$watch('PhoneDetails[1].CountryCode', function (newValue) {
				var minAndMaxPhoneLength = $scope.getMaxAndMinPhoneLength(newValue);
				$scope.phoneSecondMaxLength = minAndMaxPhoneLength.maxLength;
				$scope.phoneSecondMinLength = minAndMaxPhoneLength.minLength;
				$scope.setSecondPhonemandatory();
			});

			$scope.$watch('PhoneDetails[1].Number', function (/* newValue */) {
				$scope.setSecondPhonemandatory();
			});

			$scope.setSecondPhonemandatory = function () {
				if (!($scope.PhoneDetails[1].CountryCode) ||
					($scope.PhoneDetails[1].Number && $scope.PhoneDetails[1].Number !== '')) {
					$scope.secondPhoneMandatory = true;
				}
				else {
					$scope.secondPhoneMandatory = false;
				}
			};

			$scope.setThirdPhonemandatory = function () {
				if (!($scope.PhoneDetails[2].CountryCode) ||
					($scope.PhoneDetails[2].Number && $scope.PhoneDetails[2].Number !== '')) {
					$scope.thirdPhoneMandatory = true;
				}
				else {
					$scope.thirdPhoneMandatory = false;
				}
			};

			$scope.$watch('PhoneDetails[2].CountryCode', function (newValue) {
				var minAndMaxPhoneLength = $scope.getMaxAndMinPhoneLength(newValue);
				$scope.phoneThirdMaxLength = minAndMaxPhoneLength.maxLength;
				$scope.phoneThirdMinLength = minAndMaxPhoneLength.minLength;
				$scope.setThirdPhonemandatory();
			});

			$scope.$watch('PhoneDetails[2].Number', function (/* newValue */) {
				$scope.setThirdPhonemandatory();
			});

			$scope.$on('validatePhone', function () {
				$scope.contactInfoSubmit();
			});

			$scope.contactInfoSubmit = function () {
				if (($scope.PhoneDetails[0].CountryCode !== '' && $scope.PhoneDetails[0].Number != null && $scope.PhoneDetails[0].Number.length > 0) ||
					($scope.PhoneDetails[1].CountryCode !== '' && $scope.PhoneDetails[1].Number != null && $scope.PhoneDetails[1].Number.length > 0) ||
					($scope.PhoneDetails[2].CountryCode !== '' && $scope.PhoneDetails[2].Number != null && $scope.PhoneDetails[2].Number.length > 0)) {
					$scope.$parent.ReflectPhoneRequired(false);
				} else {
					$scope.$parent.ReflectPhoneRequired(true);
				}
			};


		};


		memberPhoneInfoController.$inject = ['$scope'];

		return {
			restrict: 'A',
			scope: true,
			controller: memberPhoneInfoController
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module;
	try {
		module = angular.module('haMemberPhoneInfoModule');
	} catch (e) {
		module = angular.module('haMemberPhoneInfoModule', ['haUtilsModule', 'haGeoDataModule']);
	}

	module.directive('haMemberPhoneInfo2', [
		'$timeout',
		'haGlobals',
		'haUtils',
		'haGeoDataSvc',

		function ($timeout, haGlobals, haUtils, geo) {

			var link = function ($scope, $el, $attrs) {
				var MAX_PHONES = 3;

				$scope.hideAddPhoneLink = false;

				var defaultCountryCode;
				haGlobals('defaultPhoneCountryCode',
					function (defaultPhoneCountryCode) {
						defaultCountryCode = defaultPhoneCountryCode;
					});

				angular.extend($scope, {
					phoneRegex: geo.getPhoneNumberRegex,
					countryCodeData: geo.getPhoneCountryCodes()
				});

				haUtils.attachPartialVM($scope, $attrs,
					'PhoneDetailVM',
					{},
					function (VM) {
						VM.PhoneDetails = prevalidateNumbers(VM.PhoneDetails);
					});

				$scope.addPhoneNumber = function () {
					if ($scope.VM.PhoneDetails.length < MAX_PHONES) {
						$scope.VM.PhoneDetails.push({Type: null, CountryCode: defaultCountryCode, Number: null});
						$timeout(function () {
							$('#phoneType' + ($scope.VM.PhoneDetails.length - 1)).focus();
						}, 0);
					}
				};

				$scope.removePhoneNumber = function ($index) {
					if ($scope.VM.PhoneDetails.length > 1) {
						$scope.VM.PhoneDetails.splice($index, 1);
					}
				};

				$scope.canAddPhones = function () {
					return (($scope.VM != null) &&
					($scope.VM.PhoneDetails != null) &&
					($scope.VM.PhoneDetails.length < MAX_PHONES));
				};


				$scope.update = function ($index) {
					var cc = $scope.VM.PhoneDetails[$index].CountryCode;
					if (cc != null) {
						var country = geo.lookupCountryByCode(cc);
						if (country != null) {
							$scope.VM.PhoneDetails[$index].CountryKey = country.Key;
						}
					}
				};

				$scope.$on('countryChanged', function ($event, cc) {
					defaultCountryCode = cc;
					// change the country code for phone numbers that doesn't have number set yet
					angular.forEach($scope.VM.PhoneDetails, function (phone, index) {
						if (phone.Number === null || phone.Number === '') {
							phone.CountryCode = cc;
							$scope.update(index);
						}
					});
				});

				function prevalidateNumbers($PhoneDetails) {

					var PhoneDetails = [];

					angular.forEach($PhoneDetails, function (phone, $index) {
						// convert Type to string if not string
						if (typeof $PhoneDetails[$index].Type === 'number') {
							$PhoneDetails[$index].Type = $PhoneDetails[$index].Type.toString();
						}

						// setting CountryKey if CountryCode exists
						var cc = $PhoneDetails[$index].CountryCode;
						if (cc != null) {
							var country = geo.lookupCountryByCode(cc);
							if (country != null) {
								$PhoneDetails[$index].CountryKey = country.Key;
							}
						}

						// validating phone number
						// check if valid numbers
						if ($PhoneDetails[$index].Number == null) {
							return;
						}
						// getting country-specific regex
						var phoneRegex = geo.getPhoneNumberRegex($PhoneDetails[$index].CountryKey);

						// if valid,
						// add the valid phone number to the array
						if ($PhoneDetails[$index].Number.match(phoneRegex)) {
							//console.log('VALID', phone);
							PhoneDetails.push($PhoneDetails[$index]);
							// if invalid,
							// do not add the invalid phone number to the array
						}

					});

					// guarantee at least one (empty) phone number
					if (!PhoneDetails.length) {
						var country = geo.lookupCountryByCode(defaultCountryCode);
						if (country != null) {
							PhoneDetails.push({Type: null, CountryCode: defaultCountryCode, CountryKey: country.Key, Number: null});
						} else {
							PhoneDetails.push({Type: null, CountryCode: null, CountryKey: null, Number: null});
						}
					}

					// setting phoneDetails to new valid array
					return PhoneDetails;
				}

			};

			return {
				restrict: 'A',
				scope: true,
				link: link
			};
		}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haMemberPersonalInfoModule', []);

	module.directive('haMemberPersonalinfo', ['haGlobals',
		function (haGlobals) {

			var memberPersonalInfoController = function ($scope, $cookies) {

				haGlobals('jsonMemberPersonalInfo', function (jsonMemberPersonalInfo) {

					function InitCap(inputStr) {
						var loweredStr;
						if (inputStr != null) {
							loweredStr = inputStr.toLowerCase();
							var Rx = /\b([a-z']+)\b/gi;
							loweredStr = loweredStr.replace(Rx, function (w) {
								return w.charAt(0).toUpperCase() + w.substring(1);
							});
						}
						return loweredStr;
					}

					$scope.MemberPersonalInfo = jsonMemberPersonalInfo;
					$scope.MemberPersonalInfo.Suffix = InitCap($scope.MemberPersonalInfo.Suffix);
					if ($scope.MemberPersonalInfo.FirstName === null && $scope.MemberPersonalInfo.LastName === null) {
						$scope.IsReadOnly = false;
					}

					// CS: THIS IS A HACK BECAUSE THE SERVER RETURNS DEFAULT VALUES ("Day, Month") AS THE FIRST ITEM IN THE ARRAY For Day/Month, and not Year, So we remove them For Day/Month ¯\_(ツ)_/¯
					$scope.MemberPersonalInfo.DOBMonthDropDown.shift();
					$scope.MemberPersonalInfo.DOBYearDropDown.shift();

					// If user returns within 24 hours of setting a minor DOB, show tooltip message and disable form.
					$scope.MemberPersonalInfo.isMinor = $scope.restrictMinors && !!$cookies.get('HM_MINOR_REG_RESTRICTED');
				});

			};

			memberPersonalInfoController.$inject = ['$scope', '$cookies'];

			return {
				restrict: 'A',
				scope: true,
				controller: memberPersonalInfoController
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haMemberDobModule', ['haUtilsModule']);

	module.directive('haMemberDob', [
		'$log',
		'$cookies',
		'haFeatureFlags',
		function ($log, $cookies, flags) {

			var ctrl = ['$scope', function ($scope) {

				var minorsRestrictedCookie = 'HM_MINOR_REG_RESTRICTED';

				$scope.getDOB = function () {
					// joining array to get rid of ugly `undefined` string because barf.
					return [$scope.VM.dobMonth, $scope.VM.dobDay, $scope.VM.dobYear].join('/');
				};

				// every time dob values change
				function dobChangeHandler() {
					// detect dob change
					var dobText = $scope.getDOB();
					$scope.MemberPersonalInfo.SelectedDOB = $scope.getDOB();
					// if we're dealing with unique dob
					// wont fire >once on watch trickle
					if (dobText !== $scope.dobText) {
						$scope.dobText = dobText;
					}
					if ($scope.getIsMinor()) {
						$scope.MemberPersonalInfo.isMinor = true;
						if ($scope.restrictMinors && !$scope.setIsMinor) {
							$scope.setIsMinor = true; 
							createMinorRestrictionCookie();
						}
					}
				}
				
				function createMinorRestrictionCookie() {
					if ($scope.restrictMinors && !$cookies.get(minorsRestrictedCookie)) {
						var expiration = new Date();
						expiration.setDate(expiration.getDate() + 1); // 24 hours
						$cookies.put(minorsRestrictedCookie, true, { expires: expiration });
					}
				};

				$scope.$watch('VM.dobDay', function (newValue, oldValue) {
					if (newValue === oldValue) {
						return;
					}
					// update is minor and dob text
					dobChangeHandler();
				});

				$scope.$watch('VM.dobMonth', function (newValue, oldValue) {
					if (newValue === oldValue) {
						return;
					}
					// update is minor and dob text
					dobChangeHandler();
				});

				$scope.$watch('VM.dobYear', function (newValue, oldValue) {
					if (newValue === oldValue) {
						return;
					}
					// update is minor and dob text
					dobChangeHandler();
				});

				var AGE_LIMIT = flags.get('AGE_LIMIT_TO_DISPLAY_GUARDIAN_INFO', 20) * 365.25 * 24 * 60 * 60 * 1000;
				$scope.getIsMinor = function () {
					if ($scope.VM.dobYear && $scope.VM.dobMonth && $scope.VM.dobDay) {
						var day = parseInt($scope.VM.dobDay);
						var month = parseInt($scope.VM.dobMonth) - 1;  // 0-11
						var year = parseInt($scope.VM.dobYear);
						var dob = new Date(year, month, day);
						var since = Date.now() - dob.getTime();
						return (since < AGE_LIMIT);
					}
					return false;
				};


				var init = function () {
					if ($scope.MemberPersonalInfo.DateOfBirth && $scope.MemberPersonalInfo.DateOfBirth !== null && $scope.MemberPersonalInfo.DateOfBirth !== '') {

						var dob = $scope.MemberPersonalInfo.DateOfBirth.split('/');
						$scope.VM = {};
						if (dob && dob.length === 3) {
							$scope.VM.dobMonth = dob[0];
							$scope.VM.dobDay = dob[1];
							$scope.VM.dobYear = dob[2];
						}
						$scope.dobText = $scope.MemberPersonalInfo.DateOfBirth;
						dobChangeHandler();
					}
				};

				// run init() when VM is Ready...True Up form emits this
				$scope.$on('VMReady', function () {
					init();
				});

				// if VM is ready on load, go ahead and run init()
				if ($scope.MemberPersonalInfo) {
					init();
				}


			}];

			return {
				restrict: 'A',
				scope: true,
				controller: ctrl
			};
		}
	]);

})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haMemberSubscriptionsModule', ['haUtilsModule']);

	module.directive('haMemberSubscriptions', ['haGlobals', 'haUtils',
		function (haGlobals, haUtils) {

			var link = function ($scope, $el, $attrs) {
				haUtils.attachPartialVM($scope, $attrs,
					'EmailSubscriptionsVM',
					{LowFaresAndDeals: true, ENewsLetters: false, HMPartners: false});

				haGlobals(['isHMMember'], function (isHMMember) {
					$scope.isHMMember = isHMMember;
				});
			};

			return {
				restrict: 'A',
				scope: true,
				link: link
			};
		}
	]);
})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haContactUsModule', ['haMemberPhoneInfoModule', 'haGeoDataModule']);

	module.directive('haContactUs', ['$rootScope', '$timeout', 'haGlobals', 'haGeoDataSvc', function ($rootScope, $timeout, haGlobals, geo) {

		return {
			restrict: 'A',
			scope: true,
			link: function ($scope) {

				haGlobals('jsonContactUs', function (jsonContactUs) {
					$.extend($scope, jsonContactUs);

					// Convert the date string from the json into a Date object to prepopulate the date input.
					if ($scope.TravelInfoVM.TravelDate) {
						var tempDate = new Date($scope.TravelInfoVM.TravelDate);
						if (!isNaN(tempDate.getTime())) {
							$scope.TravelInfoVM.TravelDate = tempDate;
						}
					}
				});

				haGlobals(['jsonMemberPhoneInfo', 'countryData'], function (jsonMemberPhoneInfo, countryData) {
					$scope.jsonMemberPhoneInfo = jsonMemberPhoneInfo;
					$scope.countries = geo.setCountryData(countryData); // set country data in geo service
				});

				$scope.$$childHead.$watch('NoEmailAddress', function (nv) {
					if (nv) {
						$scope.ContactInfoVM.EmailAddress = '';
					}
				});
				$scope.ReflectPhoneRequired = angular.noop;

				// Workaround for angular ie placeholder shim
				// Clears placeholder elements on submit.
				$scope.clearPlaceholders = function (event) {
					// select elements displaying placeholder
					var inputs = event.target.querySelectorAll('.empty');
					// clear elements
					for (var i = 0; i < inputs.length; i++) {
						inputs[i].value = '';
					}
				};
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haCashBagModule', []);

	module.directive('haCashBag', ['haGlobals', function (haGlobals) {
		var HaCashBagRegistrationController = function ($scope) {

			$scope.Error = '';

			haGlobals('jsonCashBagModel', function (jsonCashBagModel) {

				$scope.model = angular.extend({}, jsonCashBagModel);

                $scope.model.FromLocationCode = jsonCashBagModel.DestinationLocation.Cities[1].Code;
                $scope.model.DestinationLocationCode = jsonCashBagModel.DestinationLocation.Cities[0].Code;

                // cast TripType to a string (for angular to map to select option)...
                // ...and default to roundtrip ('2') if TripType is 0 (initial page load)
                if ( $scope.model.TripType === 0 || $scope.model.TripType === 2 ) {
                    $scope.model.TripType = '2';
                } else {
                    $scope.model.TripType = '1';
                }

                // cast dates strings to real dates (to prefill date range pickers)
                if ( $scope.model.FromBoardingDate ) {
                    $scope.model.FromBoardingDate = moment($scope.model.FromBoardingDate).toDate();
                }

                if ( $scope.model.DestinationBoardingDate ) {
                    $scope.model.DestinationBoardingDate = moment($scope.model.DestinationBoardingDate).toDate();
                }

				if ($scope.model.Passengers === null) {
					$scope.model.Passengers = [];
				}

				if ($scope.model.Passengers.length === 0) {
					$scope.model.Passengers.push({ FirstName: '', LastName: '', MembershipNumber: '' });
				}
			});

			haGlobals('errorMessage', function (errorMessage) {
				if (errorMessage !== '') {
					$scope.Error = errorMessage;
				}
			});

			$scope.addPax = function () {
				$scope.model.Passengers.push({ FirstName: '', LastName: '', MembershipNumber: '' });
			};

			// Watch departure and return cities to determine if they are the same.
			var setLocationValidity = function () {
				var locationsMatch = $scope.model.FromLocationCode === $scope.model.DestinationLocationCode;
				if ($scope.okCashBagForm.FromLocationCode && $scope.okCashBagForm.DestinationLocationCode) {
					$scope.okCashBagForm.FromLocationCode.$setValidity('unique', !locationsMatch);
					$scope.okCashBagForm.DestinationLocationCode.$setValidity('unique', !locationsMatch);
				}
			};
			$scope.$watch('model.FromLocationCode', function () {
				setLocationValidity();
			});
			$scope.$watch('model.DestinationLocationCode', function () {
				setLocationValidity();
			});

            $scope.range_datepicker_config = {
                double_wide: true,
                start: "[name='FromBoardingDatePicker']",
                end: "[name='DestinationBoardingDatePicker']",
                range_start: moment(new Date('2014/8/1')),
                range_end: moment().startOf('day'),
				format: 'YYYY/MM/DD'
            };

			$scope.$watch('model.Passengers[0].MembershipNumber', function () {
				$scope.DuplicateMembershipCheck(0);
			});

			$scope.$watch('model.Passengers[1].MembershipNumber', function () {
				$scope.DuplicateMembershipCheck(1);
			});

			$scope.$watch('model.Passengers[2].MembershipNumber', function () {
				$scope.DuplicateMembershipCheck(2);
			});

			$scope.$watch('model.Passengers[3].MembershipNumber', function () {
				$scope.DuplicateMembershipCheck(3);
			});

			$scope.DuplicateMembershipCheck = function (current) {
				if ($scope.okCashBagForm['Passengers[' + current + '].MembershipNumber'] === undefined) {
					return;
				}
				if ($scope.model.Passengers[current].MembershipNumber) {
					for (var i = 0; i < $scope.model.Passengers.length; i++) {
						if (i === current) {
							continue;
						}
						if ($scope.model.Passengers[current].MembershipNumber === $scope.model.Passengers[i].MembershipNumber) {
							$scope.okCashBagForm['Passengers[' + current + '].MembershipNumber'].$setValidity('duplicate', false);
							return;
						}
					}
					$scope.okCashBagForm['Passengers[' + current + '].MembershipNumber'].$setValidity('duplicate', true);
				} else {
					$scope.okCashBagForm['Passengers[' + current + '].MembershipNumber'].$setValidity('duplicate', true);
				}
			};
		};

		HaCashBagRegistrationController.$inject = ['$scope'];

		var HaCashBagRegistrationLink = function ($scope, elem) {
			setTimeout(function () {
				$scope.theFormName = elem.attr('name');
				$scope.theForm = $scope[$scope.theFormName];
			}, 0);
		};
		return {
			restrict: 'A',
			scope: true,
			controller: HaCashBagRegistrationController,
			link: HaCashBagRegistrationLink
		};
	}]);
})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haChinaRewardsModule', []);

	module.directive('haChinaRewards', ['haGlobals', function (haGlobals) {
		var HaChinaRewardsRegistrationController = function ($scope) {
			$scope.FlightInfoVM = {};
			$scope.TravellerList = [];
			$scope.EmailAddress = '';
			$scope.PhoneNumber = '';
			$scope.ConfirmationCode = '';
			$scope.CityList = [];
			$scope.FlightInfoVM.DepartureCityCode = '';
			$scope.FlightInfoVM.ReturnCityCode = '';
			$scope.FlightInfoVM.DepartureFlightNumber = '';
			$scope.FlightInfoVM.ReturnFlightNumber = '';
			$scope.FlightInfoVM.DepartureDatePicker = '';
			$scope.FlightInfoVM.ReturnDatePicker = '';
			$scope.FlightInfoVM.DepartureDate = '';
			$scope.FlightInfoVM.ReturnDate = '';

            $scope.range_datepicker_config = {
                double_wide: true,
                start: "[name='FlightInfoVM.DepartureDatePicker']",
                end: "[name='FlightInfoVM.ReturnDatePicker']",
                range_start: moment(new Date('2014/9/1')),
                range_end: moment().startOf('day'),
				format: 'YYYY/MM/DD'
            };

			// Fetch form rendering data from the global json model.
			haGlobals('jsonChinaRewardsModel', function (jsonChinaRewardsModel) {

				$.extend($scope, jsonChinaRewardsModel);

				if ($scope.TravellerList === null) {
					$scope.TravellerList = [];
				}

				if ($scope.FlightInfoVM === null) {
					$scope.FlightInfoVM = {
						DepartureCityCode: 'PEK',
						ReturnCityCode: 'HNL'
					};
				}

                // cast TripType to a string (for angular to map to select option)...
                // ...and default to roundtrip ('2') if TripType is 0 (initial page load)
                if ( $scope.TripType === 0 || $scope.TripType === 2 ) {
                    $scope.FlightInfoVM.TripType = '2';
                } else {
                    $scope.FlightInfoVM.TripType = '1';
                }

                // cast dates strings to real dates (to prefill date range pickers)
                if ( $scope.FlightInfoVM.DepartureDate ) {
                    $scope.FlightInfoVM.DepartureDate = moment($scope.FlightInfoVM.DepartureDate).toDate();
                }

                if ( $scope.FlightInfoVM.ReturnDate ) {
                    $scope.FlightInfoVM.ReturnDate = moment($scope.FlightInfoVM.ReturnDate).toDate();
                }

			});

			// Ensure that we have at least one traveler
			if ($scope.TravellerList.length === 0) {
				$scope.TravellerList.push({FirstName: '', LastName: '', MembershipNumber: ''});
			}

			$scope.$on('haFormValidationSuccess', function (currentScope, args) {
				if ($scope.Validated === false && $scope.formClicked === false) {
					args.event.preventDefault();
					$scope.formClicked = true;
					$scope.formScope = args.formScope;
				}
			});

			$scope.setClear = function () {
				$('body, html').animate({scrollTop: $('input[type="text"]:first').offset().top - 120}, 'slow');
				setTimeout(function () {
					$('input[type="text"]:first').focus();
					$scope.TripType = 2;
				}, 300);
			};
			$scope.addPax = function () {
				$scope.TravellerList.push({FirstName: '', LastName: '', MembershipNumber: ''});
			};

			// Watch departure and return cities to determine if they are the same.
			var setLocationValidity = function () {
				var locationsMatch = $scope.FlightInfoVM.DepartureCityCode === $scope.FlightInfoVM.ReturnCityCode;
				if ($scope.ChinaRewardsForm['FlightInfoVM.DepartureCityCode'] && $scope.ChinaRewardsForm['FlightInfoVM.ReturnCityCode']) {
					$scope.ChinaRewardsForm['FlightInfoVM.DepartureCityCode'].$setValidity('unique', !locationsMatch);
					$scope.ChinaRewardsForm['FlightInfoVM.ReturnCityCode'].$setValidity('unique', !locationsMatch);
				}
			};
			$scope.$watch('FlightInfoVM.DepartureCityCode', function () {
				setLocationValidity();
			});
			$scope.$watch('FlightInfoVM.ReturnCityCode', function () {
				setLocationValidity();
			});
		};

		HaChinaRewardsRegistrationController.$inject = ['$scope'];

		var HaChinaRewardsRegistrationLink = function ($scope, elem) {
			setTimeout(function () {
				$scope.theFormName = elem.attr('name');
				$scope.theForm = $scope[$scope.theFormName];
			}, 0);
		};
		return {
			restrict: 'A',
			scope: true,
			controller: HaChinaRewardsRegistrationController,
			link: HaChinaRewardsRegistrationLink
		};
	}]);


})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haSecurityQuestionAnswerModule', ['haUtilsModule']);

	module.directive('haSecurityQuestionAnswer', ['haGlobals', 'haUtils', function (haGlobals, haUtils) {

		var link = function ($scope, $el, $attrs) {
			$scope.securityQAdataSource = [];
			$scope.securityQAdataSourceFirst = [];
			$scope.securityQAdataSourceSecond = [];
			$scope.securityQAdataSourceThird = [];
			$scope.securityQAPlaceHolder = '';

			haGlobals(['securityQuestionDisabled'], function (securityQuestionDisabled) {
				$scope.securityQuestionDisabled = securityQuestionDisabled;
			});

			haUtils.attachPartialVM($scope, $attrs,
				'SecurityQuestionVM',
				{ SecurityQuestionAnswers: [] },
				function (VM) {
					while (3 > VM.SecurityQuestionAnswers.length) {
						VM.SecurityQuestionAnswers.push({ QuestionID: '', Answer: '' });
					}
					if (VM.SecurityQuestionsDropDown != null && VM.SecurityQuestionsDropDown.length !== 0) {
						angular.forEach(VM.SecurityQuestionsDropDown, function (item) {
							if (item.Value === '') {
								$scope.securityQAPlaceHolder = item.Name;
							} else {
								$scope.securityQAdataSource.push({ name: item.Name, value: item.Value });
							}
						});
						$scope.securityQAdataSourceFirst = $scope.securityQAdataSource;
						$scope.securityQAdataSourceSecond = $scope.securityQAdataSource;
						$scope.securityQAdataSourceThird = $scope.securityQAdataSource;
						startWatchers();
					}
					$scope.needExplainMaskString = false;
					for (var i = 0; i < VM.SecurityQuestionAnswers.length; i++) {
						if (VM.SecurityQuestionAnswers[i].Answer && VM.SecurityQuestionAnswers[i].Answer.length > 0) {
							$scope.needExplainMaskString = true;
						}
					}

					//console.log('security Q&A VM:', VM);
				});

			var startWatchers = function () {
				$scope.$watch('VM.SecurityQuestionAnswers[0].QuestionID', function () {
					if (!$scope.question1set) {
						$scope.question1set = true;
					} else {
						$scope.$broadcast('securityQuestionChanged', 'VM.SecurityQuestionAnswers[0].QuestionID');
					}
					if ($scope.VM.SecurityQuestionAnswers != null && $scope.VM.SecurityQuestionAnswers.length > 0) {
						$scope.securityQAdataSourceSecond = getdataSource($scope.securityQAdataSource, [$scope.VM.SecurityQuestionAnswers[0].QuestionID, $scope.VM.SecurityQuestionAnswers[2].QuestionID]);
						$scope.securityQAdataSourceThird = getdataSource($scope.securityQAdataSource, [$scope.VM.SecurityQuestionAnswers[0].QuestionID], $scope.VM.SecurityQuestionAnswers[1].QuestionID);
					}
				});

				$scope.$watch('VM.SecurityQuestionAnswers[1].QuestionID', function () {
					if (!$scope.question2set) {
						$scope.question2set = true;
					} else {
						$scope.$broadcast('securityQuestionChanged', 'VM.SecurityQuestionAnswers[1].QuestionID');
					}
					if ($scope.VM.SecurityQuestionAnswers != null && $scope.VM.SecurityQuestionAnswers.length > 0) {
						$scope.securityQAdataSourceFirst = getdataSource($scope.securityQAdataSource, [$scope.VM.SecurityQuestionAnswers[1].QuestionID, $scope.VM.SecurityQuestionAnswers[2].QuestionID]);
						$scope.securityQAdataSourceThird = getdataSource($scope.securityQAdataSource, [$scope.VM.SecurityQuestionAnswers[0].QuestionID, $scope.VM.SecurityQuestionAnswers[1].QuestionID]);
					}
				});

				$scope.$watch('VM.SecurityQuestionAnswers[2].QuestionID', function () {
					if (!$scope.question3set) {
						$scope.question3set = true;
					} else {
						$scope.$broadcast('securityQuestionChanged', 'VM.SecurityQuestionAnswers[2].QuestionID');
					}
					if ($scope.VM.SecurityQuestionAnswers != null && $scope.VM.SecurityQuestionAnswers.length > 0) {
						$scope.securityQAdataSourceSecond = getdataSource($scope.securityQAdataSource, [$scope.VM.SecurityQuestionAnswers[0].QuestionID, $scope.VM.SecurityQuestionAnswers[2].QuestionID]);
						$scope.securityQAdataSourceFirst = getdataSource($scope.securityQAdataSource, [$scope.VM.SecurityQuestionAnswers[1].QuestionID, $scope.VM.SecurityQuestionAnswers[2].QuestionID]);
					}
				});
			};

			var getdataSource = function (source, filteredCollection) {
				return source.filter(function (item) {
					return (item.value !== filteredCollection[0]) && (item.value !== filteredCollection[1]);
				});
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: link
		};
	}]);

	/*	goes on an input - input is initially masked bc type = password
	 If user focuses the input, it clears it and switches type back to text.
	 If user blurs the input with no edits, it switches back to the masked value. */
	module.directive('typeSwitch', function () {
		return {
			restrict: 'A',
			scope: {
				answer: '=ngModel'
			},
			link: function ($scope, $el, $attrs) {
				//console.log('name', $attrs.name);

				$scope.answerIndex = nameToIndex($attrs.name);
				$scope.receivedMasked = false;

				$scope.$watch('answer', function (newValue) {

					if (!$scope.existingAnswer && newValue && newValue.length) {
						$scope.existingAnswer = newValue;
						// kyle insists that service layer controls masking
						// after edit and submit, answer is returned as plain text
						// so must check for masked value from server
						// which will determine if input type is set to password
						if (newValue === '**********') {
							$scope.receivedMasked = true;
							$el.attr('type', 'password');
						}
					}
				});

				// input should stay cleared on blur if question changes
				$scope.$on('securityQuestionChanged', function (event, data) {
					if (nameToIndex(data) === $scope.answerIndex) {
						$scope.stayCleared = true;
						$el.val('').attr('type', 'text');
						$scope.answer = '';
					}
				});

				$el.on('focus', function () {
					if ($scope.receivedMasked) {
						$el.val('').attr('type', 'text');
						$scope.answer = '';
					}
				});

				$el.on('blur', function () {
					if ($scope.receivedMasked && $el.val().length === 0 && !$scope.stayCleared) {
						$scope.$apply(function () {
							$scope.answer = $scope.existingAnswer;
							$el.val($scope.existingAnswer).attr('type', 'password');
						});
					}
				});

				function nameToIndex(name) {
					return Number(name.match(/\d/g).join(''));
				}
			}
		};
	});

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haPaymentModule', []);

	module.directive('haPayment', ['haGlobals', function (haGlobals) {

		var paymentController = function ($scope) {
			$scope.CCTypes = [];
			$scope.SavedCards = [];
			$scope.CCInfo = {};
			$scope.oldCardType = '';
			$scope.SavePaymentMethod = false;
			$scope.ExpirationMonths = [];
			$scope.ExpirationMonthsDatasource = [];
			$scope.ExpirationYears = [];
			$scope.monthPlaceHolder = '';
			$scope.yearPlaceHolder = '';
			$scope.HideSaveCardInfoForLaterUse = true;

			haGlobals('jsonCCTypes', function (jsonCCTypes) {
				$scope.CCTypes = jsonCCTypes;
			});

			haGlobals('jsonCCInfo', function (jsonCCInfo) {
				$scope.CCInfo = jsonCCInfo.CCInfo;
				$scope.SavePaymentMethod = jsonCCInfo.SavePaymentMethod;
			});

			haGlobals('jsonSavedCards', function (jsonSavedCards) {
				$scope.SavedCards = jsonSavedCards;
			});

			haGlobals('jsonHideSaveCardInfoForLaterUse', function (jsonHideSaveCardInfoForLaterUse) {
				$scope.HideSaveCardInfoForLaterUse = jsonHideSaveCardInfoForLaterUse;
			});

			haGlobals('ccExpirationMonths', function (ccExpirationMonths) {
				angular.forEach(ccExpirationMonths, function (item) {
					if (item.Value !== '') {
						$scope.ExpirationMonths.push({
							name: item.Name,
							value: item.Value
						});
						$scope.ExpirationMonthsDatasource.push({
							name: item.Name,
							value: item.Value
						});
					}
					else {
						$scope.monthPlaceHolder = item.Name;
					}
				});
			});

			haGlobals('ccExpirationYears', function (ccExpirationYear) {
				angular.forEach(ccExpirationYear, function (item) {
					if (item.Value !== '') {
						$scope.ExpirationYears.push({
							name: item.Name,
							value: item.Value
						});
					}
					else {
						$scope.yearPlaceHolder = item.Name;
					}
				});
			});

			$scope.$watch('CCInfo.ExpirationYear', function (newValue) {
				var year = parseInt(newValue, 10);
				var currentYear = new Date().getFullYear();

				if (year === currentYear) {
					var currentMonth = new Date().getMonth() + 1;
					$scope.ExpirationMonths = [];
					angular.forEach($scope.ExpirationMonthsDatasource, function (item) {
						if (parseInt(item.value, 10) >= currentMonth) {
							$scope.ExpirationMonths.push(item);
							if (parseInt($scope.CCInfo.ExpirationMonth) < currentMonth) {
								$scope.CCInfo.ExpirationMonth = '';
							}
						}
					});
				} else {
					$scope.ExpirationMonths = $scope.ExpirationMonthsDatasource;
				}
			});

			function isAmEx() {
				// In case of a saved card, we need to check the CardType in order to determine if it's AmEx.
				return /^3[47][0-9]{13}$/.test($scope.CCInfo.CardNumber) || $scope.CCInfo.CardType === 'AX';
			}

			$scope.ccvLength = function () {
				return isAmEx() ? 4 : 3;
			};

			$scope.GetCardName = function (cardType) {
				if ($scope.CCTypes.length > 0) {
					var item = $($scope.CCTypes).filter(function () {
						return this.Value === cardType;
					}).get(0);
					if (item) {
						return item.Name;
					}
					else {
						return cardType;
					}
				}
				return cardType;
			};

			$scope.truncateCC = function (ccNumber) {
				return ccNumber.substr(ccNumber.length - 4);
			};

			var ccBulletEncoded = '&bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull; ';
			var ccBulletDecoded = $('<div/>').html(ccBulletEncoded).text();

			if ($scope.CCInfo.CardNumber !== null) {
				$scope.CCInfo.CardNumber = ccBulletDecoded + $scope.truncateCC($scope.CCInfo.CardNumber);
			}

			if ($scope.IsBarclayReturn) {
			    if ($scope.CCInfo.ExpirationMonth !== null) {
			        $scope.CCInfo.ExpirationMonth = $.grep($scope.ExpirationMonths, function (a) { return a.value === $scope.CCInfo.ExpirationMonth; })[0];
			    }
			    if ($scope.CCInfo.ExpirationYear !== null) {
			        $scope.CCInfo.ExpirationYear = $.grep($scope.ExpirationYears, function (a) { return a.value === $scope.CCInfo.ExpirationYear; })[0];
			    }
			}

			$scope.cardLength = function () {
				if ($scope.CCInfo.CCId > 0 && $scope.CCInfo.CardNumber.indexOf(ccBulletDecoded) !== -1) {
					return 19;
				}
				else {
					return 16;
				}
			};

			$scope.isUseCard = function () {
				return $scope.CCInfo.CCId > 0 && $scope.CCInfo.CardNumber.indexOf(ccBulletDecoded) !== -1;
			};

			$scope.useCard = function (cardInfo) {
				var newCardInfo = angular.copy(cardInfo);
				$scope.CCInfo = newCardInfo.CCInfo;
				// Find the correct year and month objects in the array used for ng-options
				$scope.CCInfo.ExpirationMonth = $.grep($scope.ExpirationMonths, function(a){ return a.value === $scope.CCInfo.ExpirationMonth;})[0];
				$scope.CCInfo.ExpirationYear = $.grep($scope.ExpirationYears, function(a){ return a.value === $scope.CCInfo.ExpirationYear;})[0];

				$scope.CCInfo.CardNumber = ccBulletDecoded + $scope.truncateCC($scope.CCInfo.CardNumber);
				$scope.oldCardType = $scope.CCInfo.CardType;
				$scope.$broadcast('AddressInfoDetails', {'AddressInfo': newCardInfo.BillingAddress});
				$scope.$broadcast('$closeCustomDropdown');
			};

			$scope.$on('ClearCCInfo', function () {
				$scope.CCInfo.CardNumber = '';
				$scope.CCInfo.CardType = '';
				$scope.CCInfo.ExpirationMonth = '';
				$scope.CCInfo.ExpirationYear = '';
			});

		};

		paymentController.$inject = ['$scope'];

		return {
			restrict: 'A',
			scope: true,
			controller: paymentController
		};

	}]);

	module.directive('haValidateCardType', ['haGlobals', function (haGlobals) {
		return {
			link: function (scope, elem, attrs) {
				var ccInput = elem.find('input');
				var ccInputController = ccInput.controller('ngModel');
				ccInput.on('blur', function () {
					scope.ValidateCardType();
				});

				attrs.$observe('cardType', function (attrValue) {
					if (attrValue) {
						scope.ValidateCardType();
					}
				});

				scope.$watch(ccInput.attr('ng-model'), function () {
					scope.safeApply(function () {
						ccInputController.$setValidity('ValidCardType', true);
					});
				});

				scope.ValidateCardType = function () {
					if (!scope.isValid()) {
						var bulletEncoded = '&bull;';
						var bulletDecoded = $('<div/>').html(bulletEncoded).text();
						if (ccInput.val() && ccInput.val() !== '' && ccInput.val().indexOf(bulletDecoded) === -1 && attrs.cardType && attrs.cardType !== '') {
							haGlobals(attrs.cardSource, function (cardSource) {
								var cardType = $(cardSource).filter(function () {
									return this !== undefined && this.Value.toLowerCase() === attrs.cardType.toLowerCase();
								});

								if (cardType.length) {
									var cardTypeRegex = new RegExp(cardType[0].CCRegex);
									if (!cardTypeRegex.test(elem.find('input').val())) {
										ccInputController.$setValidity('ValidCardType', false);
										elem.removeClass('ng-pristine').addClass('ng-dirty');
									}
									else {
										ccInputController.$setValidity('ValidCardType', true);
									}
								}
								else {
									ccInputController.$setValidity('ValidCardType', true);
								}
							});
						}
						else if (attrs.oldCardType && attrs.oldCardType !== '' && attrs.cardType !== attrs.oldCardType) {
							ccInput.controller('ngModel').$setValidity('ValidCardType', false);
							elem.removeClass('ng-pristine').addClass('ng-dirty');
						}
						else {
							ccInputController.$setValidity('ValidCardType', true);
						}
					}
				};

				scope.safeApply = function (fn) {
					var phase = this.$root.$$phase;
					if (phase === '$apply' || phase === '$digest') {
						if (fn && (typeof (fn) === 'function')) {
							fn();
						}
					} else {
						this.$apply(fn);
					}
				};

				scope.isValid = function () {
					if (ccInputController.$error) {
						var isError = false;
						var obj = ccInputController.$error;
						for (var prop in obj) {
							if (obj.hasOwnProperty(prop)) {
								if (prop !== 'ValidCardType' && obj[prop] === true) {
									isError = true;
									break;
								}
							}
						}
						return isError;
					}
					else {
						return false;
					}
				};
			}
		};
	}]);

	module.directive('ccNumber', function () {
		return {
			require: 'ngModel',
			link: function (scope, element, attrs, ngModel) {
				ngModel.$parsers.push(function (inputValue) {
					return scope.check(inputValue);
				});

				element.on('focus mouseup keyup', function () {
					if (attrs.savedcc === 'true') {
						this.select();
						return false;
					}
				});

				var bulletEncoded = '&bull;';
				var bulletDecoded = $('<div/>').html(bulletEncoded).text();

				scope.check = function (inputValue) {
					if (inputValue === undefined) {
						return '';
					}
					var transformedInput = '';
					if (attrs.savedcc === 'true') {
						var expression = '[^0-9 ' + bulletDecoded + ']';
						var regex = new RegExp(expression, 'g');
						transformedInput = inputValue.replace(regex, '');
					}
					else {
						transformedInput = inputValue.replace(/[^0-9]/g, '');
					}
					if (transformedInput !== inputValue) {
						var el = element[0];
						var start = 0;
						var end = 0;
						var isError = false;
						try {
							start = el.selectionStart;
							end = el.selectionEnd + transformedInput.length - inputValue.length;
						}
						catch (ex) {
							isError = true;
						}

						ngModel.$setViewValue(transformedInput);
						ngModel.$render();

						if (!isError) {
							try {
								el.setSelectionRange(start, end);
							}
							catch (ex) {
							}
						}
					}
					return transformedInput;
				};

				//attrs.$observe('savedcc', function () {
				//    scope.check(ngModel.$viewValue);
				//});
			}
		};
	});

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haPaymentCdeModule', ['haPaymentMethodsAPI']);

	module.directive('haPaymentCde', ['haGlobals', '$window', 'haPaymentMethodsAPI', function (haGlobals, $window, haPaymentMethodsAPI) {

		var paymentCdeController = function ($scope, $attrs, $timeout) {
			$scope.SavedCards = [];
			$scope.CCInfo = {};
			$scope.BillingAddress = {};
			$scope.SavePaymentMethod = false;
			$scope.ExpirationMonths = [];
			$scope.ExpirationYears = [];
			$scope.monthPlaceHolder = '';
			$scope.yearPlaceHolder = '';
			$scope.HideSaveCardInfoForLaterUse = true;
			$scope.isSavedCard = false;
			$scope.ShowCDEFormSubmitFailError = false;

			var formName = $attrs.formName;

			haGlobals('jsonCCInfo', function (jsonCCInfo) {
				$scope.CCInfo = jsonCCInfo.CCInfo;
				$scope.SavePaymentMethod = jsonCCInfo.SavePaymentMethod;
			});

			haGlobals('jsonSavedCards', function (jsonSavedCards) {
				$scope.SavedCards = jsonSavedCards;
			});

			haGlobals(['jsonHideSaveCardInfoForLaterUse', 'isLimitCountry', 'isCustomSubmitFailError'], function (jsonHideSaveCardInfoForLaterUse, isLimitCountry, isCustomSubmitFailError) {
				$scope.HideSaveCardInfoForLaterUse = jsonHideSaveCardInfoForLaterUse;
				$scope.isLimitCountry = isLimitCountry;

				// handle the CDE Submit Fail error if not cutom submit fail error
				if (!isCustomSubmitFailError) {
					if(window.sessionStorage.getItem('showCDESubmitFailError')) {
						window.sessionStorage.removeItem('showCDESubmitFailError');
						$scope.ShowCDEFormSubmitFailError = true;
					}
				}
			});

			haGlobals('ccExpirationMonths', function (ccExpirationMonths) {
				angular.forEach(ccExpirationMonths, function (item) {
					if (item.Value !== '') {
						$scope.ExpirationMonths.push({
							name: item.Name,
							value: item.Value
						});
					}
					else {
						$scope.monthPlaceHolder = item.Name;
					}
				});
			});

			haGlobals('ccExpirationYears', function (ccExpirationYear) {
				angular.forEach(ccExpirationYear, function (item) {
					if (item.Value !== '') {
						$scope.ExpirationYears.push({
							name: item.Name,
							value: item.Value
						});
					}
					else {
						$scope.yearPlaceHolder = item.Name;
					}
				});
			});

			$scs.get('ReviewAndPay').then(function (data) {
				$scope.strings = data;
			});

			//##### for Barclays begin #####
			$scope.truncateCC = function (ccNumber) {
				return ccNumber.substr(ccNumber.length - 4);
			};

			var ccBulletEncoded = '&bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull; ';
			var ccBulletDecoded = $('<div/>').html(ccBulletEncoded).text();

			if ($scope.IsBarclayReturn) {
				if ($scope.CCInfo.CardNumber !== null) {
					$scope.IsBarclaysApplied = true;
					$scope.CCInfo.MaskedCardNumber = ccBulletDecoded + $scope.truncateCC($scope.CCInfo.CardNumber);
				}
				if ($scope.CCInfo.ExpirationMonth !== null) {
					$scope.CCInfo.ExpirationMonth = $.grep($scope.ExpirationMonths, function(a){ return a.value === $scope.CCInfo.ExpirationMonth;})[0];
				}
				if ($scope.CCInfo.ExpirationYear !== null) {
					$scope.CCInfo.ExpirationYear = $.grep($scope.ExpirationYears, function(a){ return a.value === $scope.CCInfo.ExpirationYear;})[0];
				}
			}
			//##### for Barclays end ######

			$scope.useCard = function (cardInfo) {

				$scope.loadingSavedCard = true;
				$scope.loadingSavedCardFailed = false;
				$scope.invalidCountryCard = false;
				$scope.invalidCardType = false;

				haPaymentMethodsAPI.getCDESavedCard(cardInfo.CCInfo.CCId).success(function (result) {

					if (result && result.CCInfo && result.BillingAddress) {

						// if isLimitCountry is true, only allow cards with USA and CAN billing address
						if ($scope.isLimitCountry && result.BillingAddress.Country !== 'USA' && result.BillingAddress.Country !== 'CAN') {
							$scope.loadingSavedCard = false;
							$scope.invalidCountryCard = true;
						} else if ($scope.isLimitCountry && result.CCInfo.CardType === 'TP') {
							// if isLimitCountry is true, don't allow UATP cards
							$scope.loadingSavedCard = false;
							$scope.invalidCardType = true;
						} else {
							$scope.CCInfo = result.CCInfo;
							$scope.CCInfo.MaskedCardNumber = ccBulletDecoded + $scope.truncateCC($scope.CCInfo.CardNumber);
							// Find the correct year and month objects in the array used for ng-options
							$scope.CCInfo.ExpirationMonth = $.grep($scope.ExpirationMonths, function(a){ return a.value === $scope.CCInfo.ExpirationMonth;})[0];
							$scope.CCInfo.ExpirationYear = $.grep($scope.ExpirationYears, function(a){ return a.value === $scope.CCInfo.ExpirationYear;})[0];

							$scope.BillingAddress = result.BillingAddress;

							$timeout(function() {
								$scope.$broadcast('AddressInfoDetails', {'AddressInfo': result.BillingAddress});
							}, 0);

							$scope.loadingSavedCard = false;
							$scope.isSavedCard = true;
						}
					} else {
						$scope.loadingSavedCard = false;
						$scope.loadingSavedCardFailed = true;
					}

				}).error(function () {
					$scope.loadingSavedCard = false;
					$scope.loadingSavedCardFailed = true;
				});

				$scope.$broadcast('$closeCustomDropdown');
			};

			$scope.useDifferentCard = function () {
				$scope.isSavedCard = false;
				$scope.IsBarclaysApplied = false;
				$scope.CCInfo = {
					CCId: 0
				};
				$scope.BillingAddress = {};
			};

			$scope.$on('CDESubmit', function () {

				var carrentalPaytype = document.getElementById("carrental-paytype");

				if (carrentalPaytype) {
					var payBaseFareMiles = document.getElementById("pay-baseFareMiles");
					var payBaseFareDollar = document.getElementById("pay-baseFareDollar");
					var payBaseFareDollarMiles = document.getElementById("pay-baseFareDollarMiles");
					var payType = "dollars";

					if (payBaseFareMiles) {
						payType = payBaseFareMiles.innerText.toLowerCase();
					}
					else if (payBaseFareDollarMiles) {
						payType = payBaseFareDollarMiles.innerText.toLowerCase();
					}
					else if (payBaseFareDollar) {
						payType = payBaseFareDollar.innerText.toLowerCase();
					}

					window.digitalData.rentalCarPayment = {};

					window.digitalData.rentalCarPayment = {
						name: 'rentalCarPayment',
						timestamp: new Date().getTime(),
						paid: payType,
						requestFrom: 'In-Path'
					};
				}

			    if (!$scope.isSavedCard && !$scope.IsBarclaysApplied && !$scope.totalZero) {
					var iframe = document.getElementById('formIFrame');

					// validate local form
					if (!$scope.$eval(formName + '.$valid')) {
						// tell iframe form to validate
						iframe.contentWindow.postMessage('validate', iframeOrigin);

						// manually turn on error displays
						$timeout(function() {
							$scope.$eval(formName + '.$validate()');
						}, 0);

						return false;
					}

					// tell iframe form to submit
					iframe.contentWindow.postMessage('submit', iframeOrigin);
				} else {
					// submit form
					$timeout(function() {
						$('#' + formName).submit();
					}, 0);
				}
			});

			// for communicating with CDE payment form
			$window.addEventListener("message", function ($event) {
				// Do we trust the sender of this message?
				if ($event.origin !== iframeOrigin) {
					// not sent from our site
					return;
				}

				if ($event.data.substring(0, 7) === 'resize:') {
					// resize iframe height
					var list = $event.data.split(':');
					if (list.length === 2) {
						// resize the iframe height
						angular.element('#formIFrame').height(list[1]);
					}
				} else if ($event.data === 'submitted') {
					$scope.$emit('CDEPaymentSubmitted');

				} else if ($event.data === 'error') { // for 17.2, remove when 17.3 rolled out
					$scope.$emit('CDEPaymentSubmitError');

				} else if ($event.data === 'submitFail') {
					// set session storage and refresh the page
					window.sessionStorage.setItem('showCDESubmitFailError', true);
					window.onbeforeunload = function(){ window.scrollTo(0,0); };
					window.location.reload();

				} else if ($event.data.substring(0, 9) === 'complete:') {
					// submit has completed, get the response
					var responseStr = $event.data.substring(9, $event.data.length);
					var response = JSON.parse(responseStr);

					// populate hidden form inputs
					$scope.SavePaymentMethod = response.SavePaymentMethod;

					$scope.CCInfo.FirstName = response.CCInfo.FirstName;
					$scope.CCInfo.LastName = response.CCInfo.LastName;
					$scope.CCInfo.CardNumber = response.CCInfo.CardNumber;
					$scope.CCInfo.CCV = response.CCInfo.Cvv;
					$scope.CCInfo.CardType = response.CCInfo.CardType;
					$scope.CCInfo.ExpirationMonth = response.CCInfo.ExpirationMonth;
					$scope.CCInfo.ExpirationYear = response.CCInfo.ExpirationYear;
					$scope.CCInfo.BinNumber = response.CCInfo.BinNumber;
					$scope.CCInfo.NickName = $scope.SavePaymentMethod ? response.CCInfo.NickName : null;

					$scope.BillingAddress.Country = response.BillingAddress.Country;
					$scope.BillingAddress.Address1 = response.BillingAddress.Address1;
					$scope.BillingAddress.Address2 = response.BillingAddress.Address2;
					$scope.BillingAddress.Address3 = response.BillingAddress.Address3;
					$scope.BillingAddress.City = response.BillingAddress.City;
					$scope.BillingAddress.State = response.BillingAddress.State;
					$scope.BillingAddress.ZipCode = response.BillingAddress.ZipCode;

					// submit form
					$timeout(function() {
						$('#' + formName).submit();
					}, 0);
				} else if ($event.data.substring(0, 14) === 'barclaysCheck:') {
					var responseStr = $event.data.substring(14, $event.data.length);
					var response = JSON.parse(responseStr);

					// call barclays credit approval check
					$scope.$emit('BarclaysCreditpreApprovalCheck', response);

				} else if ($event.data.substring(0, 14) === 'barclaysApply:') {
					var responseStr = $event.data.substring(14, $event.data.length);
					var response = JSON.parse(responseStr);

					// call barclays apply now
					$scope.$emit('BarclaysApplyNow', response);
				}
			});

			// tell the CDE form when the valid status of the local form changes
			$scope.$watch(formName + '.$valid', function (newValue, oldValue) {
				if (oldValue !== undefined && newValue !== oldValue) {
					var iframe = document.getElementById('formIFrame');
					var message = newValue ? 'valid' : 'invalid';
					if (iframe) {
						iframe.contentWindow.postMessage(message, iframeOrigin);
					}
				}
			});

			$scope.$watch('giftCardResponse.BookingBalance', function() {
				$scope.isSavedCard = false;
			});
		};

		paymentCdeController.$inject = ['$scope', '$attrs', '$timeout'];

		var paymentCdeLink = function ($scope, $el) {
			var src = '/PaymentAuthorization?rand=' + (Math.floor(Math.random() * 9999) + 1);
			if ($scope.$switch('Global:EnableVIC') || $scope.$root.vicilicious) {
				src += '&vicilicious=true';
			}
			$($el).find('#formIFrame').prop('src', src);
		};

		return {
			restrict: 'A',
			scope: true,
			link: paymentCdeLink,
			controller: paymentCdeController
		};

	}]);

})(angular);
;
(function (angular) {

	// Ha Cms Rich Text Editor
	// --------------------------------------------
	//
	// * **Class:** HaCmsRichTextEditor
	// * **Author:** Melissa Rota
	//
	// CMS Component for entering HTML rich text

	'use strict';

	var mod = angular.module('haCmsRichTextEditorModule', []);

	mod.directive('haCmsRichTextEditor', ['$compile', function ($compile) {

		var HaCmsRichTextEditorController = function ($scope) {
			$scope.$emit('$haCmsRichTextEditorReady');
		};

		HaCmsRichTextEditorController.$inject = ['$scope'];

		var HaCmsRichTextEditorLink = function ($scope, $element) {

			// NEP: Hack to inject ha-scroll-to into ha-cms-rich-text-editor for internal hrefs
			$element.find('a').each(function (idx, el) {
				var href = $(el).attr('href');
				if ((typeof href === 'string') && (href.indexOf('#') === 0)) {
					$(el).attr('ha-scroll-to', href.slice(1));
					$compile(el)($scope);
				}
			});

			// set border class for two column layout
			var $el = $($element);
			var parentColumn = $el.closest('.column');
			if (parentColumn.length > 0) {
				var twoColumnSublayout = $el.closest('.two-column-sublayout');
				// check if in two col and richTextCol has not been added already
				if (twoColumnSublayout.length > 0 && twoColumnSublayout.find('.rich-text-col').length === 0) {
					twoColumnSublayout.find('.column').removeClass('default-split');
					parentColumn.addClass('rich-text-col');
				}
			}
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCmsRichTextEditorLink,
			controller: HaCmsRichTextEditorController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Advanced Search
	// --------------------------------------------
	//
	// * **Class:** HaAdvancedSearch
	// * **Author:** Michael Toymil
	//
	// Controls the advanced search page.

	'use strict';

	var module = angular.module('haAdvancedSearchModule', []);

	module.directive('haAdvancedSearch', ['haSitecoreStrings', 'haGlobals', '$interpolate', function ($scs, haGlobals, $interpolate) {

		var HaAdvancedSearchController = function ($scope) {

			haGlobals('ETCOResponseModel', function (etcoResponseModel) {
				$scope.ETCOResponseModel = etcoResponseModel;

				$scs.get('ReservationsTravelCreditRedemption.GetOffAllRoutesText').then(function (txt) {
					$scope.ecertTitle = $interpolate(txt)($scope);
				});
			});

			// console.log('ssssstrings!');
			// console.log($scope.strings);

			$scope.enableHotelsWidget = $scope.$switch('BookingWidget:enablehotelwidget');
			$scope.enableCarsWidget = $scope.$switch('BookingWidget:enablecarwidget');
			$scope.enablePackagesWidget = $scope.$switch('BookingWidget:enablepackageswidget');

			$scope.$on('$bookingWidgetReady', function () {
				$('.advWidgetFooter').show();
				$('.initLoader').hide();
			});

			$scope.$emit('$haAdvancedSearchReady');


			$scope.doSubmit = function (/* $event */) {
				// NEP: Awful, I know. It would help if the submit button lived inside the form!
				setTimeout(function () {
					$('form[name="flightSearch"]').trigger('submit');
				}, 0);
			};
		};


		HaAdvancedSearchController.$inject = ['$scope'];

		var HaAdvancedSearchLink = function ($scope /* , $el */) {

			$scope.exampleMethod = function () {
				return $scope;
			};

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaAdvancedSearchLink,
			controller: HaAdvancedSearchController
		};
	}]);
})(angular);
;
(function (angular) {

	// Ha Cms Full Width Promotion
	// --------------------------------------------
	//
	// * **Class:** HaCmsFullWidthPromotion
	// * **Author:** Chad Kumabe
	//
	// Full-Width Promotion for CMS

	'use strict';

	var mod = angular.module('haCmsFullWidthPromotionModule', []);

	mod.directive('haCmsFullWidthPromotion', function () {

		var HaCmsFullWidthPromotionController = function ($scope) {
			$scope.$emit('$haCmsFullWidthPromotionReady');
		};

		HaCmsFullWidthPromotionController.$inject = ['$scope'];

		var HaCmsFullWidthPromotionLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCmsFullWidthPromotionLink,
			controller: HaCmsFullWidthPromotionController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Cms Body Copy Ad
	// --------------------------------------------
	//
	// * **Class:** HaCmsBodyCopyAd
	// * **Author:** Chad Kumabe
	//
	// Body Copy Ad for CMS

	'use strict';

	var mod = angular.module('haCmsBodyCopyAdModule', []);

	mod.directive('haCmsBodyCopyAd', function () {

		var HaCmsBodyCopyAdController = function ($scope) {
			$scope.$emit('$haCmsBodyCopyAdReady');
		};

		HaCmsBodyCopyAdController.$inject = ['$scope'];

		var HaCmsBodyCopyAdLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCmsBodyCopyAdLink,
			controller: HaCmsBodyCopyAdController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Cms Detail Page Banner Headline
	// --------------------------------------------
	//
	// * **Class:** HaCmsDetailPageBannerHeadline
	// * **Author:** Chad Kumabe
	//
	// Detail Page Banner Headline for CMS

	'use strict';

	var mod = angular.module('haCmsDetailPageBannerHeadlineModule', []);

	mod.directive('haCmsDetailPageBannerHeadline', function () {

		var HaCmsDetailPageBannerHeadlineController = function ($scope) {
			$scope.$emit('$haCmsDetailPageBannerHeadlineReady');

		};

		HaCmsDetailPageBannerHeadlineController.$inject = ['$scope'];

		var HaCmsDetailPageBannerHeadlineLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.$on('hacontactusEmailSuccess', function (currentScope, args) {

				if (args.showMessageFlag === 'SUCCESS' || args.showMessageFlag === 'FAIL') {
					$scope.showmessageContentFlag = args.showMessageFlag;
					$scope.showmessageContent = args.showMessage;

				}

			});
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCmsDetailPageBannerHeadlineLink,
			controller: HaCmsDetailPageBannerHeadlineController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Cms Subpage Banner Headline
	// --------------------------------------------
	//
	// * **Class:** HaCmsSubpageBannerHeadline
	// * **Author:** Chad Kumabe
	//
	// Subpage Banner Headline for CMS

	'use strict';

	var mod = angular.module('haCmsSubpageBannerHeadlineModule', [
		'haBreadcrumbModule',
		'shareWidgetModule'
	]);

	mod.directive('haCmsSubpageBannerHeadline', function () {

		var HaCmsSubpageBannerHeadlineController = function ($scope) {
			$scope.$emit('$haCmsSubpageBannerHeadlineReady');
		};

		HaCmsSubpageBannerHeadlineController.$inject = ['$scope'];

		var HaCmsSubpageBannerHeadlineLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCmsSubpageBannerHeadlineLink,
			controller: HaCmsSubpageBannerHeadlineController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Photo Gallery
	// --------------------------------------------
	//
	// * **Class:** HaPhotoGallery
	// * **Author:** Melissa Rota
	//
	// Modal with photo slideshow.

	'use strict';

	var module = angular.module('haPhotoGalleryModule', [
		'haCarouselModule'
	]);

	module.directive('haPhotoGallery', ['haGlobals', function (haGlobals) {

		var HaPhotoGalleryController = function ($scope) {
			$scope.$emit('$haPhotoGalleryReady');
		};

		HaPhotoGalleryController.$inject = ['$scope'];

		var HaPhotoGalleryLink = function ($scope, $el) {
			$scope.showPhotoGallery = function () {
				$scope.isVisible = true;
				setTimeout(setModalSize(), 200);
			};


			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.PhotoGallery = [];
			haGlobals('PhotoGalleryVM', function (PhotoGalleryVM) {
				$scope.PhotoGallery = PhotoGalleryVM;
			});

			$scope.slides = $el.find('.ha-carousel-slide');

			$scope.closeBtnClick = function () {
				$scope.isVisible = false;
			};
			// handle resize and modal cover
			var setModalSize = function () {
				if ($('.ha-photo-gallery .is-mobile').css('display') === 'none') {
					var backdropHeight = $(window).height();
					$('.ha-photo-gallery img').css('max-height', backdropHeight - 100);
					$('.ha-photo-gallery .ha-carousel-slide').css('height', backdropHeight);
					$('.ha-photo-gallery .ha-carousel-slide .main-content .paddle').css('height', backdropHeight);
				}
			};
			$(window).resize(function () {
				setModalSize();
			});
			$scope.showPhotoGallery();
			setModalSize();
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaPhotoGalleryLink,
			controller: HaPhotoGalleryController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Cms Child Nav Sub Page
	// --------------------------------------------
	//
	// * **Class:** HaCmsChildNavSubPage
	// * **Author:** Chad Kumabe
	//
	// Child Navigation Component on a Standard Sub-page

	'use strict';

	var module = angular.module('haCmsChildNavSubPageModule', []);

	module.directive('haCmsChildNavSubPage', function () {

		var HaCmsChildNavSubPageController = function ($scope) {
			$scope.$emit('$haCmsChildNavSubPageReady');

			// if all html rendered by server you can just start here
			// equal to tallest
			var tiles = $('.child-nav-tile');
			var heights = [];
			for (var j = 0; j < tiles.length; j++) {
				var $tile = $(tiles[j]);
				heights.push($tile.height());
			}
			tiles.css('height', Math.max.apply(null, heights));
			// end here
		};

		HaCmsChildNavSubPageController.$inject = ['$scope'];

		var HaCmsChildNavSubPageLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCmsChildNavSubPageLink,
			controller: HaCmsChildNavSubPageController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Cms Child Nav Front Page
	// --------------------------------------------
	//
	// * **Class:** HaCmsChildNavFrontPage
	// * **Author:** Chad Kumabe
	//
	// Child Navigation Component on a Section Front Page

	'use strict';

	var module = angular.module('haCmsChildNavFrontPageModule', []);

	module.directive('haCmsChildNavFrontPage', function () {

		var HaCmsChildNavFrontPageController = function ($scope) {
			$scope.$emit('$haCmsChildNavFrontPageReady');

			// if all html rendered by server you can just start here
			// equal to tallest
			var tiles = $('.child-nav-tile');
			var heights = [];
			for (var j = 0; j < tiles.length; j++) {
				var $tile = $(tiles[j]);
				heights.push($tile.height());
			}
			$('.child-nav-tile').css('height', Math.max.apply(null, heights));
			// end here
		};

		HaCmsChildNavFrontPageController.$inject = ['$scope'];

		var HaCmsChildNavFrontPageLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};


		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCmsChildNavFrontPageLink,
			controller: HaCmsChildNavFrontPageController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Main Image Content Block
	// --------------------------------------------
	//
	// * **Class:** HaMainImageContentBlock
	// * **Author:** Chad Kumabe
	//
	// Main Image Content Block for CMS

	'use strict';

	var module = angular.module('haMainImageContentBlockModule', []);

	module.directive('haMainImageContentBlock', function () {

		var HaMainImageContentBlockController = function ($scope) {
			$scope.$emit('$haMainImageContentBlockReady');
		};

		HaMainImageContentBlockController.$inject = ['$scope'];

		var HaMainImageContentBlockLink = function ($scope, $el) {

			$scope.slides = $el.find('.ha-carousel-slide');

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaMainImageContentBlockLink,
			controller: HaMainImageContentBlockController
		};
	});

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haFeaturedDealsModule', ['haCarouselModule']);

	module.directive('haFeaturedDeals', [ function () {


		var HaFeaturedDealsLink = function ($scope, $el, $attrs) {

			var viewModel = angular.fromJson($attrs.dealsData),
				tileCount = viewModel.DealTiles ? viewModel.DealTiles.length : 99;

			// Labels
			$scope.toLabelText = $attrs.toLabelText;
			$scope.fromLabelText = $attrs.fromLabelText;
			$scope.perPersonLabelText = $attrs.perPersonLabelText;
			$scope.referenceMark = $attrs.referenceMark;
			$scope.defaultColumns = 12 / viewModel.PerPage;

			// Short circuit display in certain cases - set 50% width
			if (tileCount < 3) {
				$scope.defaultColumns = 6;
			}

			$scope.dealType = viewModel.TileSize || 'Small'; // Small or Large - case sensitive
			$scope.deals = viewModel.DealTiles;

			var dealGroups = [];
			var groupIndex = -1;

			for (var i = 0; i < $scope.deals.length; i++) {
				var deal = $scope.deals[i];
				if (i % viewModel.PerPage === 0) {
					dealGroups.push({ group: [] });
					groupIndex++;
				}
				dealGroups[groupIndex].group.push(deal);
			}
			$scope.dealGroups = dealGroups;
			$scope.slides = dealGroups;
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaFeaturedDealsLink
		};
	}]);

	module.directive('haPromoTile', ['$rootScope', '$filter', '$timeout', 'haConfig', function ($rootScope, $filter, $timeout, haConfig) {

		var HaPromoTileLink = function ($scope, $el, $attr) {

			var model = $scope.$eval($attr.model);

			$scope.model = model;

			// Labels/Images
			model.bgImage = { 'background-image': 'url(\'' + model.BackgroundImageURL + '\')' };
			model.iconUrl = { 'background-image': 'url(\'' + model.IconURL + '\')' };
			model.promoType = (model.TripType === 1) ? $scope.oneWayLabelText : $scope.roundTripLabelText;

			// Allow for other types
			$scope.dealType = (model.TripType === 3) ? 'RichText' : $scope.dealType;
			//$scope.dealType = 'RichText';

			model.LowestFare = Math.min(model.MainCabinBasicFare || Number.POSITIVE_INFINITY, model.Fare);

			// Ref Marks/Disclaimers
			$scope.referenceMark = $scope.referenceMark || '*';
			if (!$rootScope.footnotes || !$rootScope.footnotes.numeric || !$rootScope.references) {
				$rootScope.footnotes = { numeric: [] };
				$rootScope.references = {};
			}

			if (!$rootScope.references[model.DisclaimerGuid]) {

				if (model.Disclaimer) {
					$rootScope.references[model.DisclaimerGuid] = $scope.referenceMark + (Object.keys($rootScope.references).length + 1);
					model.DisclaimerReferenceMark = $rootScope.references[model.DisclaimerGuid];

					var isMCBFare = model.LowestFare && model.LowestFare === model.MainCabinBasicFare;
					var lowFareDisclaimer = isMCBFare ? model.MainCabinBasicDisclaimer : model.Disclaimer;
					$rootScope.footnotes.numeric.push({
						id: '*' + ($rootScope.footnotes.numeric.length + 1),
						text: lowFareDisclaimer
					});
				}

			} else {
				model.DisclaimerReferenceMark = $rootScope.references[model.DisclaimerGuid];
			}

			// Navigation
			$scope.navigateTo = function (deal) {

				// Timeout to let the drag events first first
				$timeout(function () {

					if ($scope.isAnimating) {
						return;
					}

					var target;

					if (deal.CallToActionType > 0) {

						// searchDetails=o=LAX;d=HNL;dd=12/26/2014;a=1;c=0;rd=12/29/2014;tt=2
						var departureDate = ';dd=' + $filter('date')(deal.DepartureDate, 'MM/dd/yyyy');
						var returnDate = (+model.TripType === 1) ? ';rd=' + $filter('date')(deal.ReturnDate, 'MM/dd/yyyy') : '';
						var searchString = ['searchDetails=o=', deal.OriginAirportCode, ';d=', deal.DestinationAirportCode, departureDate, returnDate, ';a=1;c=0;tt=', (+deal.TripType === 0) ? '1' : '2'].join('');

						target = '/book/home?' + searchString;
					} else {
						target = deal.CTALinkURL;
					}

					location.href = target;
				}, 10);
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaPromoTileLink,
			templateUrl: haConfig.getTemplateUrl('ha-promo-tile-base-template.html')
		};
	}]);

})(angular);

;
(function (angular) {

	// Ha Search Results
	// --------------------------------------------
	//
	// * **Class:** HaSearchResults
	// * **Author:** Melissa Rota
	//
	// Displays site search results.

	'use strict';

	var module = angular.module('haSearchResultsModule', []);

	module.directive('haSearchResults', ['haGlobals', 'haGlobalHeaderAPI', function (haGlobals, haGlobalHeaderAPI) {

		var HaSearchResultsController = function ($scope) {

			/* globals s_gi, s_account */

			$scope.googleResult = null;
			haGlobals('GoogleSearchResult', function (GoogleSearchResult) {
				$scope.googleResult = GoogleSearchResult;
			});

			$scope.$emit('$haSearchResultsReady');
			$scope.pagecount = 0;
			$scope.totalrecords = 0;
			$scope.pages = [];
			$scope.selectedpage = 1;
			$scope.startPageIndex = 1;
			var pagecount = 0;
			$scope.IsServiceErrors = false;
			$scope.ServiceErrorMessage = '';


			$scope.UpdateAll = function () {
				if ($scope.googleResult != null) {
					if ($scope.googleResult.ErrorMessage) {
						$scope.IsServiceErrors = true;
						$scope.ServiceErrorMessage = $scope.googleResult.ErrorMessage;
					}
					else if ($scope.googleResult.ErrorMessage == null && $scope.googleResult.items != null) {
						$scope.pagesize = +$scope.googleResult.queries.request[0].count;
						$scope.initialSearchText = $scope.googleResult.queries.request[0].searchTerms;
						$scope.lastSearchText = $scope.initialSearchText;
						$scope.totalrecords = $scope.googleResult.queries.request[0].totalResults;
						pagecount = Math.ceil($scope.totalrecords / $scope.pagesize);
						$scope.totalPageCount = pagecount;
						document.body.dispatchEvent(new CustomEvent('SearchResultFired', { 'detail': $scope.totalrecords }));
						if (pagecount < $scope.pagesize) {
							$scope.pagesize = pagecount;
						}
						$scope.pagecount = pagecount;
						if ($scope.pagesize <= $scope.pagecount) {
							$scope.pages.splice(0, 15);
							for (var counter = $scope.startPageIndex; counter <= $scope.pagesize; counter++) {
								$scope.pages.push(counter);
							}
							//reset pagination value.
							$scope.selectedpage = $scope.pages[0];
							if ($scope.selectedpage === 1) {
								$scope.setPrvDisabled = true;
							}
							if ($scope.pages.length <= 1) {
								$scope.setNxtDisabled = true;
							}
						}
						if ($scope.googleResult.items != null) {
							$scope.SearchIndex = $scope.googleResult.queries.request[0];
							// CS: QUERY: JSHint: Confusing plusses
							// $scope.SearchTerms = +$scope.googleResult.queries.request[0].startIndex + +$scope.SearchIndex.count - 1;
							$scope.SearchTerms = parseInt($scope.googleResult.queries.request[0].startIndex, 10) + parseInt($scope.SearchIndex.count, 10) - 1;
							$scope.itemsPerPage = $scope.googleResult.items.length;
						}
					}
				}
			};

			$scope.NextOrPreviousOrNewSearchText = function (newSearchQuery) {
				haGlobalHeaderAPI.searchGoogle(newSearchQuery).success(function (result) {
					if (result.ErrorMessage) {
						$scope.IsServiceErrors = true;
						$scope.ServiceErrorMessage = result.ErrorMessage;
					}
					else if (result) {
						$scope.googleResult = result;
						$scope.SearchIndex = $scope.googleResult.queries.request[0];
						// CS: QUERY: JSHint: Confusing plusses
						// $scope.SearchTerms = +$scope.googleResult.queries.request[0].startIndex + +$scope.SearchIndex.count - 1;
						$scope.SearchTerms = parseInt($scope.googleResult.queries.request[0].startIndex) + parseInt($scope.SearchIndex.count) - 1;
					}
					if (!$scope.$root.isMobile) {
						$('body, html').animate({
							scrollTop: 350
						}, 'slow');
					}
				});
			};

			$scope.NavigateToPage = function (page) {
				if (page > 1) {
					$scope.selectedpage = page;
					var newSearch = $scope.SearchIndex.searchTerms;
					var pageNumber = ($scope.selectedpage - 1) * $scope.itemsPerPage + 1;
					newSearch = newSearch + '&start=' + pageNumber + '&sa=N';
					$scope.googleResult.SearchPhrase = $scope.SearchIndex.searchTerms;
					$scope.NextOrPreviousOrNewSearchText(newSearch);
					$scope.disableNextPagination();
					$scope.disablePreviousPagination();
				} else {
					$scope.selectedpage = page;
					var newSear = $scope.SearchIndex.searchTerms;
					$scope.NextOrPreviousOrNewSearchText(newSear);
				}
			};

			//-Build google search quary
			$scope.nextResult = function () {
				var newSearch = $scope.SearchIndex.searchTerms;
				var pageNumber = $scope.selectedpage * $scope.itemsPerPage + 1;
				newSearch = newSearch + '&start=' + pageNumber + '&sa=N';
				$scope.googleResult.SearchPhrase = $scope.SearchIndex.searchTerms;
				$scope.NextOrPreviousOrNewSearchText(newSearch);
				$scope.disableNextPagination();
			};

			$scope.newPreviousResult = function () {
				var newSearch = $scope.SearchIndex.searchTerms;
				var pageNumber = ($scope.selectedpage - 2) * $scope.itemsPerPage + 1;
				newSearch = newSearch + '&start=' + pageNumber + '&sa=N';
				$scope.googleResult.SearchPhrase = $scope.SearchIndex.searchTerms;
				$scope.NextOrPreviousOrNewSearchText(newSearch);
				$scope.selectedpage--;
				$scope.disablePreviousPagination();
				$scope.disableNextPagination();
			};

			$scope.NavigateByStep = function (oper) {
				var curpage = $scope.selectedpage;
				if (oper === 'next') {
					if (!$scope.setNxtDisabled && $scope.selectedpage < 10) {
						if (curpage >= $scope.pagesize && curpage <= $scope.totalPageCount) {
							$scope.pa = $scope.pages.splice(1, 1);
							$scope.pages.splice(0, 15);

							for (var newCounter = $scope.pa[0]; newCounter <= $scope.pagesize + 1; newCounter++) {
								$scope.pages.push(newCounter);
							}
							$scope.nextResult();
							$scope.pagesize++;
							curpage++;
							$scope.selectedpage = curpage;
							if ($scope.selectedpage > 1) {
								$scope.setPrvDisabled = false;
							}
						} else {
							$scope.nextResult();
							curpage++;
							$scope.selectedpage = curpage;
							if ($scope.selectedpage > 1) {
								$scope.setPrvDisabled = false;
							}
						}
					}
				} else if (oper === 'prev') {
					if (!$scope.setPrvDisabled) {
						if (curpage >= $scope.pagesize) {
							var prve = $scope.pages[0];
							prve--;
							if (prve > 0) {
								$scope.pa = $scope.pages.splice(9, 1);
								$scope.pages.splice(0, 15);
								for (var newCount = prve; newCount <= $scope.pagesize - 1; newCount++) {
									$scope.pages.push(newCount);
								}
								$scope.newPreviousResult();
								$scope.pagesize--;
								curpage--;

								$scope.selectedpage = curpage;
							} else {
								$scope.newPreviousResult();
								curpage--;
								$scope.selectedpage = curpage;
							}
						} else {
							$scope.newPreviousResult();
							curpage--;
							$scope.selectedpage = curpage;
						}
					}
				}
			};
			$scope.searchSubmitBtnClick = function () {
				if ($scope.googleResult.SearchPhrase === $scope.lastSearchText) {
					return false;
				}

				var newSearchText = $scope.googleResult.SearchPhrase;

				if (newSearchText !== '' && newSearchText !== $scope.lastSearchText) {
					$scope.lastSearchText = newSearchText;
					haGlobalHeaderAPI.searchGoogle(newSearchText).success(function (result) {
						if (result.ErrorMessage || result.TranslateServiceError !== '') {
							$scope.IsServiceErrors = true;
							$scope.ServiceErrorMessage = result.ErrorMessage;
							document.body.dispatchEvent(new CustomEvent('SearchResultFired', { 'detail': '0' }));
						}
						else if (result) {
							$scope.googleResult = result;
							$scope.IsServiceErrors = false;
							$scope.setNxtDisabled = false;
							$scope.pagesize = +$scope.googleResult.queries.request[0].count;
							$scope.pagecount = $scope.pagesize;
							$scope.UpdateAll();
							var totalRecords = $scope.googleResult.queries.request[0].totalResults;
							document.body.dispatchEvent(new CustomEvent('SearchResultFired', { 'detail': totalRecords }));
						}
						if (!$scope.$root.isMobile) {
							$('body, html').animate({
								scrollTop: 350
							}, 'slow');
						}
					});
				}
			};
			$scope.UpdateAll();

		};

		HaSearchResultsController.$inject = ['$scope'];

		var HaSearchResultsLink = function ($scope, $el) {

			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.$searchInput = $el.find('.search-field');
			$scope.$searchSubmitBtn = $el.find('.search-submit-btn');
			$scope.lastindex = $el.find('.last-index');
			$scope.firstindex = $el.find('.first-index');


			if ($scope.pages.length <= 1) {
				$scope.setNxtDisabled = true;
				$scope.lastindex.addClass('disabled');
			} else {
				$scope.setNxtDisabled = false;
			}

			$scope.setPrvDisabled = true;
			$scope.firstindex.addClass('disabled');

			$scope.onSearchFieldKeyup = function () {
				if ($scope.googleResult.SearchPhrase === '') {
					$scope.$searchSubmitBtn.addClass('disabled');
				} else {
					$scope.$searchSubmitBtn.removeClass('disabled');
				}
			};
			$scope.disableNextPagination = function () {
				if ($scope.pagecount > $scope.selectedpage) {
					$scope.lastindex.removeClass('disabled');
					$scope.setNxtDisabled = false;
				}
				else if ($scope.pagecount <= $scope.selectedpage + 1) {
					$scope.lastindex.addClass('disabled');
					$scope.setNxtDisabled = true;
				}
				else {
					$scope.setNxtDisabled = false;
				}
			};

			$scope.disablePreviousPagination = function () {
				if ($scope.selectedpage >= 2) {
					if ($scope.selectedpage <= $scope.startPageIndex) {
						$scope.firstindex.addClass('disabled');
						$scope.setPrvDisabled = true;
					}
					else if ($scope.selectedpage >= 2) {
						$scope.firstindex.removeClass('disabled');
						$scope.setPrvDisabled = false;
					}
				}
				else if ($scope.selectedpage <= 1) {
					$scope.firstindex.addClass('disabled');
					$scope.setPrvDisabled = true;
				}
			};
			$scope.activePage = function () {


			};

		};
		return {
			restrict: 'A',
			scope: true,
			link: HaSearchResultsLink,
			controller: HaSearchResultsController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Global Alerts
	// --------------------------------------------
	//
	// * **Class:** HaGlobalAlerts
	// * **Author:** Melissa Rota & Nathan Probst
	//
	// Alerts that can be displayed above the header across pages

	'use strict';

	var module = angular.module('haGlobalAlertsModule', []);

	module.directive('haGlobalAlerts', [
		function () {
			var HaGlobalAlertsController = [
				'$scope',
				'$rootScope',

				function ($scope, $rootScope) {
					$rootScope.haGlobalAlerts = {};

					this.updateHeight = function (clientHeight) {
						$rootScope.haGlobalAlerts.clientHeight = clientHeight;
					};
				}
			];

			return {
				restrict: 'A',
				require: 'haGlobalAlerts',
				controller: HaGlobalAlertsController,
				link: function ($scope, $el, $attrs, ctrl) {
					var el = $el[0];
					ctrl.updateHeight($el.innerHeight());

					var first = true;
					$scope.$watch(function () {
							return $el.innerHeight();
						},
						function (newHeight, oldHeight) {
							if (first || (newHeight !== oldHeight)) {
								first = false;
								ctrl.updateHeight(newHeight);
							}
						});
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Mileage Statement
	// --------------------------------------------
	//
	// * **Class:** HaMileageStatement
	// * **Author:** Curtis Floth
	//
	// Account mileage statement

	'use strict';

	var module = angular.module('haMileageStatementModule', []);

	module.directive('haMileageStatement', [
		'haGlobals', '$filter', 'filterFilter', 'haModal', function (haGlobals, $filter, filterFilter, haModal) {

			var HaMileageStatementController = function ($scope) {

				$scope.MileageActivityList = [];

				$scope.sortcol = 'PostedDate'; // Default sort
				$scope.mobilesort = 'PostedDate-Descending'; // Default sort for mobile
				$scope.reverse = true;
				$scope.sorticon = 'sort--down';
				$scope.pages = [];
				$scope.keyword = {search: '', activityType: 'X', dateType: 540};
				//$scope.selectedpage = 1;

				$scope.pgInfo = {
					currentPage: 1,
					pageNumber: 20,
					maxSize: 10
				};
				var pagecount = 0;

				$scope.stringToDate = function (dateStr) {
					var theDate = new Date(dateStr);
					return theDate;
				};

				$scope.getHelpContent = function () {
					haModal('', {
						id: 'mileage-statement-help',
						backdrop: 'true',
						template: angular.element('.getHelpContent')
					});
				};

				$scope.changePageCount = function () {
					var keysearchFiltered = filterFilter($scope.InitialMileageActivityList, $scope.keyword.search);
					var flightFiltered = $filter('activityFlt')(keysearchFiltered, $scope.keyword.activityType);
					var dateFiltered = $filter('lastDate')(flightFiltered, $scope.keyword.dateType);
					$scope.totalrecords = dateFiltered.length;
				};

				haGlobals(['MileageStatementModelJson', 'milesAbbText'], function (MileageStatementModelJson, milesAbbText) {
					// console.log('mileage statement VM /////////////////// ');
					// console.log(MileageStatementModelJson);

					if (MileageStatementModelJson.MilageActivityDetails != null) {
						$scope.pagesize = MileageStatementModelJson.MileageActivityPerPage;
						//$scope.totalrecords = MileageStatementModelJson.MilageActivityDetails.length;

						pagecount = Math.ceil($scope.totalrecords / $scope.pagesize);
						$scope.pagecount = pagecount;

						for (var counter = 1; counter <= pagecount; counter++) {
							$scope.pages.push(counter);
						}

						$scope.InitialMileageActivityList = MileageStatementModelJson.MilageActivityDetails;
						$scope.changePageCount();
						//console.log($scope.InitialMileageActivityList)
					}

					$scope.AccountInfo = MileageStatementModelJson.AccountInfo;
					$scope.MileageSummary = MileageStatementModelJson.MileageSummary;

					$scope.milesAbbText = milesAbbText;
				});

				$scope.changeSort = function (col) {
					if ($scope.sortcol === col) {
						$scope.reverse = !$scope.reverse;
						$scope.sorticon = $scope.sorticon === 'sort--up' ? 'sort--down' : 'sort--up';
					} else {
						$scope.reverse = true;
						$scope.sorticon = 'sort--down';
					}

					$scope.sortcol = col;
				};

				$scope.$watch('mobilesort', function (nv, ov) {
					if (ov !== undefined && nv !== ov) {
						var selections = nv.split('-');
						if (selections.length === 2) {
							$scope.sortcol = selections[0];
							$scope.reverse = selections[1] === 'Descending';
							$scope.sorticon = $scope.reverse ? 'sort--down' : 'sort--up';
						}
					}
				});

			};

			HaMileageStatementController.$inject = ['$scope'];

			return {
				restrict: 'A',
				scope: true,
				controller: HaMileageStatementController
			};
		}
	])
	.filter('activityFlt', [function () {
		return function (items, type) {
			if (!items) {
				return [];
			}
			var filtered = [];
			angular.forEach(items, function (item) {
				if (item.ActivityFilter && item.ActivityFilter.indexOf(type) !== -1) {
					filtered.push(item);
				}
			});
			return filtered;
		};
	}])
	.filter('lastDate', [function () {
		return function (items, dateStr) {
			if (!items) {
				return [];
			}
			var filtered = [];
			var today = new Date();

			angular.forEach(items, function (item) {
				var itemDate = new Date(item.ActivityDateDisplay);
				var timeDiff = Math.abs(today.getTime() - itemDate.getTime());
				var diff = Math.ceil(timeDiff / (1000 * 3600 * 24));
				if (diff <= +dateStr) {
					filtered.push(item);
				}
			});

			return filtered;
		};
	}])
	.filter('positivePlus', function() {
		return function(str) {
			var int = parseInt(str);
			if (int > 0) {
				return '+' + str;
			}
			return str;
		};
	});


})(angular);
;
(function (angular) {

	// Ha Reshop Selection Modal
	// --------------------------------------------
	//
	// * **Class:** HaReshopSelectionModal
	// * **Author:** Melissa Rota
	//
	// Modal to select passengers and flights to change.

	'use strict';

	var module = angular.module('haReshopSelectionModalModule', []);

	module.directive('haReshopSelectionModal', function () {

		var HaReshopSelectionModalController = function ($scope) {
			$scope.$emit('$haReshopSelectionModalReady');
		};

		HaReshopSelectionModalController.$inject = ['$scope'];

		var HaReshopSelectionModalLink = function ($scope, $el) {

			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.$paxSelect = $el.find('.pax-select');
			$scope.$flightSelect = $el.find('.flight-select');
			$scope.$seeFlightsBtn = $el.find('.see-flights-btn');
			$scope.$continueBtn = $el.find('.continue-btn');

			$scope.selectedPax = [0, 1];
			$scope.selectedLegs = [];

			$scope.closeChangeFlightsBtnClick = function () {
				$el.hide();
			};

			$scope.continueBtnClick = function () {
				$scope.transitionFromPaxToFlights();
			};

			$scope.transitionFromPaxToFlights = function () {
				$scope.$paxSelect.addClass('hide');
				$scope.$flightSelect.addClass('expand');
			};

			$scope.enableContinueBtn = function (enable) {
				if (enable) {
					$scope.$continueBtn.removeClass('disabled');
				}
				else {
					$scope.$continueBtn.addClass('disabled');
				}
			};

			$scope.togglePaxSelect = function (paxIndex) {
				if ($scope.isPaxSelected(paxIndex)) {
					$scope.removePax(paxIndex);
				}
				else {
					$scope.addPax(paxIndex);
				}
				$scope.enableContinueBtn($scope.selectedPax.length > 0);
			};

			$scope.addPax = function (paxIndex) {
				if ($scope.selectedPax.indexOf(paxIndex) === -1) {
					$scope.selectedPax.push(paxIndex);
				}
			};

			$scope.removePax = function (paxIndex) {
				if ($scope.selectedPax.indexOf(paxIndex) !== -1) {
					$scope.selectedPax.splice($scope.selectedPax.indexOf(paxIndex), 1);
				}
			};

			$scope.isPaxSelected = function (paxIndex) {
				return $scope.selectedPax.indexOf(paxIndex) >= 0;
			};

			$scope.showFlightControlsForLeg = function (legIndex) {
				$scope.$currentLegControls = $el.find('.leg-' + legIndex + '-controls');

				if ($scope.isLegSelected(legIndex)) {
					$scope.removeLeg(legIndex);
					$scope.$currentLegControls.removeClass('expand');
				}
				else {
					$scope.addLeg(legIndex);
					$scope.$currentLegControls.addClass('expand');
				}

				$scope.enableSeeFlightsBtn($scope.selectedLegs.length > 0);

			};

			$scope.enableSeeFlightsBtn = function (enable) {
				if (enable) {
					$scope.$seeFlightsBtn.removeClass('disabled');
				}
				else {
					$scope.$seeFlightsBtn.addClass('disabled');
				}
			};

			$scope.addLeg = function (legIndex) {
				if ($scope.selectedLegs.indexOf(legIndex) === -1) {
					$scope.selectedLegs.push(legIndex);
				}
			};

			$scope.removeLeg = function (legIndex) {
				if ($scope.selectedLegs.indexOf(legIndex) !== -1) {
					$scope.selectedLegs.splice($scope.selectedLegs.indexOf(legIndex), 1);
				}
			};

			$scope.isLegSelected = function (legIndex) {
				return $scope.selectedLegs.indexOf(legIndex) >= 0;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaReshopSelectionModalLink,
			controller: HaReshopSelectionModalController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Ec Downgrade Modal
	// --------------------------------------------
	//
	// * **Class:** HaEcDowngradeModal
	// * **Author:** Melissa Rota
	//
	// Modal for downgrading from an extra comfort seat.

	'use strict';

	var module = angular.module('haEcDowngradeModalModule', []);

	module.directive('haEcDowngradeModal', function () {

		var HaEcDowngradeModalController = function ($scope) {
			$scope.$emit('$haEcDowngradeModalReady');
		};

		HaEcDowngradeModalController.$inject = ['$scope'];

		var HaEcDowngradeModalLink = function ($scope, $el) {

			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.closeECDowngradeBtnClick = function () {
				$el.removeClass('is-open');
			};

			$scope.continueDowngradeBtnClick = function () {
				$scope.closeECDowngradeBtnClick();
				$scope.callEditSeatConfirmationService();
			};

			$scope.selectECSeatBtnClick = function () {
				$el.removeClass('is-open');
			};

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaEcDowngradeModalLink,
			controller: HaEcDowngradeModalController
		};
	});

})(angular);
;
(function (angular) {

	// Ha My Trips Itinerary Details
	// --------------------------------------------
	//
	// * **Class:** HaMyTripsItineraryDetails
	// * **Author:** Cory Shaw / Jamie Perkins
	//
	// This is the itinerary details page you get when viewing an itinerary from my trips

	'use strict';

	var passengersList;
	var editTravelerIndex;
	var maxRecordsPerPage = 1;

	var module = angular.module('haMyTripsItineraryDetailsModule', ['haItineraryAPI']);

	module.directive('haMyTripsItineraryDetails', ['$timeout', 'haGlobals', 'haConfig', 'haItineraryAPI', '$http', '$window', 'haUtils', 'haHttpService', '$location', '$anchorScroll', 'haDateUtils', '$document', 'haPassengersService', 'haAirportTimezones',
		function ($timeout, haGlobals, haConfig, haItineraryAPI, $http, $window, haUtils, haHttpService, $location, $anchorScroll, haDateUtils, $document, haPassengersService, haAirportTimezones) {

			// globals
			var ItineraryDetailsJson = $window.ItineraryDetailsJson;

            var HaMyTripsItineraryDetailsController = function ($scope, $pax, $rootScope, haCustomerSelections, haModal) {

                $scope.inFlightEntertainmentInfo = null;
                $scope.planeCodes = {};

                haGlobals('HA', function (ha) {
                    if (ha && ha.SCStrings && ha.SCStrings.entertainment_data) {
                        $scope.inFlightEntertainmentInfo = ha.SCStrings.entertainment_data;
                    }
                });

                haGlobals('PlaneCodes', function (planeCodes) {
                    $scope.planeCodes = planeCodes;
                });

				haGlobals('ItineraryDetailsJson', function (ItineraryDetailsJson) {
					// console.log('itinerary details json /////////////////////');
					//console.log(ItineraryDetailsJson);

					var equipmentUrl = '/planes/configuration';
                    haHttpService.GET(equipmentUrl).success(function (planeList) {

                        var getEquipmentInfoByCode = function (code) {
                            var equipmentList = planeList || [];
                            for (var i = 0, len = equipmentList.length; i < len; i++) {
                                var item = equipmentList[i];
                                if ($.inArray(code, item.codes) > -1) {
                                    return item;
                                }
                            }
                            return undefined;
                        };

                        angular.forEach($scope.VM.Segments, function (segment) {
                            angular.forEach(segment.Flights, function (flight) {
                                var foundInfo = getEquipmentInfoByCode(flight.EquipmentType);
								if (!!foundInfo && !!foundInfo.name) {
									flight.EquipmentType = foundInfo.name;
								}
                            });
                        });
                    });

                    if (PartnerAirlinesContent != null) {
                        $scope.partnerAirlinesContent = PartnerAirlinesContent;
                    }

					if (ItineraryDetailsJson != null) {

						$scope.VM = ItineraryDetailsJson;

						//Tracking MilesMaximazer Offers
						if ($scope.VM && $scope.VM.IsMilesMaximizerEligible) {
							// Custom Event for Analytics
							document.body.dispatchEvent(new CustomEvent("MilesMaximizerOffered"));
						}

						$scope.requestsentcheck = false;
						$scope.RemovalSuccess = false;
						$scope.cancelCurrencyMatch = true;
						$scope.purchaseCurrencyMatch = true;
						$scope.cancelClicked = [];
						$scope.purchaseClicked = [];

						// saved and held trips
						$scope.VM.HoldTripsList = ItineraryDetailsJson.HoldTrip;
						if ($scope.VM.HoldTripsList == null) {
							$scope.VM.HoldTripsList = [];
						}
						for (var i = 0; i < $scope.VM.HoldTripsList.length; i++) {
							$scope.VM.HoldTripsList[i].tripType = 'held';
							$scope.VM.HoldTripsList[i].Removed = false;
						}

						$scope.VM.SavedAndHeldTrips = $scope.VM.HoldTripsList;


						if (ItineraryDetailsJson.MaxRecordsPerPage !== '') {
							maxRecordsPerPage = ItineraryDetailsJson.MaxRecordsPerPage;
						}

						$scope.upcomingTripsLimit = maxRecordsPerPage;
						$scope.savedHeldTripsLimit = maxRecordsPerPage;

						angular.forEach(ItineraryDetailsJson.Tickets, function (itiTicket, index) {
							if (itiTicket.HawaiianMiles != null && itiTicket.HawaiianMiles != '' && ItineraryDetailsJson.Travellers.length > 0
								&& ItineraryDetailsJson.Travellers[index].DisplayName === itiTicket.TravellerName) {
								ItineraryDetailsJson.Travellers[index].HMNumber = itiTicket.HawaiianMiles;
								ItineraryDetailsJson.Travellers[index].IsHMNumberValid = 'true';
							}
						});

						// edit traveler
						$rootScope.Passengers = ItineraryDetailsJson.Travellers;
						$rootScope.HasPaidSeat = ItineraryDetailsJson.HasPaidSeat;
						var proceed = true;
						if (ItineraryDetailsJson.Tickets != null && ItineraryDetailsJson.Tickets.length > 0) {
							angular.forEach(ItineraryDetailsJson.Tickets, function (ticket) {
								if (proceed) {
									if (ticket.ETicket != null && ticket.ETicket.length > 0) {
										angular.forEach(ticket.ETicket, function (eTicket) {
											if (eTicket.Status === 0) {
												$scope.ticketNo = eTicket.Value;
												proceed = false;
											}
										});
									}
								}
							});
						}

						//$scope.ticketNo = (ItineraryDetailsJson.Tickets != null && ItineraryDetailsJson.Tickets.length > 0 ? (ItineraryDetailsJson.Tickets[0].ETicket != null && ItineraryDetailsJson.Tickets[0].ETicket.length > 0 ? (ItineraryDetailsJson.Tickets[0].ETicket[0].Status === 0) ? ItineraryDetailsJson.Tickets[0].ETicket[0].Value : 'NV' : '') : '');
						angular.forEach($rootScope.Passengers, function (pax) {
							if (pax.DOB != null) {
								pax.DOB = moment(pax.DOB).toDate();
							}
						});

						// APH Bookings
						if (ItineraryDetailsJson.VacationPackageHotelDetail.Address != null && ItineraryDetailsJson.VacationPackageHotelDetail.Room.length > 0) {
							$scope.hotel = ItineraryDetailsJson.VacationPackageHotelDetail;
							if ($scope.hotel != null) {
								$scope.VM.ReservationCode = $scope.hotel.ConfirmationNumber;
								$scope.hideChangeSeats = false;
								$scope.hideUpdateTravelerInfo = true;
								$scope.hideViewOrPrintReceipt = true;
								$scope.showCallUsToChange = true;
							}
						}

						// Online Upgrade Eligibility
						$scope.IsOnlineUpgradeEligible = false;
						angular.forEach(ItineraryDetailsJson.Segments, function(segment) {
							angular.forEach(segment.Flights, function(flight) {
								if (flight.IsOnlineUpgradeEligible) {
									$scope.IsOnlineUpgradeEligible = true;
								}
							});
						});

						// check if loggedin Platinum user is in pax
						var loggedinUser = $rootScope.Passengers.filter(function (pax) {
							return pax.IsLoggedinPassenger;
						})[0];
						if (loggedinUser === undefined) {
							$scope.IsOnlineUpgradeEligible = false;
						}
					}
				});

				haGlobals('ItineraryReceiptJson', function (ItineraryReceiptJson) {
					if (ItineraryReceiptJson != null) {
						$scope.VM.ReservationCode = ItineraryReceiptJson.ReservationCode;
					}
                });

                //Check for entertainment PNRs
                if ($scope.inFlightEntertainmentInfo !== null && $scope.inFlightEntertainmentInfo.airplanes !== undefined && $scope.inFlightEntertainmentInfo.airplanes !== '') {
                    for (var code in $scope.planeCodes) {
                        var currentCode = $scope.planeCodes[code];
						var found = $scope.inFlightEntertainmentInfo.airplanes.filter(function (plane) {
							if (typeof plane.codes !== 'undefined') {
								return plane.codes.filter(function (code) {
									return code.value === currentCode;
								}).length > 0;
							}
                        }).length > 0;
                        if (found) {
                            $scope.VM.IsEntertainment = found;
                            break;
                        }
                    }
                }

				// Get upgrade timeframe configuration
				$scs.get('UPGRADE_MODAL').then(function (data) {
					if (data.starthoursbeforedeparture !== undefined && data.endhoursbeforedeparture !== undefined) {
						$scope.upgradeStart = Number(data.starthoursbeforedeparture);
						$scope.upgradeEnd = Number(data.endhoursbeforedeparture);
					}
				});

				$scope.openUpgradeModal = function () {
					$scope.VM.allLegs = Array.prototype.concat.apply([], $scope.VM.Segments.map(function(segment) {
						return segment.Flights;
					}));
					haModal(haConfig.getTemplateUrl('ha-upgrade-modal.html'), {
						id: 'upgrade-modal',
						backdrop: 'true',
						scope: $scope,
						extendScope: {
							trip: $scope.VM,
							parent_view: "itinerary"
						},
						cancel: {
							label: 'Close',
							fn: function () {
								$scope.resetModalData();
							}
						}
					});
				};

				//Get car supplier strings
				$scs.get('Car_Ancillary').then(function (data) {
					$scope.scContent = data;
				});

				$scope.redirectToCarSupplierSite = function () {

					var pid = $scope.scContent.supplierpartnerid; // PID given by supplier ABG
					var profileAddition = true; // Always true - required in order to show the profile section
					var confirmation = ItineraryDetailsJson.lstAncillaryPurchases[0].SupplierConfirmCode;
					var lastName = ItineraryDetailsJson.lstAncillaryPurchases[0].DriverLastName;
					var locale = haUtils.getLocale($scope.$root.$language);

					var queryObject = {
						pid: pid,
						profileAddition: profileAddition,
						locale: locale,
						lastName: lastName,
						confNumber: confirmation
					}

					var formGetUrl = $scope.scContent.supplierloyaltywebsiteurl;

					formGetUrl += haUtils.createQueryString(queryObject);

					window.location.href = formGetUrl;  
				}


				$scope.resetModalData = function () {
					angular.forEach($scope.VM.allLegs, function (leg) {
						leg.selected = false;
						leg.platinum_pax = false;
						leg.optional_pax = "";
					});
				}

				$scope.getDepartDateFromStartDate = function () {
					var startDate = moment(ItineraryDetailsJson.PeriodStartDateRaw).toDate();
					return startDate.getMonth() + 1 + '/' + startDate.getDate() + '/' + startDate.getFullYear();
				};
				$scope.hideChangeSeats = ItineraryDetailsJson.IsScheduleChange || ItineraryDetailsJson.IsMainCabinBasicItinerary || ItineraryDetailsJson.HasUCSegments || !haPassengersService.isAdultInFlight($rootScope.Passengers, $scope.getDepartDateFromStartDate());

				$scope.addToCalendar = function (e) {

					// close dropdown menu
					$scope.$broadcast('$closeCustomDropdown');

					// assemble links and events body
                    $scope.googleCalendarLinks = [];
					var vEvents = [];

					var fileName = 'Flights.ics';
					var fileContents = '';

					$scs.get('UPCOMING_TRIPS').then( function (promiseVal) {

						// collect flights
						angular.forEach($scope.VM.flightAndSeat, function(val, key) {

							haAirportTimezones.getAirportTimeZone(val.flightInfo.DepartureCityCode).then( function(response) {

								// get user's timezone
								var userTimezone = moment.tz.guess();

								// determine flight times and durations
								var departTimeHours = parseInt(val.flightInfo.DepartureTimeMilitaryTime.substring(0, 2));
								var departTimeMinutes = parseInt(val.flightInfo.DepartureTimeMilitaryTime.substring(2, 4));

	                            var arriveTimeHours = parseInt(val.flightInfo.ArrivalTimeMilitaryTime.substring(0, 2));
	                            var arriveTimeMinutes = parseInt(val.flightInfo.ArrivalTimeMilitaryTime.substring(2, 4));

	                            var durationHours, durationMinutes;

	                            if ( val.flightInfo.FlightDuration.length > 4 ) {
	                            	durationHours = parseInt(val.flightInfo.FlightDuration.substring(0, 2));
	                            	durationMinutes = parseInt(val.flightInfo.FlightDuration.substring(4, 6));
	                            } else {
	                            	durationHours = 0;
	                            	durationMinutes = parseInt(val.flightInfo.FlightDuration.substring(1, 3));
	                            }

								// calculate departure and arrival times based on user's current locale
								var departureMoment = moment.tz(val.flightInfo.DepatureDateRaw, response.timezone);
								var arrivalMoment = moment.tz(val.flightInfo.DepatureDateRaw, response.timezone);

								departureMoment.add(departTimeHours, 'hours');
								departureMoment.add(departTimeMinutes, 'minutes');
								var departDate = departureMoment.clone().tz(userTimezone).format('YYYYMMDDTHHmm00');

								arrivalMoment.add(departTimeHours + durationHours, 'hours');
								arrivalMoment.add(departTimeMinutes + durationMinutes, 'minutes');
								var arriveDate = arrivalMoment.clone().tz(userTimezone).format('YYYYMMDDTHHmm00');

								// build details string
								var detailFlightInfo = promiseVal.addtocalendardetailflightinfo + ' ' + moment(val.flightInfo.DepatureDateRaw).format('MM/DD/YYYY') + ' ' + val.flightInfo.FlightNumber + ' ' + val.flightInfo.DepartureCityCode + '\u279E' + val.flightInfo.ArrivalCityCode + ' ' + val.flightInfo.DepartureTime + ' (' + response.timezone + ')' + '.';
								var detailManage = promiseVal.addtocalendardetailmanage + ' ' + window.host + '/manage.';
								var detailContact = promiseVal.addtocalendardetailcontact + ' ' + window.host + '/contact-us.';
								var detailFull = detailFlightInfo + ' ' + detailManage + ' ' + detailContact;

								// create google calendar links
								$scope.googleCalendarLinks[key] = 'https://www.google.com/calendar/render?action=TEMPLATE&text=' + val.flightInfo.DepartureCityCode + '\u279E' + val.flightInfo.ArrivalCityCode +  '&dates=' + departDate + '/' + arriveDate + '&location=' + val.flightInfo.DepartureCity + '&details=' + detailFull;

								// create .ics vevent (for vcalendar body)
								vEvents.push('BEGIN:VEVENT\nDTSTAMP:' + departDate + '\nORGANIZER;CN=Hawaiian Airlines:MAILTO::support@hawaiianairlines.com\nDTSTART:' + departDate + '\nDTEND:' + arriveDate + '\nLOCATION:' + val.flightInfo.DepartureCity + '\nSUMMARY:' + val.flightInfo.DepartureCityCode + '\u279E' + val.flightInfo.ArrivalCityCode + ', ' + val.flightInfo.FlightDuration + '\nDESCRIPTION:' + detailFull + '\nEND:VEVENT\n');
							});

	                    });

					});

					// download ics file using data uri or blob (depending on browser)
					$scope.downloadICS = function() {

						// create .ics file contents
						fileContents = 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Hawaiian Airlines//NONSGML v1.0//EN\n';

						angular.forEach(vEvents, function(val, key) {
							fileContents += val;
						});

						fileContents += 'END:VCALENDAR';

						// set up blob
						var blob = new Blob([fileContents], {type: 'text/calendar'});
						var icsBlobUrl = URL.createObjectURL(blob);

						if (window.navigator && window.navigator.msSaveBlob) {
							// IE won't visit blob urls, but provides its own saving method for blobs
							window.navigator.msSaveBlob(blob, fileName);
						} else {
							// for all other browsers simply create a temp link and visit the data URL
							// (changing window.location doesn't let us specify a filename, and angular's ng-href chokes on data: since the protocol is not whitelisted)
							var tempLink = document.createElement("a");
							tempLink.href = "data:text/calendar;charset=utf-8," + encodeURIComponent(fileContents);
					        tempLink.download = fileName;
							document.body.appendChild(tempLink);
					        tempLink.click();
							tempLink.remove();
						}
					};

					// disable ics feature for IE9 (no data uri support) and chrome iOS (can't download ics files via data uri)
					if ( !$('html').hasClass('lte-ie9') && !navigator.userAgent.match('CriOS') ) {
						$scope.icsSupported = true;
					}

					// display selection modal
					$scope.lang = $rootScope.$language;

					haModal(haConfig.getTemplateUrl('ha-add-to-calendar-modal.cshtml'), {
						id: 'addToCalendarModal',
						backdrop: true,
						modalLock: false,
						scope: $scope
					});

				};

				$scope.print = function () {
					$scope.dismissDropdown();
					// allow page to render before print() is called
					$timeout(function () {
						window.print();
					}, 1000);
				};

				$scope.dismissDropdown = function () {
  						$scope.$broadcast('$closeCustomDropdown');
   					};

				$scope.scrollToAnchor = function (anchor) {
					//console.log('scroll to anchor: '+anchor);
					$location.hash(anchor);
					$anchorScroll();
				};

				// parse hash on page load
				var hash = window.location.hash.substr(2);
				if (hash && hash.length) {
					if (hash === 'print') {
						$scope.print();
					} else {
						$scope.scrollToAnchor(hash);
					}
				}

				$scope.createLegs = function (howMany) {
					haCustomerSelections.createLegs(howMany, false);
				};

				$scope.addLeg = function () {
					$rootScope.selections.legs.push({});
					haCustomerSelections.setLegDate($rootScope.selections.legs.length - 1);
				};

				$scope.updateLegDates = function () {
					haCustomerSelections.updateLegDates();
				};

				$scope.$emit('$haMyTripsItineraryDetailsReady');

				$scope.$on('$haDataLoaded', function () {
					// load passengers into passengers service
					//$pax.add($scope.Trip.Passengers);

					// load selected segments so receipt can access them
					//$scope.selectedSegments = $scope.Trip.Segments;

					$scope.currency = $scope.Currency;
					$scope.isMileagePricing = $scope.isMileagePricing;
					if ($scope.isMileagePricing) {
						$scope.currency = 'MILES';
					}

					$scope.$broadcast('calcTaxes');
				});

				$rootScope.$on('grandTotal', function (e, grandTotal) {
					$scope.grandTotal = grandTotal()[$scope.Currency];
				});

				var modifyPaxModalId = 'my-trips-modify-pax';

				$scope.modifyPax = function () {
					$rootScope.editTravelerSuccess = false;
					haModal('/MyAccount/EditPassengerInfo/EditPassengerInfo', {
						id: modifyPaxModalId
					});
				};

				$(document).ready(function () {
					switch ($scope.VM.ModelFlag) {
						case 'ut':
							$scope.modifyPax(); //for opening Update Traveler Info Popup
							break;
						case 'cf':
							$scope.showReshopModal(); //for opening Change Flight popup
							break;
						case 'cs':
							$scope.showChangeSeatsPage(); //for opening Change Seat page
							break;
					}
				});

				$scope.optionalCancellationEmailAddress = "";
				$scope.confirmedOptionalCancellationEmailAddress = "";
				$scope.optionalEmailAddressMatch = true;

				//self service cancel modal
				$scope.showSscModal = function () {
					$scope.waiverModalBehaviour = 'default';

					haModal(haConfig.getTemplateUrl('ha-ssc-modal.html'), {
						id: 'ha-ssc-modal',
						backdrop: 'true',
						scope: $scope,
						modalLock:true,
						size: 'custom-modal-width'
					});
				};

				$scope.updateOptionalAdditionalEmailAddressField = function () {
					$scope.optionalCancellationEmailAddress = document.getElementById('optionalEmailAddress').value;
				}

				$scope.updateConfirmedOptionalCancellationEmailAddress = function () {
					$scope.confirmedOptionalCancellationEmailAddress = document.getElementById('confirmOptionalEmailAddress').value;
				}

				$scope.optionalEmailAddressesMatch = function () {
					if ($scope.optionalCancellationEmailAddress !== $scope.confirmedOptionalCancellationEmailAddress) {
						$scope.optionalEmailAddressMatch = false;
					} else {
						$scope.optionalEmailAddressMatch = true;
					}
				}

				//self service cancel behaviour
				$scope.submitCancellation = function (pnr, lastName) {
					$scope.submitting = true;
					haItineraryAPI.CancelWaiverPnr(pnr, lastName, $scope.optionalCancellationEmailAddress).success(function (result) {
						if (result && result.success) {
							$scope.waiverModalBehaviour = 'success';
							$scope.submitting = false;
							document.body.dispatchEvent(new CustomEvent('successCancelModal', {
								detail: {
									isCancelled: true
								}
							}));
						} else {
							$scope.waiverModalBehaviour = 'failure';
							$scope.submitting = false;
							document.body.dispatchEvent(new CustomEvent('failureCancelModal', {
								detail: {
									isCancelled: false
								}
							}));
						}
					})
						.error(function () {
							$scope.waiverModalBehaviour = 'failure';
							$scope.submitting = false;
							document.body.dispatchEvent(new CustomEvent('failureCancelModal', {
								detail: {
									isCancelled: false
								}
							}));
						});

				};

				//self service cancel sucessfull redirection
				$scope.postSuccessWaiverCancellation = function(){
					if(isLoggedIn){
						$window.location.href = '/my-account';
					}
					else{
						$window.location.href = '/my-account/my-trips/manage-trip-itinerary';
					}
				};

				// remove hold
				$scope.confirmRemoveHeldTrip = function (index) {
					var modalScope = $rootScope.$new(true, $scope);
					modalScope.RemoveHoldTripIndex = index;
					if ($scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].PNRCurrencyCode !== $scope.VM.CookiesCurrencyCode) {
						$scope.cancelCurrencyMatch = false;
						$scope.cancelClicked[modalScope.RemoveHoldTripIndex] = true;
						$scope.currencyMismatchPNR = $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].ReservationCode;
						$scope.heldCurrency = $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].PNRCurrencyCode;
						$scope.siteCurrency = $scope.VM.CookiesCurrencyCode;
					} else {
						$scope.cancelCurrencyMatch = true;
						modalScope.removeHeldTrip = function () {
							// console.log('root scope remove held trip at index: ' + index);
							var postObject = {
								cancelHoldTripRQ: {
									RecordLocator: $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].ReservationCode,
									LastName: $scope.VM.LastName
								}
							};
							modalScope.RequestingRemoval = true;
							$scope.VM.RemoveRequestSent = false; // for result banner

							haHttpService.POST('/MyAccount/ItineraryDetails/CancelHoldTrip', postObject).success(function (data) {
								// console.log('cancel held trip success');
								// console.log(data);

								$scope.loading = false;
								$scope.VM.RemoveRequestSent = true;
								modalScope.RequestingRemoval = false;
								$scope.RemovalSuccess = data.PNRCancelStatus;
								$scope.holdremovedorig = $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].OriginCityCode;
								$scope.holdremoveddest = $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].DestinationCityCode;
								$scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].Removed = true;
								modalScope.$modalCancel();

							}).error(function () {
								$scope.VM.RemoveRequestSent = true;
								$scope.RemovalSuccess = false;
								modalScope.RequestingRemoval = false;
								modalScope.$modalCancel();
							});
						};
						haModal('/Areas/MyAccount/Views/Renderings/Shared/_Held_Trips.cshtml', {
							id: 'cancel-held-trip',
							backdrop: 'true',
							template: angular.element('.remove-held-booking'),
							scope: modalScope
						});
						$scope.RemovalSuccess = false;
					}
				};

				$scope.checkCurrency = function (index) {
					if ($scope.VM.SavedAndHeldTrips[index].PNRCurrencyCode !== $scope.VM.CookiesCurrencyCode) {
						$scope.purchaseCurrencyMatch = false;
						$scope.purchaseClicked[index] = true;
						$scope.currencyMismatchPNR = $scope.VM.SavedAndHeldTrips[index].ReservationCode;
						$scope.heldCurrency = $scope.VM.SavedAndHeldTrips[index].PNRCurrencyCode;
						$scope.siteCurrency = $scope.VM.CookiesCurrencyCode;
					} else {
						$scope.submitting = true;
						$scope.purchaseCurrencyMatch = true;
						$window.location.href = '/Book/Itinerary/HeldItinerary?enc=1&pnr=' + $scope.VM.SavedAndHeldTrips[index].EncryptedReservationCode + '&lastName=' + $scope.VM.SavedAndHeldTrips[index].EncryptedLastName;
					}
				};

				//isSelectedFlight,IsSelected
				$scope.SearchNewFlights = function (e) {
					e.preventDefault();
					$('.LoadingTextReshop').css('display', 'block');
					//updating pax details
					angular.forEach($scope.ChangeFlightModel.travellerInfo, function (pax, $index) {
						if ($scope.selectedPax.indexOf($index) > -1) {
							pax.IsSelectedPax = true;
						}
						else {
							pax.IsSelectedPax = false;
						}
					});
					//updating flight details
					var currFlight;
					angular.forEach($scope.ChangeFlightModel.ChangeFlight, function (flight, $index) {
						if ($scope.selectedLegs.indexOf($index) > -1) {
							flight.isSelectedFlight = false;

							currFlight = $scope.ChangeFlightModel.ChangeFlight[$index];

							if (currFlight && angular.isDate(currFlight.OriginDateTime)) {
								flight.OriginDate = moment(currFlight.OriginDateTime).format('MM/DD/YYYY');
								flight.isSelectedFlight = true;
							}

						}
					});
					// Calling Flight Search
					haItineraryAPI.UpdatePaxAndFlightDetails($scope.ChangeFlightModel).success(function (results) {
						if (results.IsSuccess) {
							$('.LoadingTextReshop').css('display', 'none');
							$scope.isInvalidSearch = false;
							$scope.isFlightSearchMsg = '';
							angular.element('#changeFlightForm').submit();
						}
						else {
							$scope.isInvalidSearch = true;
							$scope.isFlightSearchMsg = results.Message;
							$('.LoadingTextReshop').css('display', 'none');
						}
					}).error(function () {
					});

				};

				$scope.GetReceiptInfo = function () {

					haItineraryAPI.GetReceiptInfo($scope.ticketNo).then(
						function (results) {
							results = results.data || results;
							if (results.responseBaseModel.IsSuccess) {

								angular.forEach(results.ChangeFlight, function (flight) {
									flight.OriginDateTime = new Date(parseInt(flight.OriginDateTime.match(/\d+/)[0], 10));
									flight.DestinationDateTime = new Date(parseInt(flight.DestinationDateTime.match(/\d+/)[0], 10));
								});

								angular.forEach(results.OldFlightDetails, function (flight) {
									flight.OriginDateTime = new Date(parseInt(flight.OriginDateTime.match(/\d+/)[0], 10));
									flight.DestinationDateTime = new Date(parseInt(flight.DestinationDateTime.match(/\d+/)[0], 10));
								});

								$scope.ChangeFlightModel = results;
								$scope.trips = results.ChangeFlight;
								$scope.tripsOld = results.OldFlightDetails;

								$scope.paxDetails = results.travellerInfo;
								$scope.selectedPax = [];

								//fillinmg selected PAX Array
								angular.forEach(results.travellerInfo, function (pax, index) {
									$scope.selectedPax.push(index);
								});
								$scope.enableContinueBtn($scope.selectedPax.length > 0);

								$scope.Initialize();

								$scope.loading = false;
							}
							else {
								if (results.ErrorMsgDescription != null && results.ErrorMsgDescription.length > 0) {
									$rootScope.errorMsg = results.ErrorMsgDescription;
									$rootScope.hasError = true;
								}
								$scope.closeReshopModal();
							}
						},
						function (results) {
							if (results.ErrorMsgDescription.length > 0) {
								$rootScope.errorMsg = results.ErrorMsgDescription;
								$rootScope.hasError = true;
							}
							$scope.closeReshopModal();
						}
					);

					// Scroll to modal
					$('.ha-modal').animate({ scrollTop: 0 }, 'slow');
					$timeout(function () {
						$rootScope.$apply();
					}, 0);
				};

				$scope.showItineraryHelp = function () {
					haModal('', {
						id: 'itinerary-help',
						backdrop: 'true',
						template: angular.element('.getHelpContent')
					});
				};

				$scope.displayReshopModal = function () {
					$scope.reshopModal = haModal(haConfig.getTemplateUrl('ha-reshop-selection-modal.html'), {
						backdrop: true,
						id: 'reshop-selection',
						scope: $scope,
						cancel: {
							label: 'Close',
							fn: function () {
								$scope.resetReshopModal();
							}
						}
					});
				};

				$scope.Initialize = function () {
					$scope.createLegs($scope.trips.length);
					var legIndex = 0;
					angular.forEach($scope.trips, function () {
						$rootScope.selections.legs[legIndex].origin = $scope.trips[legIndex].OriginCityCode;
						$rootScope.selections.legs[legIndex].originCityData = {};
						$rootScope.selections.legs[legIndex].originCityData.Code = $scope.trips[legIndex].OriginCityCode;

						$rootScope.selections.legs[legIndex].destination = $scope.trips[legIndex].DestinationCityCode;
						$rootScope.selections.legs[legIndex].destinationCityData = {};
						$rootScope.selections.legs[legIndex].destinationCityData.Code = $scope.trips[legIndex].DestinationCityCode;

						var date = new Date($scope.trips[legIndex].OriginDateTime);

						$rootScope.selections.legs[legIndex].date = {
							year: date.getFullYear(),
							month: date.getMonth() + 1,
							day: date.getDate()
						};
						legIndex++;
					});
				};

				$rootScope.editTravelerSuccess = false;

				$rootScope.editPax = function (traveler, index) {
					//console.log('edit pax: '+index);
					$rootScope.editingPax = true;
					$rootScope.hasError = false;
					$rootScope.editTravelerSuccess = false;
					$rootScope.EditTraveler = angular.copy(traveler);
					$rootScope.EditTraveler.index = index;
					$rootScope.EditTraveler.HasHMNumber = (traveler.HMNumber && traveler.HMNumber.length > 0) ? 'Yes' : 'No';
					angular.forEach($rootScope.Passengers, function (val, i) {
						if (i === index) {
							$rootScope.Passengers[i].Editing = true;
						} else {
							$rootScope.Passengers[i].Editing = false;
						}
					});

					editTravelerIndex = index;
					$timeout(function () {
						$rootScope.$apply();
						haModal({
							id: 'update-pax-info',
							backdrop: 'true',
							templateUrl: '/editPaxMobile.html',
							scope: $scope
						});
					}, 0);
				};
				passengersList = $scope.Passengers;

				$rootScope.saveTraveler = function (traveler, $event) {
					$timeout(function () {
						$rootScope.errorMsg = '';
						$rootScope.hasError = false;
						var travelDate = new Date(traveler.TravelDate);
						travelDate = (travelDate.getMonth() + 1) + '/' + travelDate.getDate() + '/' + travelDate.getFullYear();
						traveler.HMNumber = traveler.HasHMNumber === 'Yes' ? traveler.HMNumber : null;
						$rootScope.currentTraveler = angular.copy(traveler);
						$rootScope.currentTraveler.TravelDate = travelDate;

						var formElement = $($event.target);
						var form = formElement.scope()[formElement.attr('name')];
						if (form.$valid) {

							$rootScope.working = true; // spinner, disable fields

							// Update travler info to the back end.
							haItineraryAPI.updatePassengerInfo($scope.VM.ReservationCode, $rootScope.currentTraveler).success(function (results) {
								$scope.$broadcast('$modalCancel', 'update-pax-info');

								if (results.Travellers != null && results.Travellers.length > 0 && results.IsSuccess) {
									//$rootScope.Passengers[$rootScope.EditTraveler.index] = traveler;
									$rootScope.Passengers = results.Travellers;
									angular.forEach($rootScope.Passengers, function (pax,index) {
										if (pax.DOB != null) {
											pax.DOB = new Date(parseInt(pax.DOB.substr(6)));
										}
										pax.TravelDate = traveler.TravelDate;
										if (pax.HMNumber === '' && results.Tickets != null && results.Tickets.length > 0) {
											pax.HMNumber = results.Tickets[index].HawaiianMiles;
											$rootScope.Passengers[index].HMNumber = results.Tickets[index].HawaiianMiles;
											$scope.VM.Tickets[index].HawaiianMiles = results.Tickets[index].HawaiianMiles;
										}
										
									});							

									passengersList = results.Travellers;
									$rootScope.working = false;
									$rootScope.Passengers[$rootScope.EditTraveler.index].Editing = false;
									$rootScope.editingPax = false;
									$rootScope.editTravelerSuccess = true;
									$rootScope.hasError = false;
								}
								else {
									$rootScope.working = false;
									$rootScope.editTravelerSuccess = false;
									if (results.ErrorMsgDescription.length > 0) {
										$rootScope.errorMsg = results.ErrorMsgDescription;
										$rootScope.hasError = true;
									}
								}

							})
							.error(function (results) {
								$rootScope.working = false;
								$rootScope.editTravelerSuccess = false;
								if (results.ErrorMsgDescription.length > 0) {
									$rootScope.errorMsg = results.ErrorMsgDescription;
									$rootScope.hasError = true;
								}
							});
							$('.ha-modal').animate({ scrollTop: 0 }, 'slow');
							$timeout(function () {
								$rootScope.$apply();
							}, 0);


						}
						else {
							$rootScope.working = false;
							$rootScope.editTravelerSuccess = false;
						}
					}, 0);
				};


				$rootScope.cancelEditPax = function () {
					$rootScope.hasError = false;
					$rootScope.editingPax = false;
					$('.ha-modal').animate({ scrollTop: 0 }, 'slow');
				};

				// on modal close, reset modal
				$rootScope.$on('haModalClosed', function (event, data) {
					if (data === modifyPaxModalId) {
						$rootScope.working = false;
						$rootScope.hasError = false;
						$rootScope.editTravelerSuccess = false;
						$rootScope.editingPax = false;
						for (var i = 0; i < $rootScope.Passengers.length; i++) {
							$rootScope.Passengers[i].Editing = false;
						}
					}
				});

				//
				$scope.$on('OnlineUpgradeRqSuccess', function (event, trip) {
					// fetch updated trip
					$scope.isReloading = true;
					var url = '/MyAccount/ItineraryDetails/GetItineraryVM';
					haHttpService.POST(url, {
						pnr: trip.ReservationCode,
						lastName: trip.LastName
					}).then(function (response) {
						if (response.status === 200) {
							// update online upgrade data
							$scope.VM.AreAllSegmentsOnlineUpgraded = response.data.AreAllSegmentsOnlineUpgraded;
							$scope.VM.OnlineUpgradePassengers = response.data.OnlineUpgradePassengers;
							$scope.VM.Segments = response.data.Segments;
						}

						$scope.isReloading = false;
					});
				});

				$scope.showChangeSeatsPage = function () {
					if (haUtils.checkForInfant($scope.VM)) {
						if (haUtils.checkIfTicketedInfant($scope.VM)) {
							$window.location.href = '/my-account/my-trips/select-or-upgrade-seats';
						} else {
							$scope.infantChangeSeatsError = true;
						}
					} else {
						$window.location.href = '/my-account/my-trips/select-or-upgrade-seats';
					}
				};
			};

			HaMyTripsItineraryDetailsController.$inject = ['$scope', 'haItineraryAPI', '$rootScope', 'haCustomerSelections', 'haModal'];

			var HaMyTripsItineraryDetailsLink = function ($scope, $el, $attr) {

				$scope.modalData = JSON.parse($attr.modalData);

				//$scope.$reshopModal = $el.find('.reshop-selection-modal');
				//$scope.$paxSelect = $el.find('.pax-select');
				//$scope.$flightSelect = $el.find('.flight-select');
				//$scope.$seeFlightsBtn = $el.find('.see-flights-btn');
				//$scope.$continueBtn = $el.find('.continue-btn');

				$scope.continueButtonDisabled = true;
				$scope.paxHide = false;
				$scope.flightSelectExpand = false;
				$scope.seeFlightsDisabled = true;

				$scope.selectedPax = [];
				$scope.selectedLegs = [];
				$scope.isInvalidSearch = false;
				$scope.isFlightSearchMsg = '';
				$scope.isChecked = true;
				// Set this when page loads based on VM.isSuccess and check for infant
				$scope.isSuccess = $scope.VM.isSuccess || (haUtils.checkForInfant($scope.VM) && haUtils.checkIfTicketedInfant($scope.VM));


				$scope.showReshopModal = function () {

					// Open the modal pending
					$scope.displayReshopModal();

					$scope.loading = true;

					$timeout(function () {
						haItineraryAPI.IsEligibleForChangeFlight().then(
							function (results) {
								results = results.data || results;
								if (results.IsEligible) {
									if ($scope.VM.IsLiveItineraryExchangeEligible && $scope.VM.LiveItineraryExchangeUrl !=="") {
										//Get SPA URL & Redirect.
										var spaRebookUrl = $scope.VM.LiveItineraryExchangeUrl;
										window.location.href = spaRebookUrl;
									}
									else {
										$scope.GetReceiptInfo();
									}
								}
								else {									
										$scope.Message = results.Message;
										$scope.closeReshopModal();
								}
							},
							function (/* results */) {
								$scope.Message = 'Unable to determine eligibility';
								//console.log("Error Returned From Service");
								$scope.closeReshopModal();
							}
						);
					}, 0);
				};

				$scope.closeReshopModal = function ($event) {
					$scope.$modalCancel($event);
					if ($scope.$modalClose) {
						$scope.$modalClose();
					}
				};

				$scope.resetReshopModal = function () {
					//$scope.$paxSelect.removeClass('hide');
					//$scope.$flightSelect.removeClass('expand');
					$scope.paxHide = false;
					$scope.flightSelectExpand = false;
					$('#changeflight').css('display', 'block');
					$scope.selectedLegs = [];
					$scope.loading = false;
				};

				$scope.closeChangeFlightsBtnClick = function () {
					if ($scope.$modalClose) {
						$scope.$modalClose();
					}
				};

				$scope.hasUnselectedChildPax = false;
				$scope.continueBtnClick = function () {
					if ($scope.continueButtonDisabled) {
						return false;
					}

					var i = 0;
					var hasUnselectedPax = false;
					var hasChild = false;
					$scope.hasUnselectedChildPax = false;
					angular.forEach($scope.paxDetails, function (value) {
						i++;
						if (value.Type === 'CHD') {
							hasChild = true;
						}
						if (!value.IsSelectedPax) {
							hasUnselectedPax = true;
						}
						if (i === $scope.paxDetails.length) {
							if (hasChild && hasUnselectedPax) {
								$scope.hasUnselectedChildPax = true;
							} else {
								$scope.transitionFromPaxToFlights();
							}
						}
					});

				};

				$scope.transitionFromPaxToFlights = function () {
					$scope.paxHide = true;
					$scope.flightSelectExpand = true;
					//$scope.$paxSelect.addClass('hide');
					//$scope.$flightSelect.addClass('expand');
				};

				$scope.enableContinueBtn = function (enable) {
					$scope.continueButtonDisabled = !enable;
				};

				$scope.togglePaxSelect = function (paxIndex, disabledItem) {
					if (!(disabledItem === true || disabledItem === 'true')) {
						if ($scope.isPaxSelected(paxIndex)) {
							$scope.removePax(paxIndex);
						} else {
							$scope.addPax(paxIndex);
						}
						$scope.enableContinueBtn($scope.selectedPax.length > 0);
					}
				};

				$scope.addPax = function (paxIndex) {
					if ($scope.selectedPax.indexOf(paxIndex) === -1) {
						$scope.selectedPax.push(paxIndex);
					}
				};

				$scope.removePax = function (paxIndex) {
					if ($scope.selectedPax.indexOf(paxIndex) !== -1) {
						$scope.selectedPax.splice($scope.selectedPax.indexOf(paxIndex), 1);
					}
				};

				$scope.isPaxSelected = function (paxIndex) {
					return $scope.selectedPax.indexOf(paxIndex) >= 0;
				};

				$scope.showFlightControlsForLeg = function (legIndex, disabledItem) {

					if (!(disabledItem === true || disabledItem === 'true')) {
						$scope.$currentLegControls = $el.find('.leg-' + legIndex + '-controls');

						if ($scope.isLegSelected(legIndex)) {
							$scope.showLeg = undefined;
							$scope.removeLeg(legIndex);
							//$scope.$currentLegControls.removeClass('expand');
							//$scope.$currentLegControls.removeClass('active');
						}
						else {
							$scope.showLeg = legIndex;
							$scope.addLeg(legIndex);
							//$scope.$currentLegControls.addClass('expand');
							//    $scope.$currentLegControls.addClass('active');
						}

						$scope.enableSeeFlightsBtn($scope.selectedLegs.length > 0);
					}
				};

				$scope.enableSeeFlightsBtn = function (enable) {
					if (enable) {
						$scope.seeFlightsDisabled = false;
						//$scope.$seeFlightsBtn.removeClass('disabled');
					}
					else {
						$scope.seeFlightsDisabled = true;
						//$scope.$seeFlightsBtn.addClass('disabled');
					}
				};

				$scope.addLeg = function (legIndex) {
					if ($scope.selectedLegs.indexOf(legIndex) === -1) {
						$scope.selectedLegs.push(legIndex);
					}
				};

				$scope.removeLeg = function (legIndex) {
					if ($scope.selectedLegs.indexOf(legIndex) !== -1) {
						$scope.selectedLegs.splice($scope.selectedLegs.indexOf(legIndex), 1);
					}
				};

				$scope.isLegSelected = function (legIndex) {
					return $scope.selectedLegs.indexOf(legIndex) >= 0;
				};

				$scope.activateWidget = function () {
					$el.removeClass('closed');
					$el.addClass('expanded');
					$timeout(function () {
						$el.addClass('active');

						// This repositions the carouesl to slide 2, which I have to do manually since
						// I'm hiding the carousels until after the widget expands due to performance
						// issues. If the widget ever needs to be dynamic for calendar-count per slide,
						// etc, this will need to be refactored/removed to cover the different use-cases.

						$el.find('[ha-carousel]').each(function () {
							$(this).scope().gotoSlide(2);
						});

					}, 1000);
				};

				/*  ////////////////////////////////////////////////////////
				 CALENDAR - fns pulled and adapted from ha-booking-form.js
				 ////////////////////////////////////////////////////////  */

				$scope.calendarOpen = [];
				angular.forEach($scope.trips, function () {
					$scope.calendarOpen.push(false);
				});
				$scope.currentDateChoice = 'departDate';
				$scope.departDate = '';
				$scope.idx = 0;

				// listen for date input focused
				$scope.$on('dateInputFocused', function (e, currentDateChoice, idx) {
					$scope.idx = idx;
					$scope.calendarOpen[idx] = true;
					wireCalendarCloseEvent();
				});

				// private fn - delay cal close so user can see selection
				function delayCalendarClose(idx) {
					$timeout(function () {
						$scope.calendarOpen[idx] = false;
					}, 500);
				}

				function wireCalendarCloseEvent() {
					$document.off('click.closeCalendar');
					$document.on('click.closeCalendar', function (e) {
						var $targ = $(e.target);

						if ($targ.closest('.originDestinationWrap0').length) {
							return;
						}
						$document.off('click.closeCalendar');
						angular.forEach($scope.calendarOpen, function (val, key) {
							delayCalendarClose(key);
						});
						$scope.$digest();
					});
				}

				// Calendar clicked
				$scope.$on('setDate', function (e, date) {

					$scope.trips[$scope.idx].OriginDateTime = date;
					angular.forEach($scope.calendarOpen, function (val, key) {
						delayCalendarClose(key);
					});
					$scope.$digest();
				});

				// Check unavailable
				var now = new Date();
				var threeThirtyOneDaysAhead = new Date(new Date().setDate(now.getDate() + 331));
				var lastMonth = threeThirtyOneDaysAhead.getMonth();
				var currentMonth = now.getMonth();

				$scope.monthsToShow = 12 - currentMonth + lastMonth;

				$scope.getIsUnavailable = function (date) {
					return (haDateUtils.isBefore(date, now) || haDateUtils.isAfter(date, threeThirtyOneDaysAhead));
				};

				/*  ////////////////////////////////////////////////////////
				 END CALENDAR
				 ////////////////////////////////////////////////////////  */

				$scope.isShowRightUpsell = $scope.VM.lstAncillaryUpSellsRight && $scope.VM.lstAncillaryUpSellsRight.length > 0 && !$scope.VM.IsGroupPNR;
			};

			return {
				restrict: 'A',
				scope: true,
				link: HaMyTripsItineraryDetailsLink,
				controller: HaMyTripsItineraryDetailsController
			};
		}]);

	//Known traveler no duplicate check validation
	module.directive('validTravelerNo', function () {
		return {
			require: 'ngModel',
			link: function (scope, elm, attrs, ctrl) {
				scope.$watch(attrs.ngModel, function (travelerNo) {
					ctrl.$setValidity('duplicateTravelerNo', true);
					scope.isValidTravelerNo = false;
					scope.checkDuplicateTravelerNo(travelerNo);
					ctrl.$setValidity('duplicateTravelerNo', !scope.isValidTravelerNo);
				});
				scope.checkDuplicateTravelerNo = function (travelerNo) {
					angular.forEach(passengersList, function (passenger) {
						if (passenger.KnownTravelerNumber && passengersList[editTravelerIndex].KnownTravelerNumber !== passenger.KnownTravelerNumber && travelerNo === passenger.KnownTravelerNumber) {
							scope.isValidTravelerNo = true;
						}
					});
				};
			}
		};
	});

	//Validation for HMAccount number availability in HM db.
	module.directive('validHmNumber', ['haItineraryAPI', function (haItineraryAPI) {
		return {
			require: 'ngModel',
			link: function (scope, elm, attrs, ctrl) {
				scope.$watch(attrs.ngModel, function (hmNumber) {
					ctrl.$setValidity('hmNumberNotFound', true);
					var isBlank = hmNumber === '';
					var isValidLength = !isBlank && (hmNumber != null && hmNumber.length === 9);
					var invalidChars = !isBlank && !/^\d+$/.test(hmNumber);
					if (!isBlank && isValidLength && !invalidChars) {
						haItineraryAPI.isValidHMAccount(hmNumber, passengersList[editTravelerIndex].FirstName, passengersList[editTravelerIndex].LastName).success(function (results) {
							if (results != null && results === 'false') {
								ctrl.$setValidity('hmNumberNotFound', false);
							}
							else {
								ctrl.$setValidity('hmNumberNotFound', true);
							}
						})
						.error(function (/* results */) {
							ctrl.$setValidity('hmNumberNotFound', false);
						});
					}
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Print Confirmation
	// --------------------------------------------
	//
	// * **Class:** HaPrintConfirmation
	// * **Author:** Cory Shaw
	//
	// This is the print confirmation page you get when you want to print your itinerary post-purchase

	'use strict';

	var module = angular.module('haPrintConfirmationModule', []);

	module.directive('haPrintConfirmation', function () {

		var HaPrintConfirmationController = function ($scope, $pax, $rootScope) {
			$scope.$emit('$haPrintConfirmationReady');

			$scope.$on('$haDataLoaded', function () {
				// load passengers into passengers service
				$pax.add($scope.Passengers);

				// load selected segments so receipt can access them
				$scope.selectedSegments = $scope.Trips[0].Segments;

				$scope.currency = $scope.Currency;
				if ($scope.isMileagePricing) {
					$scope.currency = 'MILES';
				}

				$scope.$broadcast('calcTaxes');
			});

			$rootScope.$on('grandTotal', function (e, grandTotal) {
				$scope.grandTotal = grandTotal;
			});

			$scope.taxDetails = true;
		};

		HaPrintConfirmationController.$inject = ['$scope', 'haPassengersService', '$rootScope'];

		var HaPrintConfirmationLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaPrintConfirmationLink,
			controller: HaPrintConfirmationController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Account Landing
	// --------------------------------------------
	//
	// * **Class:** HaAccountLanding
	// * **Author:** Curtis Floth
	//
	// HawaiianMiles Account Landing Page

	'use strict';

	var module = angular.module('haAccountLandingModule', []);

	module.directive('haAccountLanding', function () {

		var HaAccountLandingController = function ($scope) {
			$scope.$emit('$haAccountLandingReady');
		};

		HaAccountLandingController.$inject = ['$scope'];

		var HaAccountLandingLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaAccountLandingLink,
			controller: HaAccountLandingController
		};
	});

})(angular);
;
(function (angular) {

	// Travel Credit Redemption
	// --------------------------------------------
	//
	// * **Class:** TravelCreditRedemption
	// * **Author:** Melissa Rota
	//
	// Form to redeem travel credit

	'use strict';

	var module = angular.module('travelCreditRedemptionModule', ['haEcertAPI']);

	module.directive('travelCreditRedemption', ['$location', 'haEcertAPI', 'haGlobals', 'haConfig', 'haModal', function ($location, haEcertAPI, haGlobals, haConfig, haModal) {

		var TravelCreditRedemptionController = function ($scope) {

			haGlobals(['monthDropDown', 'yearDropDown', 'dayDropDown'], function (monthDropDown, yearDropDown) {
				$scope.monthDropDown = monthDropDown;
				$scope.yearDropDown = yearDropDown;
			});

			$scope.dayDropDown = (function (a, b) {
				while (a--) {
					b[a] = (a + 1).toString();
				}
				return b;
			})(31, []);
			$scope.yearDropDown = (function (a, b) {
				while (a--) {
					b[a] = (a + (new Date()).getFullYear() - 2).toString();
				}
				return b;
			})(3, []);

			$scope.$emit('$travelCreditRedemptionReady');
			$scope.travelCreditModel = {};
			$scope.isLoading = false;

			var urlVars = {};
			var hashes = $location.absUrl().slice($location.absUrl().indexOf('?') + 1).split('&');
			angular.forEach(hashes, function (hash) {
				var hashArray = hash.split('=');
				if (hashArray[0] && hashArray[1]) {
					urlVars[hashArray[0]] = hashArray[1];
				}
			});
			if (!!urlVars.invalid) {
				$scope.hasError = true;
				$scope.errorCode = 101;
			}

			$scope.submitClicked = function (e, form) {
				$scope.hasError = false;
				$scope.errorCode = null;
				e.preventDefault();
				if (form.$valid) {
					$scope.isLoading = true;
					haEcertAPI.getPassengerCredit($scope.travelCreditModel).success(function (response) {
						$scope.isLoading = false;
						if (response.ETCOStatus === 6) {
							window.location.href = '/Book/Home';
						} else if (response.ETCOStatus === 8) {
							// initialize data
							$scope.pnrModel = {};
							$scope.pnrList = angular.copy(response.Passengers);

							haModal(haConfig.getTemplateUrl('duplicate-PNR-modal.html'), {
								id: 'duplicate-PNR-modal',
								backdrop: 'true',
								scope: $scope
							});
						} else {
							$scope.hasError = true;
							$scope.errorCode = response.ETCOStatus;
						}

						// Handle special error codes as well...
						//if (response.ErrorCode !== null) {
						if (response.HasServiceError === true) {
							$scope.hasError = true;
							$scope.errorCode = 100;
							$scope.errorMessage = response.ErrorCode;
						}

					}).error(function () {
						$scope.isLoading = false;
						$scope.hasError = true;
					});
				}
			};

			$scope.selectPassenger = function () {
				$scope.isLoading = true;

				haEcertAPI.selectDuplicatePassenger($scope.pnrModel.pnr).success(function (response) {
					if (response.IsValid) {
						window.location.href = '/Book/Home';
					} else {
						$scope.isLoading = false;
						$scope.hasError = true;
						$scope.errorCode = 'custom';
						$scope.errorMessage = response.ErrorMessage;
						$scope.$modalCancel();
					}
				}).error(function () {
					$scope.isLoading = false;
				});
			};

			$scope.showResultsHelp = function () {
				haEcertAPI.fetchPopup('GetHelpPopup').success(function (content) {
					//if ($scope.handleExceptions(content, 'haEcertAPI-showResultsHelp')) {
					//    return;
					//}
					haModal({
						id: 'results-help',
						backdrop: 'true',
						template: content
					});
				});
			};


		};

		TravelCreditRedemptionController.$inject = ['$scope'];

		return {
			restrict: 'A',
			scope: true,
			controller: TravelCreditRedemptionController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Flight Status
	// --------------------------------------------
	//
	// * **Class:** HaFlightStatus
	// * **Author:** Melissa Rota
	//
	// Check flight status by route or flight number.

	'use strict';

	var module = angular.module('haFlightStatusModule', []);

	module.directive('haFlightStatus', ['haGlobals', '$window', function (haGlobals, $window) {

		// globals
		var serverShortDate = $window.serverShortDate;

		var HaFlightStatusController = function ($scope, haCustomerSelections) {
			haGlobals('jsonFSModel', function (jsonFSModel) {
				$scope.FSByFlightNoModel = jsonFSModel;
			});
			if (typeof (Storage) !== 'undefined') {
				// Code for localStorage/sessionStorage.
				try {
					sessionStorage.flightStatus = true;
				}
				catch (err) {
				}
			}
			$scope.createLegs = function (howMany) {
				haCustomerSelections.createLegs(howMany, false);
			};

			//$scope.$emit('$haFlightStatusReady');

			//$scope.depart = 'today';
			//Flight Search by results by flight number
			setTimeout(function () {
				angular.element('#DepartureDate').click();

			}, 1000);


			$scope.FlightDelayStatus = {
				latedeparture: 'Late Departure',
				latearrival: 'Late Arrival',
				delayed: 'Delayed',
				cancelled: 'Cancelled'
			};

			$scope.FlightOnTimeStatus = {
				ontime: 'on Time',
				earlyarrival: 'Early Arrival'
			};

			$scope.departureDateToday = (typeof serverShortDate !== 'undefined') ? new Date(serverShortDate) : new Date();
			$scope.departureDateTomorrow = (typeof serverShortDate !== 'undefined') ? new Date(serverShortDate) : new Date();
			$scope.departureDateYesterday = (typeof serverShortDate !== 'undefined') ? new Date(serverShortDate) : new Date();
			$scope.departureDateTomorrow.setDate($scope.departureDateToday.getDate() + 1);
			$scope.departureDateYesterday.setDate($scope.departureDateToday.getDate() - 1);

			var date = new Date();
			$scope.DepartureDate = [date.getMonth() + 1, date.getDate(), date.getFullYear()].join('/');

			haGlobals('jsonFlightResultModel', function (jsonFlightResultModel) {
				$scope.OperatedBy = jsonFlightResultModel.OperatedBy;
				$scope.FlightNo = jsonFlightResultModel.FlightNumber;
				$scope.Origin = jsonFlightResultModel.Origin;
				$scope.Destination = jsonFlightResultModel.Destination;
				$scope.FlightStatus = jsonFlightResultModel.FlightStatus;
				$scope.AircraftType = jsonFlightResultModel.AircraftType;

				$scope.EquipmentType = jsonFlightResultModel.EquipmentType;

				if (jsonFlightResultModel.FlightStatus === 'Delayed') {
					$scope.FlightStatusStyle = 'delayed';
				} else if (
					jsonFlightResultModel.FlightStatus === 'Arrived' ||
					jsonFlightResultModel.FlightStatus === 'Landed') {
					$scope.FlightStatusStyle = 'arrived';
				} else if (jsonFlightResultModel.FlightStatus === 'Scheduled' ||
					jsonFlightResultModel.FlightStatus === 'Delayed' ||
					jsonFlightResultModel.FlightStatus === 'Cancelled') {
					$scope.FlightStatusStyle = 'scheduled';
				} else if (jsonFlightResultModel.FlightStatus === 'Recovery' ||
					jsonFlightResultModel.FlightStatus === 'Diverted' ||
					jsonFlightResultModel.FlightStatus === 'In Air' ||
					jsonFlightResultModel.FlightStatus === 'Expected' ||
					jsonFlightResultModel.FlightStatus === 'Departed') {
					$scope.FlightStatusStyle = 'active';
				}

				//$scope.FlightStatus = 'arrived'; //arrived, active, delayed, scheduled

				if (jsonFlightResultModel.FlightOnTimeStatus != null && jsonFlightResultModel.FlightOnTimeStatus !== '') {
					$scope.FlightStatusIndicator = jsonFlightResultModel.FlightOnTimeStatus;
					if ($scope.FlightStatusIndicator in $scope.FlightDelayStatus) {
						$scope.OnTimeStatusStyle = 'delayed'; //ontime, delayed
					} else if ($scope.FlightStatusIndicator in $scope.FlightOnTimeStatus) {
						$scope.OnTimeStatusStyle = 'ontime'; //ontime, delayed
					}
				}

				$scope.ScheduledDepartureTime = jsonFlightResultModel.ScheduledDepartureTime;
				$scope.ScheduledArrivalTime = jsonFlightResultModel.ScheduledArrivalTime;
				$scope.ActualDepartureTime = jsonFlightResultModel.ActualDepartureTime;
				$scope.EstimatedArrivalTime = jsonFlightResultModel.EstimatedArrivalTime;
				$scope.ScheduledDepartureDate = jsonFlightResultModel.ScheduledDepartureDate;
				$scope.ScheduledArrivalDate = jsonFlightResultModel.ScheduledArrivalDate;
				$scope.ActualDepartureDate = jsonFlightResultModel.ActualDepartureDate;
				$scope.EstimatedArrivalDate = jsonFlightResultModel.EstimatedArrivalDate;

				$scope.ScheduledDepartureTerminal = jsonFlightResultModel.ScheduledDepartureTerminal;
				$scope.ScheduledDepartureGate = jsonFlightResultModel.ScheduledDepartureGate;
				$scope.ScheduledArrivalGate = jsonFlightResultModel.ScheduledArrivalGate;
				$scope.ActualArrivalTerminal = jsonFlightResultModel.ActualArrivalTerminal;
				$scope.MapURL = jsonFlightResultModel.MapURL;

				if (jsonFlightResultModel.FlightStatus === 'In Air') {
					$scope.MapHideStyle = 'map-hidden';
				} else {
					$scope.MapHideStyle = '';
				}

				if (jsonFlightResultModel.FlightDelayDuration == null || jsonFlightResultModel.FlightDelayDuration === '00:00') {
					$scope.FlightDelayDuration = '';
				} else {
					$scope.FlightDelayDuration = jsonFlightResultModel.FlightDelayDuration;
				}
			});
		};

		HaFlightStatusController.$inject = ['$scope', 'haCustomerSelections'];

		var HaFlightStatusLink = function ($scope, $el) {

			haGlobals('jsonFSByRouteModel', function (jsonFSByRouteModel) {
				$scope.flightsByRouteVM = jsonFSByRouteModel;
			});

			$scope.$index = 0;

			$scope.$arrived = $el.find('.arrived');
			$scope.$active = $el.find('.active');
			$scope.$scheduled = $el.find('.scheduled');

			$scope.$arrivedExpandControl = $el.find('.arrived-expand-control');
			$scope.$activeExpandControl = $el.find('.active-expand-control');
			$scope.$scheduledExpandControl = $el.find('.scheduled-expand-control');

			$scope.arrivedIsExpanded = false;
			$scope.activeIsExpanded = false;
			$scope.scheduledIsExpanded = false;

			$scope.mapIsHidden = true;

			$scope.convertToDate = function (dateString) {
				return new Date(dateString);
			};

			$scope.expandBtnClick = function (section) {
				if (section === 'arrived') {
					$scope.expandArrived($scope.arriveIsExpanded);
				}
				else if (section === 'active') {
					$scope.expandActive($scope.activeIsExpanded);
				}
				else if (section === 'scheduled') {
					$scope.expandScheduled($scope.scheduledIsExpanded);
				}
			};

			$scope.expandArrived = function (expanded) {
				if (!expanded) {
					$scope.openArrived();
				}
				else {
					$scope.closeArrived();
				}
			};

			$scope.openArrived = function () {
				$scope.arriveIsExpanded = true;
				$scope.$arrived.addClass('expand');
				$scope.$arrivedExpandControl.removeClass('fontIcon20-circlePlus');
				$scope.$arrivedExpandControl.addClass('fontIcon20-circleMinus');
			};

			$scope.closeArrived = function () {
				$scope.arriveIsExpanded = false;
				$scope.$arrived.removeClass('expand');
				$scope.$arrivedExpandControl.removeClass('fontIcon20-circleMinus');
				$scope.$arrivedExpandControl.addClass('fontIcon20-circlePlus');
			};

			$scope.expandActive = function (expanded) {
				if (!expanded) {
					$scope.openActive();
				}
				else {
					$scope.closeActive();
				}
			};

			$scope.openActive = function () {
				$scope.activeIsExpanded = true;
				$scope.$active.addClass('expand');
				$scope.$activeExpandControl.removeClass('fontIcon20-circlePlus');
				$scope.$activeExpandControl.addClass('fontIcon20-circleMinus');
			};

			$scope.closeActive = function () {
				$scope.activeIsExpanded = false;
				$scope.$active.removeClass('expand');
				$scope.$activeExpandControl.removeClass('fontIcon20-circleMinus');
				$scope.$activeExpandControl.addClass('fontIcon20-circlePlus');
			};

			$scope.expandScheduled = function (expanded) {
				if (!expanded) {
					$scope.openScheduled();
				}
				else {
					$scope.closeScheduled();
				}
			};

			$scope.openScheduled = function () {
				$scope.scheduledIsExpanded = true;
				$scope.$scheduled.addClass('expand');
				$scope.$scheduledExpandControl.removeClass('fontIcon20-circlePlus');
				$scope.$scheduledExpandControl.addClass('fontIcon20-circleMinus');
			};

			$scope.closeScheduled = function () {
				$scope.scheduledIsExpanded = false;
				$scope.$scheduled.removeClass('expand');
				$scope.$scheduledExpandControl.removeClass('fontIcon20-circleMinus');
				$scope.$scheduledExpandControl.addClass('fontIcon20-circlePlus');
			};

			$scope.mapToggleBtnClick = function (mapIndex) {

				$scope.$currentMapToggle = $el.find('.map-toggle-' + mapIndex);
				$scope.$currentMap = $el.find('.flight-status-map-' + mapIndex);

				if ($scope.$currentMapToggle.hasClass('map-hidden')) {
					$scope.$currentMapToggle.removeClass('map-hidden');
					$scope.$currentMapToggle.addClass('map-shown');
					$scope.$currentMap.addClass('expand');
				}
				else {
					$scope.$currentMapToggle.removeClass('map-shown');
					$scope.$currentMapToggle.addClass('map-hidden');
					$scope.$currentMap.removeClass('expand');
				}
			};

			$scope.createLegs(2);
			//$scope.depart = 'today';

			$scope.openOnLoad = function () {
				if ($scope.$active !== null && $scope.$active !== undefined && $scope.flightsByRouteVM !== undefined && $scope.flightsByRouteVM.ArrivedFlights.length > 0) {
					$scope.openArrived();
				}
				else if ($scope.$active !== null && $scope.$active !== undefined && $scope.flightsByRouteVM !== undefined && $scope.flightsByRouteVM.ActiveFlights.length > 0) {
					$scope.openActive();
				}
				else if ($scope.$scheduled !== null && $scope.$scheduled !== undefined && $scope.flightsByRouteVM !== undefined && $scope.flightsByRouteVM.ScheduledFlights.length > 0) {
					$scope.openScheduled();
				}
			};

			$scope.$emit('$haFlightStatusReady');

			$scope.openOnLoad();

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaFlightStatusLink,
			controller: HaFlightStatusController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Insufficient Miles
	// --------------------------------------------
	//
	// * **Class:** HaInsufficientMiles
	// * **Author:** Josh Nielsen
	//
	// A page that notifies you of additional miles you will need to purchase.

	'use strict';

	var module = angular.module('haInsufficientMilesModule', []);

	module.directive('haInsufficientMiles', ['haGlobals', function (haGlobals) {

		var HaInsufficientMilesController = function ($scope, haModal, $pax) {

			haGlobals('EaWard', function (EaWard) {
				$.extend($scope, EaWard);
				$scope.PurchaseMilesCost = EaWard;
				$scope.currency = $scope.PurchaseMilesCost.Currency;
			});
			$scope.$emit('$haInsufficientMilesReady');
			$scope.$pax = $pax;
		};

		HaInsufficientMilesController.$inject = ['$scope'];

		var HaInsufficientMilesLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaInsufficientMilesLink,
			controller: HaInsufficientMilesController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Purchase Confirmation
	// --------------------------------------------
	//
	// * **Class:** HaPurchaseConfirmation
	// * **Author:** Cory Shaw
	//
	// purchase confirmation page

	'use strict';

	var module = angular.module('haPurchaseConfirmationModule', []);

	module.directive('haPurchaseConfirmation', ['haGlobals', 'haUplift', 'haUtils', function (haGlobals, haUplift, haUtils) {

		var HaPurchaseConfirmationController = function ($scope, $rootScope, haModal, $pax, aSvc, $timeout, $scs) {

			$scope.SpeedyEnrollAccountNumber = [];
			$scope.IsSpeedEnrollVerified = '';
			$scope.$pax = $pax;
			$scope.$pax.passengers = [];
			$scope.selectedSegments = [];
			$scope.IsPartialSuccess = false;
			$scope.confirmedPaxList = [];
			$scope.declinedPaxList = [];
			$scope.confirmedPaxListTxt = '';
			$scope.declinedPaxListTxt = '';
			$scope.andText = '';
			$scope.encryptedPNR = '';
			$scope.encryptedLastName = '';

			// mocked purchases

			haGlobals('andText', function (andText) {
				$scope.andText = andText || 'and';
			});

			haGlobals(['encryptedPNR', 'encryptedLastName'], function (encryptedPNR, encryptedLastName) {
				$scope.encryptedPNR = encryptedPNR;
				$scope.encryptedLastName = encryptedLastName;
			});

			haGlobals('modelJson', function (modelJson) {
				// console.log('model /////////////////');
				// console.log(modelJson);
				$scope.VM = modelJson;
				
				//Tracking MilesMaximizer Offers
				if ($scope.VM && $scope.VM.IsMilesMaximizerEligible) {
					// Custom Event for Analytics
					document.body.dispatchEvent(new CustomEvent('MilesMaximizerOffered'));
				}

				/* //////////////////
				 ////// MOCKED ///////
				 ///////////////////*/

				$scope.VM.lstAncillaryUpSells = modelJson.lstAncillaryUpSells;
				
				$scope.VM.PurchasedAncillaries = [
					{
						Type: 'Hotel',
						Title: 'Kihei Beach Marriot Resort & Spa',
						ReferenceNumberText: '',
						ReferenceNumber: '',
						IconClassName: 'hotel-tab',
						ImageURL: 'https://s3.amazonaws.com/uploads.hipchat.com/77785/1355952/e6FvRjou0h4ao0F/kihei-beach.jpg',
						DetailsDictionary: [
							{
								Key: 'Check-in',
								Value: 'Sun, Aug 3, 2014'
							}, {
								Key: 'Check-out',
								Value: 'Sun, Aug 10, 2014'
							}, {
								Key: 'Nights',
								Value: '7'
							}, {
								Key: 'Room(s)',
								Value: 'Ocean View Lahaina Kai Tower - 1 King Bed, Ocian View Lahaina Kai Tower - 2 Queen Beds'
							}, {
								Key: 'Room 1',
								Value: 'Elizabeth Lee'
							}, {
								Key: 'Room 2',
								Value: 'John Lee'
							}, {
								Key: 'Address',
								Value: '2365 Kaanapali Parkway, Lahaina, HI 96761'
							}
						],
						Terms: ''
					},
					{
						Type: 'Rental Car Reservation',
						Title: 'Hertz: Tesla Roadster',
						ReferenceNumberText: 'Car Rental Reference Number:',
						ReferenceNumber: 'US292179320',
						IconClassName: 'car-tab',
						ImageURL: 'https://s3.amazonaws.com/uploads.hipchat.com/77785/1355952/7k4Vu24h1WjJwXl/hertz-tesla.jpg',
						DetailsDictionary: [
							{
								Key: 'Driver',
								Value: 'Charlie Becket'
							}, {
								Key: 'Pick-up',
								Value: 'Sun, Aug 3, 2014'
							}, {
								Key: 'Drop-off',
								Value: 'Sun, Aug 10, 2014'
							}, {
								Key: 'Location',
								Value: 'Maui - Kahului Airport (OGG)'
							}, {
								Key: 'Cost',
								Value: '$495 (Estimated Total)'
							}
						],
						Terms: 'To avail the offer, you must be 25 years or older. Eum haero quidne saepius opto. Paratus a bis jugis blandit acsi distineo esse ut accumsan. Ad consequat venio refoveo at quis lucidus neque suscipere praesent. Vulputate paulatim saluto volutpat indoles velit eum pala ulciscor lucidus valetudo oppeto. Indoles comis wisi probo et elit. <br><a href>FAQ</a> | <a href>Terms and Conditions</a>'
					},
					{
						Type: 'Shuttle',
						Title: 'The Maui Hotel Shuttle',
						ReferenceNumberText: 'Maui Hotel Shuttle Reference Number:',
						ReferenceNumber: 'TRANSFER-M83920',
						IconClassName: 'shuttle',
						ImageURL: 'https://s3.amazonaws.com/uploads.hipchat.com/77785/1355952/FaChb3jeiGEBrUR/Hawaii-Kauai-Jan-2010-23b.JPG',
						DetailsDictionary: [
							{
								Key: 'Date',
								Value: 'Sun, Aug 3, 2014'
							}, {
								Key: 'Route',
								Value: 'Kahului Airport to your hotel. Includes your return trip from hotel to Kahului Airport.'
							}, {
								Key: 'Travelers',
								Value: '3'
							}
						],
						Terms: ''
					}
					/*
					 please use the following classes for icons:
					 hotel: 'hotel-tab'
					 car: 'car-tab'
					 lei-greeting: 'flower'
					 shuttle: 'shuttle',
					 trip insurance: 'insurance'
					 */
				];
			});


			// initial accordion state
			//VM.lstAncillaryUpSells && VM.lstAncillaryUpSells.length > 0 && !VM.IsOrbitzBooking && !VM.IsRefundableFare
			var upsellsEmpty = !$scope.VM.lstAncillaryUpSells || ($scope.VM.lstAncillaryUpSells && !$scope.VM.lstAncillaryUpSells.length);
			var isOpen = upsellsEmpty || $scope.VM.IsRefundableFare || $scope.VM.IsOrbitzBooking;
			$scope.accordionState = {};
			$scope.accordionState.isOpenItinerary = isOpen;
			$scope.accordionState.changeSeatsAccordionGroup = isOpen;
			$scope.accordionState.eticketAccordionGroup = isOpen;
			$scope.accordionState.isOpenCost = isOpen;

			$scope.openItinerary = function () {
				$scope.submitting = true;
				window.location.href = "/my-account/my-trips/manage-trip-itinerary?enc=1&p=" + $scope.encryptedPNR + "&lastName=" + $scope.encryptedLastName;
			}

			$scope.getConfirmedPaxList = function () {
				var msg = '';
				for (var i = 0; i < $scope.confirmedPaxList.length; i++) {
					msg += $scope.confirmedPaxList[i];
					if (i < $scope.confirmedPaxList.length - 2) {
						msg += ', ';
					} else if (i < $scope.confirmedPaxList.length - 1) {
						msg += $scope.andText + ' ';
					}
				}
				return msg;
			};

			$scope.getDeclinedPaxListTxt = function () {
				var msg = '';
				for (var i = 0; i < $scope.declinedPaxList.length; i++) {
					msg += $scope.declinedPaxList[i];
					if (i < $scope.declinedPaxList.length - 3) {
						msg += ', ';
					} else if (i < $scope.declinedPaxList.length - 2) {
						msg += $scope.andText + ' ';
					}
				}
				return msg;
			};

			haGlobals('confirmModel', function (confirmModel) {
				$.extend($scope, confirmModel.ConfirmationItinerary);
				$scope.IsChangeFlightBooking = confirmModel.IsChangeFlightBooking;
				$scope.IsHoldBooking = confirmModel.IsHoldBooking;
				$scope.IsSeatAssignmentError = confirmModel.IsSeatAssignmentError;
				$scope.IsSpeedEnrollVerified = confirmModel.IsItinerarySpeedEnrollSuccess;
				$scope.IsTripInsurancePurchased = confirmModel.IsTripInsurancePurchased;
				if ($scope.TripSummary.TripInsurance) {

					$scope.TripSummary.TripInsurance.IsTripInsuranceSelected = confirmModel.IsTripInsurancePurchased;
				}
				$scope.showPriceDecreaseAlert = (confirmModel.APHMessages != null) && (confirmModel.APHMessages.MessageType === 3); //PRICE_DECREASE

				if (confirmModel.IsTripInsurancePurchased) {
					$scope.IsTripInsurancePurchaseSuccess = confirmModel.IsTripInsurancePurchaseSuccesss;
					$scope.IsTripInsurancePurchaseError = confirmModel.IsTripInsurancePurchaseError;
					$scope.ShowTripInsuranceSection = confirmModel.ShowTripInsuranceSection;
				}
				$scope.IsCarTrawlerPurchased = confirmModel.IsCarTrawlerPurchased;
				if (confirmModel.IsCarTrawlerPurchased) {
					$scope.IsCarTrawlerPurchaseSuccesss = confirmModel.IsCarTrawlerPurchaseSuccesss;
					$scope.IsCarTrawlerPurchaseError = confirmModel.IsCarTrawlerPurchaseError;
				}

				if (confirmModel.ConfirmationItinerary.TripSummary.SelectedCar.CostPerDay > 0) {
					window.digitalData.purchaseCar = {};
					window.digitalData.purchaseCar = {
						name: 'purchaseCarRequest',
						timestamp: new Date().getTime(),
						requestFrom: 'In-Path',
						requestSent: 'true',
						carSelected: confirmModel.ConfirmationItinerary.TripSummary.SelectedCar.IsInitialDisplaySelected ? 'Initial' : 'ShowMore'
					};

					window.digitalData.products.push({
						name: 'rentalCarConfirmation',
						timestamp: new Date().getTime(),
						carTrawlerCode: confirmModel.ConfirmationItinerary.TripSummary.SelectedCar.ConfirmationCode,
						abgCode: confirmModel.ConfirmationItinerary.TripSummary.SelectedCar.SupplierConfirmationCode,
						carSelected: confirmModel.ConfirmationItinerary.TripSummary.SelectedCar.IsInitialDisplaySelected ? 'Initial' : 'ShowMore'
					});
				}

				$scope.IsRequirePayment = confirmModel.IsRequirePayment;
				if (confirmModel.HoldEndDate != null) {
					$scope.HoldEndDate = confirmModel.HoldEndDate;
				}
				$scope.IsAirportShuttlePurchased = confirmModel.IsAirportShuttlePurchased;
				if (confirmModel.IsAirportShuttlePurchased) {
					$scope.IsAirportShuttlePurchaseSuccesss = confirmModel.IsAirportShuttlePurchaseSuccesss;
					$scope.IsAirportShuttlePurchaseError = confirmModel.IsAirportShuttlePurchaseError;
				}

				$scope.LeiGreeting = confirmModel.LeiGreeting;
				$scope.IsLeigreetingsPurchaseError = confirmModel.IsLeigreetingsPurchaseError;

				$scope.PassengerTripSummary = confirmModel.ConfirmationItinerary.PassengerTripSummary;
				$scope.TotalPrice = confirmModel.ConfirmationItinerary.TripSummaryTotal;

				//confirmModel.ConfirmationItinerary.Travellers[travellerIndix].IsSpeedEnrollVerified
				var travellerIndex = 0;
				angular.forEach(confirmModel.ConfirmationItinerary.Travellers, function (traveller) {
					$scope.SpeedyEnrollAccountNumber[travellerIndex] = traveller.SpeedyEnrollAccountNumber;
					travellerIndex++;

					if (traveller.TicketNumberSet != null) {
						$scope.confirmedPaxList.push(traveller.FirstName + ' ' + traveller.LastName);
					} else {
						$scope.declinedPaxList.push(traveller.FirstName + ' ' + traveller.LastName);
					}

				});
				$scope.confirmedPaxListTxt = $scope.getConfirmedPaxList();
				$scope.declinedPaxListTxt = $scope.getDeclinedPaxListTxt();
				if ($scope.reservationCode == null) {
					$scope.reservationCode = $scope.PNRs[0].PNRID;
				}

				//uplift is in confirmation
				$(document).ready(function () {
					setTimeout(function () {
						$rootScope.isConfirmPage = true;
						haUtils.isUpliftConfirmed();
						////Send confirmation code to uplift is booking sucessfuly paid by Uplift
						if ($rootScope.isUpliftConfirmed) {
							//restore uplift orderid to session storage
							localStorage.setItem("up_br", $rootScope.UpliftOrderId);
							if ($scope.UpliftReservationCode != null) {
								haUplift.confirmPayMonthly($rootScope.UpliftReservationCode);
							} else if ($scope.reservationCode != null) {
								haUplift.confirmPayMonthly($scope.reservationCode);
							} else {
								console.log("confirm uplift booking error");
							}
							
						}
					}, $rootScope.UpTimeout);
				});
				
				if (confirmModel.IsMainCabinBasicBooking) {
				    $scope.hideChangeSeats = true;
				}

				//Hold Booking
				if ($scope.IsHoldBooking) {
					$scope.hideChangeSeats = true;
					$scope.hideUpdateTravelerInfo = true;
					$scope.hideViewOrPrintReceipt = true;
				}

				// APH Bookings
				if ((confirmModel.ConfirmationItinerary != null) &&
					(confirmModel.ConfirmationItinerary.TripSummary != null)) {

					if (confirmModel.ConfirmationItinerary.TripSummary.SelectedHotel != null) {
						$scope.hotel = confirmModel.ConfirmationItinerary.TripSummary.SelectedHotel;
						if ($scope.hotel != null) {
							$scope.reservationCode = confirmModel.ConfirmationItinerary.PNRs[0].OWWConfirmationID;
							$scope.hideChangeSeats = true;
							$scope.hideUpdateTravelerInfo = true;
							$scope.hideViewOrPrintReceipt = true;
							$scope.showCallUsToChange = true;
						}
					}

				}

				if ($scope.confirmedPaxList.length > 0 && $scope.declinedPaxList.length > 0) {
					$scope.IsPartialSuccess = true;
				}

				// check if this is a code share flight
				$scope.IsCodeShareOnly = true;
				angular.forEach($scope.PNRs[0].Trips, function (trip) {
					angular.forEach(trip.Legs, function (leg) {
						if (leg.ISOverNightStay) {
							$scope.PNRs[0].ISOverNightStay = true;
						}
						if (leg.IsCodeShare === false) {
							$scope.IsCodeShareOnly = false;
						}
					});
				});
			});

			function getNativeAppBannerStrings() {
				if (!$rootScope.isMobile) {
					$scs.get('CONFIRMATION_NATIVE_APP_BANNER').then(function (data) {
						$scope.nativeAppBannerCookie = 'checkInOrConfirmationNativeAppBanner'; // same as Check-in
						$scope.nativeAppBannerHeader = data.header;
						$scope.nativeAppBannerLinkText = data.linktext;
						$scope.nativeAppBannerCookieDays = data.cookiedays;
						$scope.nativeAppBannerEnabled = !!data.enable;
					}).catch(function (error) {
						console.log(error);
					});
				}
			};

			$scope.$emit('$haPurchaseConfirmationReady');

			setTimeout(function () {
				$scope.$emit('resizeFlightHop');
				getNativeAppBannerStrings();
			}, 20);
		};


		HaPurchaseConfirmationController.$inject = ['$scope', '$rootScope', 'haModal', 'haPassengersService', 'haAncillariesSvc', '$timeout', 'haSitecoreStrings'];

		var HaPurchaseConfirmationLink = function ($scope) {

			$scope.$watch('TripSummary.SelectedCar', function (SelectedCar) {
				$scope.SelectedCar = SelectedCar;
			});
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaPurchaseConfirmationLink,
			controller: HaPurchaseConfirmationController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha My Dashboard
	// --------------------------------------------
	//
	// * **Class:** HaMyDashboard
	// * **Author:** Curtis Floth
	//
	// Overview information for HA Miles customer.

	'use strict';

	var module = angular.module('haMyDashboardModule', []);

	module.directive('haMyDashboard', ['haGlobals', function (haGlobals) {

		var HaMyDashboardController = function ($scope) {

			haGlobals('jsonMemberSummaryModel', function (jsonMemberSummaryModel) {
				$scope.HMDashBoardViewModel = jsonMemberSummaryModel;

				if ($scope.HMDashBoardViewModel.HMMembershipType === 'Pualani Gold') {
					$scope.ImageUrl = 'card03_bg_flower02a.jpg';
				} else if ($scope.HMDashBoardViewModel.HMMembershipType === 'Platinum') {
					$scope.ImageUrl = 'card04_bg_pattern01.jpg';
				}

				// console.log($scope.HMDashBoardViewModel.HMMembershipType);
			});

			$scope.$emit('$haMyDashboardReady');
		};

		HaMyDashboardController.$inject = ['$scope'];

		var HaMyDashboardLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaMyDashboardLink,
			controller: HaMyDashboardController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Checkout
	// --------------------------------------------
	//
	// * **Class:** HaCheckout
	// * **Author:** Cory Shaw
	//
	// This is the main module for the review and pay page in the booking path

	'use strict';

	var module = angular.module('haCustomDirectivesModule', ['haUserProfileAPI']);

	module.directive('numberOnly', function () {
		return {
			require: 'ngModel',
			link: function (scope, element, attrs, modelCtrl) {
				modelCtrl.$parsers.push(function (inputValue) {
					if (inputValue == null) {
						return '';
					}
					var transformedInput = inputValue.replace(/[^0-9]/g, '');
					if (transformedInput !== inputValue) {
						var el = element[0];
						var start = 0;
						var end = 0;
						var isError = false;
						try {
							start = el.selectionStart;
							end = el.selectionEnd + transformedInput.length - inputValue.length;
						}
						catch (ex) {
							isError = true;
						}

						modelCtrl.$setViewValue(transformedInput);
						modelCtrl.$render();

						if (!isError) {
							try {
								el.setSelectionRange(start, end);
							}
							catch (ex) {
							}
						}
					}
					return transformedInput;
				});
			}
		};
	});

	module.directive('minLength', function () {
		return {
			require: 'ngModel',
			link: function (scope, elm, attr, ngModel) {

				var minlength = parseInt(attr.minLength);

				scope.minLengthValidator = function (value) {
					var validity = ngModel.$isEmpty(value) || value.length >= minlength;
					ngModel.$setValidity('minlength', validity);
					return validity ? value : undefined;
				};

				attr.$observe('minLength', function (val) {
					minlength = parseInt(val);
					scope.minLengthValidator(ngModel.$viewValue);
				});

				scope.$watch(attr.ngModel, function (newValue) {
					minlength = parseInt(attr.minLength);
					scope.minLengthValidator(newValue);
				});

				//  ngModel.$parsers.push(minLengthValidator);
				//      ngModel.$formatters.push(minLengthValidator);
			}
		};
	});

	module.directive('maxLength', function () {
		return {
			require: 'ngModel',
			link: function (scope, elm, attr, ngModel) {

				var maxlength = parseInt(attr.maxLength);

				scope.maxLengthValidator = function (value) {
					var validity = ngModel.$isEmpty(value) || value.length <= maxlength;
					ngModel.$setValidity('maxlength', validity);
					return validity ? value : undefined;
				};

				attr.$observe('maxLength', function (val) {
					maxlength = parseInt(val);
					elm.attr('maxlength', maxlength);
					scope.maxLengthValidator(ngModel.$viewValue);
				});


				scope.$watch(attr.ngModel, function (newValue) {
					maxlength = parseInt(attr.maxLength);
					scope.maxLengthValidator(newValue);
				});

				//ngModel.$parsers.push(maxLengthValidator);
				//     ngModel.$formatters.push(maxLengthValidator);
			}
		};
	});

	module.directive('regexPattern', function () {
		return {
			require: 'ngModel',
			link: function (scope, elm, attr, ngModel) {

				var regexPattern = new RegExp(attr.regexPattern);


				ngModel.$parsers.push(function (inputValue) {
					if (inputValue == null) {
						return '';
					}
					var transformedInput = inputValue.replace(regexPattern, '');
					if (transformedInput !== inputValue) {
						var el = elm[0];
						var start = 0;
						var end = 0;
						var isError = false;
						try {
							start = el.selectionStart;
							end = el.selectionEnd + transformedInput.length - inputValue.length;
						}
						catch (ex) {
							isError = true;
						}

						ngModel.$setViewValue(transformedInput);
						ngModel.$render();

						if (!isError) {
							try {
								el.setSelectionRange(start, end);
							}
							catch (ex) {
							}
						}
					}
					return transformedInput;
				});

			}
		};
	});

	module.directive('userNameUnique', ['$q', 'haUserProfileAPI', function ($q, haUserProfileAPI) {
		return {
			require: 'ngModel',
			link: function (scope, elem, attrs, ctrl) {
				var canceler;
				scope.safeApply = function (fn) {
					var phase = this.$root.$$phase;
					if (phase === '$apply' || phase === '$digest') {
						if (fn && (typeof (fn) === 'function')) {
							fn();
						}
					} else {
						this.$apply(fn);
					}
				};

				scope.$watch(attrs.ngModel, function () {
					scope.safeApply(function () {
						ctrl.$setValidity('userNameNotUnique', true);
					});
				});
				elem.on('blur', function (/* evt */) {
					if (!scope.isValid()) {
						scope.$apply(function () {
							ctrl.$setValidity('userNameAjax', false);
							var data = {'username': elem.val()};

							if (canceler) {
								canceler.resolve();
							}
							canceler = $q.defer();

							haUserProfileAPI.checkUsernameAvailability(data, canceler)
							.success(function (data) {
								ctrl.$setValidity('userNameNotUnique', data.IsSuccess);
								ctrl.$setValidity('userNameAjax', true);
							});
						});
					}
				});

				scope.isValid = function () {
					if (elem.controller('ngModel').$error) {
						var isError = false;
						var obj = elem.controller('ngModel').$error;
						for (var prop in obj) {
							if (obj.hasOwnProperty(prop)) {
								if (prop !== 'userNameAjax' && prop !== 'userNameNotUnique' && obj[prop] === true) {
									isError = true;
									break;
								}
							}
						}
						return isError;
					}
					else {
						return false;
					}
				};
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha My Trips
	// --------------------------------------------
	//
	// * **Class:** HaMyTrips
	// * **Author:** Curtis Floth / Jamie Perkins
	//
	// Allows customers to view their trips

	'use strict';

	var module = angular.module('haMyTripsModule', ['haGeoDataModule']);

	module.directive('haMyTrips', ['haGlobals', 'haModal', 'haHttpService', '$filter', 'haSitecoreStrings', '$log', '$timeout', '$window',
		function (haGlobals, haModal, haHttpService, $filter, $scs, $log, $timeout, $window) {

			var HaMyTripsController = function ($scope, $rootScope) {

				function localizeBracketString(string, replacementArray) {
					if (string) {
						var index = 0;
						return string.replace(/(\[[a-zA-Z0-9_\- ]+\])/g, function (j) {
							if (j) {
								var r = replacementArray[index];
								index++;
								return r;
							}
						});
					}
				}

				var resDeptLink = '<a href="/contact-us">' + $scs('UPCOMING_TRIPS.reservationsdepartmentlinktext') + '</a>';
				$scope.reservationsWithLinkText = localizeBracketString($scs('UPCOMING_TRIPS.reservationsinformationtext'), [resDeptLink]);


				// var responseMessage = '';
				// var messageType;
				// var message;

				// default selected tab
				$scope.currentTab = 'all';

				// sort upcoming trips
				$scope.model = {
					sortUpcomingByOptions: [$scs('UPCOMING_TRIPS.alldatestext')],
					sortUpcomingByIndex: 0
				};

				$scope.modelLoading = true;
				var tripsEndpoint = '/MyAccount/MyTrips/GetAllTrips' + ((/Windows /i).test(window.navigator.userAgent) ? ('?' + Math.floor(Math.random() * 9999) + 1) : '');

				$scope.tripLookup = {
					confirmationCode: '',
					lastName: ''
				};

				$scope.RedirectURL = '';
				$scope.cancelCurrencyMatch = true;
				$scope.purchaseCurrencyMatch = true;
				$scope.cancelClicked = [];
				$scope.purchaseClicked = [];

				// get trips view model
				$scope.VM = {};
				haHttpService.GET(tripsEndpoint).success(function (response) {
					// $log.debug('///////////////// trips endpoint response');
					// $log.debug(response);

					$scope.modelLoading = false;

					$scope.VM = response;
					$scope.VM.UpcomingTripsCount = 0;
					$scope.VM.SavedAndHeldTripsCount = 0;
					if ($scope.VM.IsHeldRetrieval) {
						// set default selected tab to Held/saved trips
						$scope.currentTab = 'savedHeld';
					}
					//$scope.VM.AllTripsMaxRecordsPerPage = 6;

					// old stuff
					// responseMessage = UpComingTripsJson.ModelMessage;
					// $scope.LastName = UpComingTripsJson.LastName;
					// $scope.EncryptedLastName = UpComingTripsJson.EncryptedLastName;

					if ($scope.VM.UpComingTripsList.length > 0) {
						$scope.VM.UpcomingTripsCount = $scope.VM.UpComingTripsList.length;

						// build date list to sort upcoming trips by
						var lastMonth = null;
						var lastYear = null;
						var sortByIndex = 0;
						var dateCounts = [0];
						var thisCount = 0;

						for (var i = 0; i < $scope.VM.UpComingTripsList.length; i++) {

							// NOT temporary
							var date = new Date($scope.VM.UpComingTripsList[i].DepartureDate);
							var month = date.getMonth();
							var year = date.getFullYear();
							if (lastMonth !== month || lastYear !== year) {
								var dateOptionString = $filter('date')(date, 'MMMM yyyy');
								$scope.model.sortUpcomingByOptions.push(dateOptionString);
								sortByIndex++;
								if (i > 0) {
									dateCounts.push(thisCount);
								}
								thisCount = 1;
								lastMonth = month;
								lastYear = year;
							} else {
								thisCount++;
							}
							$scope.VM.UpComingTripsList[i].sortByIndex = sortByIndex;
						}
						dateCounts.push(thisCount);
						// add counts to sort by options
						for (var i = 0; i < dateCounts.length; i++) {
							if (i > 0) {
								var dateOptionString = $scope.model.sortUpcomingByOptions[i];
								dateOptionString += ' (' + dateCounts[i] + ')';
								$scope.model.sortUpcomingByOptions[i] = dateOptionString;
							}
						}

						// build list of visible dropdown options
						$scope.VM.TripsDDVisibility = {
							showAddToCalendarOption: false,
							showPrintOption: false,
							showShareItineraryOption: false,
							showViewReceiptsOption: false
						};
						for (var i = 0; i < $scope.VM.UpcomingTripOptionsDropDown.length; i++) {
							switch ($scope.VM.UpcomingTripOptionsDropDown[i].Value) {
								case 'VR':
									$scope.VM.TripsDDVisibility.showViewReceiptsOption = true;
									break;
								case 'AC':
									$scope.VM.TripsDDVisibility.showAddToCalendarOption = true;
									break;
								case 'PR':
									$scope.VM.TripsDDVisibility.showPrintOption = true;
									break;
								case 'SR':
									$scope.VM.TripsDDVisibility.showShareItineraryOption = true;
									break;
							}
						}
					}

					// saved AND held trips list
					if ($scope.VM.HoldTripList == null) {
						$scope.VM.HoldTripList = [];
					}
					for (var i = 0; i < $scope.VM.HoldTripList.length; i++) {
						$scope.VM.HoldTripList[i].tripType = 'held';
						var replaceArr = [
							$scope.VM.HoldTripList[i].PNRHoldEndDate,
							'11:59 PM'
						];
						$scope.VM.HoldTripList[i].mustPurchaseByText = localizeBracketString($scs('UPCOMING_TRIPS.mustpurchasedatetime'), replaceArr);
						$scope.VM.HoldTripList[i].Removed = false;
						$scope.purchaseClicked[i] = false;
						$scope.cancelClicked[i] = false;
					}

					if ($scope.VM.SavedTripList == null) {
						$scope.VM.SavedTripList = [];
					}

					for (var i = 0; i < $scope.VM.SavedTripList.length; i++) {
						$scope.VM.SavedTripList[i].tripType = 'saved';
						/*
						 // 	MOCKED VALUES
						 $scope.VM.SavedTripList[i].TripTitle = 'Summer Vacation '+Number(i+1); // temporary
						 $scope.VM.SavedTripList[i].SavedDate = '02/'+Number(i+5)+'/2015'; // temporary
						 $scope.VM.SavedTripList[i].Cost = '31'+i+'5.95'; // temporary
						 $scope.VM.SavedTripList[i].ReturnDate = '02/'+Number(i+9)+'/2015'; // temporary
						 */
					}

					var SavedAndHeldTrips = [];
					SavedAndHeldTrips = SavedAndHeldTrips.concat($scope.VM.SavedTripList, $scope.VM.HoldTripList);
					$scope.VM.SavedAndHeldTrips = SavedAndHeldTrips;
					$scope.VM.SavedAndHeldTripsCount = SavedAndHeldTrips.length;

					// limiting - need separate vars bc each can change separately when more are "loaded"
					$scope.upcomingTripsLimit = $scope.VM.UpComingTripsMaxRecordsPerPage || 5;
					$scope.savedHeldTripsLimit = $scope.VM.UpComingTripsMaxRecordsPerPage || 5;

				}).error(function (data, status) {
					$log.debug('get all trips error: ' + status);
					$scope.modelLoading = false;
					$scope.Error = status;
				});

				// old banner message
				/*
				 var index = responseMessage.indexOf('|');
				 if (index > 0) {
				 messageType = responseMessage.substring(0, index);
				 message = responseMessage.substring(index + 1);

				 $scope.showmsg_upcomingtrip = true;
				 $scope.msgtype_upcomingtrip = messageType;
				 $scope.msg_upcomingtrip = message;
				 }
				 */
				$scope.selectTab = function (tab) {
					// reset sorting
					$scope.model.sortUpcomingByIndex = 0;
					$scope.currentTab = tab;
					$scope.dismissDropdown();

					$timeout(function () {
						// fixes IE9 bug in which only the first letter of selected option is displayed
						$('#sortUpcomingByOptions').width($('#sortUpcomingByOptions').width());
					});
				};

				$scope.chooseDateFilter = function (index) {
					$scope.model.sortUpcomingByIndex = index;
					$scope.$broadcast('$closeCustomDropdown');
				};

				// predicate function to filter upcoming trips by date
				$scope.upcomingTripsByDate = function (val) {
					if ($scope.model.sortUpcomingByIndex === 0) {
						return true;
					} else {
						if (val.sortByIndex === $scope.model.sortUpcomingByIndex) {
							return true;
						}
						else {
							return false;
						}
					}
				};

				$scope.dismissDropdown = function () {
					$scope.$broadcast('$closeCustomDropdown');
				};

				$scope.timeElapsed = function (timestamp) {
					var date = new Date(timestamp);
					var secs = date.getTime(date) / 1000;
					var offsetSecs = date.getTimezoneOffset() * 60;
					var now = new Date().getTime() / 1000;
					now = now + offsetSecs;
					var diff = now - secs;

					var mins = Math.round(diff / 60);
					var hrs = Math.round(mins / 60);
					var days = Math.round(hrs / 24);

					if (days >= 1) {
						return 'on ' + $filter('localShortDate')(date, $scope.$language);
					}
					if (hrs >= 1) {
						return hrs + ' hours ago';
					}
					if (mins > 1) {
						return mins + ' minutes ago';
					}
					if (diff <= 60) {
						return 'just now';
					}

					return timestamp;
				};

				$scope.loadMoreTrips = function () {
					if ($scope.currentTab === 'upcoming') {
						$scope.upcomingTripsLimit += $scope.VM.UpComingTripsMaxRecordsPerPage;
					} else {
						$scope.savedHeldTripsLimit += $scope.VM.UpComingTripsMaxRecordsPerPage;
					}
				};

				$scope.GotoBook = function () {
					window.location.href = '/Book';
				};

				// remove hold
				$scope.confirmRemoveHeldTrip = function (index) {
					var modalScope = $rootScope.$new(true, $scope);
					modalScope.RemoveHoldTripIndex = index;
					if ($scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].PNRCurrencyCode !== $scope.VM.CookiesCurrencyCode) {
						$scope.cancelCurrencyMatch = false;
						$scope.cancelClicked[modalScope.RemoveHoldTripIndex] = true;
						$scope.currencyMismatchPNR = $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].ReservationCode;
						$scope.heldCurrency = $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].PNRCurrencyCode;
						$scope.siteCurrency = $scope.VM.CookiesCurrencyCode;
					} else {
						$scope.cancelCurrencyMatch = true;
						modalScope.removeHeldTrip = function () {
							//console.log('root scope remove held trip at index: ' + index);
							var postObject = {
								cancelHoldTripRQ: {
									RecordLocator: $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].ReservationCode,
									LastName: $scope.VM.LastName
								}
							};
							modalScope.RequestingRemoval = true;
							$scope.VM.RemoveRequestSent = false; // for result banner

							haHttpService.POST('/MyAccount/MyTrips/CancelHoldTrip', postObject).success(function (data /*, status, headers, config */) {
								//console.log('cancel held trip success');
								//console.log(data);

								$scope.loading = false;
								$scope.VM.RemoveRequestSent = true;
								modalScope.RequestingRemoval = false;
								$scope.RemovalSuccess = data.PNRCancelStatus;
								$scope.holdremovedorig = $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].OriginCityCode;
								$scope.holdremoveddest = $scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].DestinationCityCode;
								$scope.VM.SavedAndHeldTrips[modalScope.RemoveHoldTripIndex].Removed = true;
								modalScope.$modalCancel();

							}).error(function (/* data, status, headers, config */) {
								//console.log('remove held trip error: ' + status);

								$scope.VM.RemoveRequestSent = true;
								$scope.RemovalSuccess = false;
								modalScope.RequestingRemoval = false;
								modalScope.$modalCancel();
							});
						};
						haModal('/Areas/MyAccount/Views/Renderings/Shared/_Held_Trips.cshtml', {
							id: 'cancel-held-trip',
							backdrop: 'true',
							template: angular.element('.remove-held-booking'),
							scope: modalScope
						});
						$scope.RemovalSuccess = false;
					}
				};

				$scope.checkCurrency = function (index) {
					if ($scope.VM.SavedAndHeldTrips[index].PNRCurrencyCode !== $scope.VM.CookiesCurrencyCode) {
						$scope.purchaseCurrencyMatch = false;
						$scope.purchaseClicked[index] = true;
						$scope.currencyMismatchPNR = $scope.VM.SavedAndHeldTrips[index].ReservationCode;
						$scope.heldCurrency = $scope.VM.SavedAndHeldTrips[index].PNRCurrencyCode;
						$scope.siteCurrency = $scope.VM.CookiesCurrencyCode;
					} else {
						$scope.submitting = true;
						$scope.purchaseCurrencyMatch = true;
						$window.location.href = '/Book/Itinerary/HeldItinerary?enc=1&pnr=' + $scope.VM.SavedAndHeldTrips[index].EncryptedReservationCode + '&lastName=' + $scope.VM.SavedAndHeldTrips[index].EncryptedLastName;
					}
				};

				$scope.submitTripLookupForm = function (Form) {
					/* global TripLookupForm */
					if (Form.$valid) {
						$scope.searchingTrip = true;
						TripLookupForm.submit();
					}
				};

				$scope.disableOpenItinerary = function (event) {
					if ($scope.openItineraryDisabled) {
						event.preventDefault();
						event.stopPropagation();
						return false;
					}
					$scope.openItineraryDisabled = true;
				};

				$scope.disableOpenSubItinerary = function (upcomingTrip, event) {
					if (upcomingTrip.openItineraryDisabled) {
						event.preventDefault();
						event.stopPropagation();
						return false;
					}
					upcomingTrip.openItineraryDisabled = true;
				};
			};

			HaMyTripsController.$inject = ['$scope', '$rootScope'];

			var HaMyTripsLink = function ($scope) {

				$scope.exampleMethod = function () {
					return $scope;
				};

			};

			HaMyTripsLink.$inject = ['$scope'];

			return {
				restrict: 'A',
				scope: true,
				link: HaMyTripsLink,
				controller: HaMyTripsController
			};
		}]);



	module.directive('haMyTripsPayment', function () {

		var HaMyTripsPaymentController = function ($scope, $rootScope, haModal, haGlobals, $timeout, geo) {
			$scope.AgentCode = '';
			$scope.PurchaseMilesPayment = { EmailAddress: '' };
			$scope.submitting = false;
			$scope.giftCardState = {};
			$scope.giftCards = [];

			haGlobals(['purchaseMilesPaymentJson', 'totalCost'], function (purchaseMilesPaymentJson, totalCost) {
				if (purchaseMilesPaymentJson) {
					$scope.PurchaseMilesPayment.EmailAddress = purchaseMilesPaymentJson.EmailAddress;
				}
				if (totalCost) {
					$scope.giftCardState.balance = totalCost;
				}
			});

			$scope.hasGiftCardAndBalance = function() {
				return $scope.giftCardState.balance > 0 && $scope.giftCards.length;
			};

			$scope.showPrivacyPolicy = function () {
				haModal('', {
					id: 'privacy-policy',
					backdrop: 'true',
					template: angular.element('.showPrivacyPolicy')
				});
			};

			$scope.showTerms = function () {
				haModal('', {
					id: 'terms-and-conditions',
					backdrop: 'true',
					template: angular.element('.showTerms')
				});
			};

			$scope.getHelpContent = function () {
				haModal('', {
					id: 'purchase-miles-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.onSubmitForm = function () {
				// ... all other internal processing on submit
				/* global userdata */
				/* jshint -W106 */
				userdata.load_data('user_data');
				/* jshint +W106 */
				$scope.submitting = true;
				return true;
			};

			$scope.cancelPayment = function () {
				/* global itinDetailsPath */
				window.location = itinDetailsPath;
			};

			// for PCI Payment Authorization
			$scope.haPaymentTypes = {
				paymentMethod: 'creditDebit'
			};

			$scope.CDESubmit = function () {
				$rootScope.$broadcast('CDESubmit');
			};

			$scope.$on('CDEPaymentSubmitted', function() {
				$scope.submitting = true;
			});

			$scope.$on('CDEPaymentSubmitError', function() {
				$scope.submitting = false;
				$timeout(function() {
					$('html, body').animate({ scrollTop: $('#formIFrame').offset().top - 50 }, 'fast');
				}, 100);
			});
			// end PCI

            // contact and travel coordinator info
			$scope.countries = geo.setCountryData(countryData);
			$scope.countryPhoneDropdown = geo.getPhoneCountryCodes();

			$scope.phoneCount = 1;
			$scope.PhoneDetails = [];

			$scope.phoneTypes = [{
				name: '',
				value: 1
			}, {
				name: '',
				value: 2
			}, {
				name: '',
				value: 3
			}];

			$scs.get('PassengerInfo').then(function (data) {
				$scope.phoneTypes[0].name = data.hometext;
				$scope.phoneTypes[1].name = data.worktext;
				$scope.phoneTypes[2].name = data.mobiletext;
			});

			$scope.PhoneDetails[0] = {
				CountryCode: window.defaultPhoneCountryCode,
				Number: '',
				Type: 1
			};

			$scope.PhoneDetails[1] = {
				CountryCode: window.defaultPhoneCountryCode,
				Number: '',
				Type: 1
			};

			$scope.PhoneDetails[2] = {
				CountryCode: window.defaultPhoneCountryCode,
				Number: '',
				Type: 1
			};

			$scope.travelCoordinator = false;

			$scope.TravelCoordinatorInfo = {
				TravelCoordinatorPhoneDetails: {
					CountryCode: window.defaultPhoneCountryCode,
					Number: ''
				}
			};

			$scope.phoneRegexByCC = function (cc) {
				var country = geo.lookupCountryByCode(cc);
				if (country != null) {
					return geo.getPhoneNumberRegex(country.Key);
				}
				return /.*/;
			};

			$scope.addPhone = function () {
				$scope.phoneCount++;
				$timeout(function () {
					$('select[name="PhoneType' + ($scope.phoneCount - 1) + '"]').focus();
				});
			};

			$scope.deletePhone = function () {
				$scope.phoneCount--;
				$timeout(function () {
					$('select[name="PhoneType' + ($scope.phoneCount - 1) + '"]').focus();
				});
			};

			$scope.$watch('PhoneDetails[0].CountryCode', function (n, o) {
				$scope.PhoneDetails[0].Number = (!o || n === o) ? $scope.PhoneDetails[0].Number : '';
			});

			$scope.$watch('PhoneDetails[1].CountryCode', function (n, o) {
				$scope.PhoneDetails[1].Number = (!o || n === o) ? $scope.PhoneDetails[1].Number : '';
			});

			$scope.$watch('PhoneDetails[2].CountryCode', function (n, o) {
				$scope.PhoneDetails[2].Number = (!o || n === o) ? $scope.PhoneDetails[2].Number : '';
			});

			$scope.$watch('TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.CountryCode', function (n, o) {
				$scope.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.Number = (!o || n === o) ? $scope.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.Number : '';
			});

			$scope.$watch('phoneCount', function (n) {
				$scope.PhoneDetails.forEach(function (p, idx, arr) {
					arr[idx] = (n < idx + 1) ? {CountryCode: window.defaultPhoneCountryCode, Number: '', Type: 1} : arr[idx];
				});
			});
			// end contact info

		};

		HaMyTripsPaymentController.$inject = ['$scope', '$rootScope', 'haModal', 'haGlobals', '$timeout', 'haGeoDataSvc'];

		return {
			restrict: 'A',
			scope: true,
			controller: HaMyTripsPaymentController
		};
	});

})(angular);
;
(function (angular) {

    // Ha Manage Travelers
    // --------------------------------------------
    //
    // * **Class:** HaManageTravelers
    // * **Author:** Curtis Floth
    //
    // Manage Travelers

    'use strict';

    var module = angular.module('haManageTravelersModule', ['haTravelersAPI']);

    module.directive('haManageTravelers', ['$timeout', 'haGlobals', 'haConfig', '$log',
		function ($timeout, haGlobals, haConfig, $log) {

		    var HaManageTravelersController = function ($scope, haModal, $rootScope) {


		        $scope.TravelerLists = [];
		        $scope.FilteredTravelersList = [];
		        $scope.VM = {
		            MaxTravelersCount: 20,
		            InitialTravelerDisplayCount: 5
		        };
		        $scope.maxTravelersReachedMsg = angular.element('#maxTravelersReachedMsg').text();

		        haGlobals('jsonTravelerListsModel', function (jsonTravelerListsModel) {

		            $scope.VM = jsonTravelerListsModel;
		            $rootScope.ShowProfileImageDropdown = jsonTravelerListsModel.ShowProfileImageDropdown;

		            if (jsonTravelerListsModel.TravelersList.length > 0) {

		                jsonTravelerListsModel.TravelersList.sort(function (a, b) {
		                    var aName = a.LastName.toLowerCase() + ' ' + a.FirstName.toLowerCase();
		                    var bName = b.LastName.toLowerCase() + ' ' + b.FirstName.toLowerCase();

		                    if (aName < bName) {
		                        return -1;
		                    }
		                    if (aName > bName) {
		                        return 1;
		                    }
		                    return 0;
		                });

		                $scope.TravelerLists = jsonTravelerListsModel.TravelersList;

		                // sort profile images so index matches ID (why doesn't it come down like this?)
		                $scope.VM.ProfileImagesList.unshift({}); // add empty object at 0 index
		                $scope.VM.ProfileImagesList.sort(function (a, b) {
		                    return Number(a.ID) - Number(b.ID);
		                });
		                // make sure a value is present for avatar ID in each traveler
		                // add property for avatar image url
		                for (var i = 0; i < $scope.TravelerLists.length; i++) {
		                	var pax = $scope.TravelerLists[i];
		                    if (!pax.AvatarURL) {
		                        if (pax.AvatarID.length === 0) {
		                            //$log.debug('user did not have av id at index: ' + i);
		                            pax.AvatarID = '1';
		                        }
		                        var avIndex = Number(pax.AvatarID);
		                        for (var k = 0; k < $scope.VM.ProfileImagesList.length; k++) {
		                            if (avIndex === Number($scope.VM.ProfileImagesList[k].ID)) {
		                                pax.AvatarURL = $scope.VM.ProfileImagesList[k].Src;
		                            }
		                        }
		                    }
		                    // check for incomplete info
		                    if (!pax.DateOfBirth ||
								!pax.FirstName ||
								!pax.LastName ||
								!pax.Gender) {
								pax.IsIncomplete = true;
		                }
		                }

		                $scope.FilteredTravelersList = $scope.TravelerLists.splice(0, $scope.VM.InitialTravelerDisplayCount);
		            }
		        });


		        ////////////////////
		        // SCOPE METHODS //
		        $scope.DisplayLoadMore = function () {
		            return $scope.TravelerLists.length > 0;
		        };

		        $scope.ShowAdditionalPax = function () {
		            var newItems = $scope.TravelerLists.splice(0, $scope.VM.InitialTravelerDisplayCount);
		            $scope.FilteredTravelersList.push.apply($scope.FilteredTravelersList, newItems);
		            angular.element('#showAdditionalPax').blur();
		        };

		        $scope.showTerms = function () {
		            haModal(haConfig.getTemplateUrl('ha-account-registration/ha-terms-conditions-modal.html'), {
		                id: 'registration-privacy',
		                backdrop: 'true'
		            });
		        };

		        $scope.addEditPax = function (traveler) {
		            var hasHmNum = traveler && traveler.HMAccountNo ? 'Yes' : 'No';
		            var avatarId = '';

		            traveler = $.extend({}, traveler, { HasHMNumber: hasHmNum });

		            if (Object.keys(traveler).length === 1) {
		                if ($scope.FilteredTravelersList.length + $scope.TravelerLists.length >= $scope.VM.MaxTravelersCount) {
		                    $scope.travelerlisterrortype = 'info';
		                    $scope.travelerlisterrordescription = $scope.maxTravelersReachedMsg.trim();
		                    $scope.ShowTravelersAlert = true;
		                    $scope.$broadcast('$showAlert');
		                    return;
		                }
		            }
		            else {
		                avatarId = traveler.AvatarID;
		            }

		            $rootScope.UpdateTraveler = angular.copy(traveler);

		            haModal('/MyAccount/TravelersList/TravelerAddEdit?AvatarID=' + avatarId, {
		                id: 'add-edit-traveler',
		                backdrop: false,
		                extendScope: { isBooking: !!$rootScope.isBooking }
		            });
		        };

		        $scope.ShowMessage = function (response) {
		            var message;

		            var responseMessage = response.Message;
		            var messageSpeedyEnroll = '';

		            var index = responseMessage.indexOf('|');

		            if (index > 0) {
		                message = responseMessage.substring(0, index);

		                $scope.ShowSpeedyEnrollAlert = true;
		                messageSpeedyEnroll = responseMessage.substring(index + 1);

		                if (messageSpeedyEnroll.indexOf('ERROR') < 0) {
		                    $scope.messageSpeedyEnroll = messageSpeedyEnroll;
		                    $scope.speedyMessageType = 'success';
		                } else {
		                    $scope.speedyMessageType = 'error';
		                    $scope.messageSpeedyEnroll = messageSpeedyEnroll.substring(5, messageSpeedyEnroll.length);
		                }

		            } else {
		                message = responseMessage;
		            }

		            $scope.travelerlisterrortype = 'success';
		            $scope.travelerlisterrordescription = message;

		            $scope.ShowTravelersAlert = true;

		            $scope.$broadcast('$showAlert');
		        };

		        $scope.RedirectToLoginPage = function (response) {
		            if (response != null && response.data != null && response.data.RedirectURL != null) {
		                window.location.href = response.data.RedirectURL;
		            }
		            else {
		                window.location.href = window.location.href;
		            }
		        };

		        $scope.stringToDate = function (dateStr) {
		            var theDate = new Date(dateStr);
		            return theDate;
		        };

		        ////////////
		        // EVENTS //
		        $scope.$on('TravelerDeleted', function (event, data) {
		            var travelerID = data.travelerID;
		            var response = data.response;
		            $scope.ShowMessage(response);
		            var item = $($scope.FilteredTravelersList).filter(function () {
		                return this.TravelerID === travelerID;
		            }).get(0);

		            if (item) {
		                $scope.FilteredTravelersList.splice($scope.FilteredTravelersList.indexOf(item), 1);
		            }
		        });

		        $scope.$on('TravelerAdded', function (event, data) {
		            $timeout(function () {
		                var response = data.response;
		                $scope.ShowMessage(response);
		                $scope.FilteredTravelersList.splice(0, 0, response.Data);
		            }, 0);
		        });

		        $scope.$on('TravelerModified', function (event, data) {

		            $timeout(function () {

		                var response = data.response;
		                $scope.ShowSpeedyEnrollAlert = false;
		                $scope.ShowMessage(response);

		                var item = $($scope.FilteredTravelersList).filter(function () {
		                    return this.TravelerID === response.Data.TravelerID;
		                }).get(0);

		                if (item) {
		                    $scope.FilteredTravelersList.splice($scope.FilteredTravelersList.indexOf(item), 1, response.Data);
		                }
		            }, 0);
		        });

		        $scope.$on('SessionError', function () {
		            window.location.href = window.location.href;
		        });

		        $scope.$on('responseError:UNAUTHORIZED', function (event, response) {
		            $scope.RedirectToLoginPage(response);
		        });

		        $scope.$on('responseError:FORBIDDEN', function (event, response) {
		            $scope.RedirectToLoginPage(response);
		        });
		    };

		    HaManageTravelersController.$inject = ['$scope', 'haModal', '$rootScope'];

		    return {
		        restrict: 'A',
		        scope: true,
		        controller: HaManageTravelersController
		    };
		}
    ]);

    module.directive('haManageTravelersAddEdit', ['haGlobals', 'haTravelersAPI', '$log', 'haUtils', '$interval', 'haSitecoreStrings',
		function (haGlobals, haTravelersAPI, $log, haUtils, $interval, $scs) {

			var HaManageTravelersAddEditController = function ($scope, haModal, $rootScope, $attrs) {

				$scope.EmailEnrollError = false;
				$scope.isEdit = false;
				$scope.errorMessage = '';
				$scope.TravelerInfo = {};
				$scope.dobDayDataSource = [];
				$scope.dayDataSource = [];
				$scope.dobMonthDataSource = [];
				$scope.monthDataSource = [];
				$scope.birthDay = '';
				$scope.monthPlaceHolder = null;
				$scope.ShowProfileImageDropdown = $rootScope.ShowProfileImageDropdown;
				$scope.DOBMonth = '';
				$scope.DOBDay = '';
				$scope.DOBYear = '';

				var dob;

				/////////////
				// GLOBALS //
				haGlobals(['jsonManageTravelerAddEditModel', 'hideSeatUpgrade'], function (jsonManageTravelerAddEditModel, hideSeatUpgrade) {
					if ($rootScope.UpdateTraveler && Object.keys($rootScope.UpdateTraveler).length > 1) {
						$scope.ManageTravelerAddEditModel = $rootScope.UpdateTraveler;
					} else {
						$scope.ManageTravelerAddEditModel = $.extend(jsonManageTravelerAddEditModel, $rootScope.UpdateTraveler);
					}

					angular.forEach(jsonManageTravelerAddEditModel.DOBDayDropDown, function (item) {
						$scope.dayDataSource.push({
							name: item.Name,
							value: item.Value
						});
						$scope.dobDayDataSource.push({
							name: item.Name,
							value: item.Value
						});
					});

					$scope.monthDataSource = jsonManageTravelerAddEditModel.DOBMonthDropDown.map(function (item) {
						return {name: item.Name, value: item.Value};
					});
					$scope.monthDataSource.shift();
					$scope.dobMonthDataSource = jsonManageTravelerAddEditModel.DOBMonthDropDown.map(function (item) {
						return {name: item.Name, value: item.Value};
					});
					$scope.dobMonthDataSource.shift();
					$scope.hideSeatUpgrade = hideSeatUpgrade;
				});


				////////////
				// INLINE //
				$scope.seatPreferences = [];
				$scs.get('SEATPREFERENCE_DROPDOWN').then(function(data) {
					$scope.seatPreferences = data;
				});
		
				if ($scope.ManageTravelerAddEditModel && $scope.ManageTravelerAddEditModel.DateOfBirth) {

					dob = $scope.ManageTravelerAddEditModel.DateOfBirth.split('/');
					if (dob.length === 3) {
						$scope.DOBMonth = dob[0];
						$scope.DOBDay = dob[1];
						$scope.DOBYear = dob[2];
					}
				}

				if ($rootScope.UpdateTraveler && Object.keys($rootScope.UpdateTraveler).length > 1) {
					$scope.TravelerInfo = $rootScope.UpdateTraveler;

					// convert HMAccountNo to string to be displayed correctly
					if ($scope.TravelerInfo.HMAccountNo && typeof $scope.TravelerInfo.HMAccountNo !== 'string') {
						$scope.TravelerInfo.HMAccountNo = $scope.TravelerInfo.HMAccountNo.toString();
					}

					$scope.isEdit = $scope.TravelerInfo.TravelerID > 0 || $scope.TravelerInfo.TravelerId > 0 || ($scope.isBooking && !$rootScope.UpdateTraveler.SaveForFutureBooking);

					if ($rootScope.UpdateTraveler.DateOfBirth && $rootScope.UpdateTraveler.DateOfBirth != null && $rootScope.UpdateTraveler.DateOfBirth !== '') {

						dob = $rootScope.UpdateTraveler.DateOfBirth.split('/');
						if (dob && dob.length === 3) {
							$scope.DOBMonth = dob[0];
							$scope.DOBDay = dob[1];
							$scope.DOBYear = dob[2];
						}
					}
					$rootScope.UpdateTraveler = null;
					if ($scope.TravelerInfo.IsIncomplete) {
						// indicate missing fields for incomplete traveler
						var formEl = $interval(function() {
							if ($('[name=ManageTravelerForm]').length) {
								$('[name=ManageTravelerForm]').addClass('submitted');
								$interval.cancel(formEl);
							}
						}, 100);
					}
				} else {
					haGlobals('jsonManageTravelerAddEditModel', function (jsonManageTravelerAddEditModel) {
						$scope.TravelerInfo = jsonManageTravelerAddEditModel;

						// Set permSave in my-account guests or for all individual account types
						var permSave = $scope.isBooking || ($rootScope.individualAccTypes.indexOf($rootScope.user.accountType) > -1);

						if ($scope.isBooking && ($rootScope.corpAccTypes.indexOf($rootScope.user.accountType) > -1)) {
							var checkCookie = haUtils.readCookie('PermanentSaveCookie');
							if (checkCookie !== undefined) {
								checkCookie = decodeURIComponent(checkCookie);
								permSave = JSON.parse(checkCookie);
							}
						}

						$scope.TravelerInfo.SaveForFutureBooking = permSave;

						if ($scope.TravelerInfo.SeatPreferences === undefined) {
							$scope.TravelerInfo.SeatPreference = '1';
						}

						if ($scope.TravelerInfo.IsIncomplete) {
							// indicate missing fields for incomplete traveler
							var formEl = $interval(function() {
								if ($('[name=ManageTravelerForm]').length) {
									$('[name=ManageTravelerForm]').addClass('submitted');
									$interval.cancel(formEl);
								}
							}, 100);
						}
					});
				}


				////////////////////
				// SCOPE METHODS //
				$scope.trim = function (e) {
					e.target.value = e.target.value.replace(/\s/g, '');
				};

				$scope.getDOB = function (ti) {

					ti = ti || $scope;

					return [ti.DOBMonth, ti.DOBDay, ti.DOBYear].join('/');
				};

				$scope.birthDay = $scope.getDOB();

				$scope.doneAddEditTraveler = function (formName) {

					if (!formName) {
						return;
					}

					$rootScope.corpAccTypes = ['C', 'R', 'W', 'A'];

					// For Corporate Users, when adding a passenger in the dashboard (ie, not booking path) we must check the hidden "Save for future" checkbox otherwise the passenger will not get saved
					if ($scope.$root.isLoggedIn && $scope.$root.corpAccTypes.indexOf($scope.$root.user.accountType) > -1 && !$scope.isBooking) {
						$('#SaveForFutureBooking').prop('checked', true);
					}

					if ($scope.DOBYear) {
						dob = $scope.getDOB();
					} else if ($scope.ManageTravelerAddEditModel) {
						dob = $scope.getDOB({
							DOBMonth: $scope.ManageTravelerAddEditModel.DOBMonth,
							DOBDay: $scope.ManageTravelerAddEditModel.DOBDay,
							DOBYear: $scope.ManageTravelerAddEditModel.DOBYear
						});
					}
					var tid = $scope.ManageTravelerAddEditModel.TravelerID || $scope.ManageTravelerAddEditModel.TravelerId;

					// TODO: Convert form serialization to Angular
					$('input[name="DateOfBirth"]').val(dob);
					$('input[name="TravelerID"]').val(tid);

					if (formName === 'ManageTravelerForm' && !$scope.processing) {
						$scope.processing = true;
						$scope.errorMessage = '';

						var frm = $('#' + formName);

						// Save the traveler
						haTravelersAPI.TravelerAddEdit(frm.serialize()).success(function (response) {

							if (response.IsSuccess) {
								if ($scope.ManageTravelerAddEditModel.HMNumber && $scope.ManageTravelerAddEditModel.TravelerID === 0 && !response.Data.IsValidHMNumber) {
									$scope.errorMessage = HA.SCStrings.passengerinfo.hawaiianmilesnotexistmessage;
									$scope.$broadcast('$showAlert');
									$('#add-edit-traveler').animate({scrollTop: 0}, 'slow');
									return;
								}

								var isEdit = !!($scope.TravelerInfo.TravelerID || $scope.TravelerInfo.TravelerId);
								$rootScope.$broadcast(isEdit ? 'TravelerModified' : 'TravelerAdded', {
									'response': response
								});
								$scope.$emit('closeModal');

								if (!isEdit) {
									var d = new Date();
									d.setTime(d.getTime() + (365 * 24 * 60 * 60 * 1000));
									var expires = "expires=" + d.toUTCString();

									// Save the "SaveForFutureBooking" checkbox value to the user's cookies to persist in the future
									if (HA.CookiesRequireSsl)
									{
									    document.cookie = ['PermanentSaveCookie', '=', $scope.TravelerInfo.SaveForFutureBooking, '; secure; ', expires, '; path=/'].join('');
									}
									else
									{
									    document.cookie = ['PermanentSaveCookie', '=', $scope.TravelerInfo.SaveForFutureBooking, '; ', expires, '; path=/'].join('');
									}
								}

							} else {
								$scope.errorMessage = response.Message;
								$scope.$broadcast('$showAlert');
								$('#add-edit-traveler').animate({scrollTop: 0}, 'slow');
							}
						}).error(function (xhr, httpStatusCode) {
							if (httpStatusCode === 403 || httpStatusCode === 401) {
								$rootScope.$broadcast('SessionError');
							}
						})['finally'](function () {
							$scope.processing = false;
						});
					}
				};

				$scope.DeleteTraveler = function (travelerID) {
					haTravelersAPI.TravelerDelete(travelerID).success(function (response) {
						if (response.IsSuccess) {
							$scope.$emit('closeModal');
							$rootScope.$broadcast('TravelerDeleted', {
								'response': response,
								'travelerID': travelerID
							});
						} else {
							$scope.errorMessage = response.Message;
						}
					}).error(function (xhr, httpStatusCode) {
						if (httpStatusCode === 403) {
							$rootScope.$broadcast('SessionError');
						}
					});
				};
			};

			HaManageTravelersAddEditController.$inject = ['$scope', 'haModal', '$rootScope', '$attrs'];
			return {
				restrict: 'A',
				scope: true,
				controller: HaManageTravelersAddEditController
			};

		}
	]);

})(angular);
;
(function (angular) {

	// Ha Checkout
	// --------------------------------------------
	//
	// * **Class:** HaCheckout
	// * **Author:** Cory Shaw
	//
	// This is the main module for the review and pay page in the booking path

	'use strict';

	var module = angular.module('haCheckoutModule', ['haInterstitialAPI', 'haPaymentAPI', 'haUplift']);

	module.directive('haCheckout', ['haGlobals', 'haPaymentAPI', '$window', 'haUplift', 'haAncillariesAPI', function (haGlobals, haPaymentAPI, $window, haUplift, haAncillariesAPI) {

		var HaCheckoutController = [
			'$scope', 'haInterstitialAPI', 'haModal', 'haPassengersService', '$rootScope', 'haAncillariesSvc', '$timeout', 'haHttpService', 'haConfig', 'haPaymentTypesService', '$log', '$filter',
			function ($scope, haInterstitialAPI, haModal, $pax, $rootScope, aSvc, $timeout, haHttpService, haConfig, haPaymentTypesSvc, $log, $filter) {

				$scope.$emit('$haCheckoutReady');

				haGlobals('paymentVm', function (paymentVm, enableTCR) {

					$.extend($scope, paymentVm);
					$scope.currency = $scope.TripSummary.currency;
					$scope.PricingType = $scope.TripSummary.PricingType;
					$scope.SelectedHotel = $scope.TripSummary.SelectedHotel;
					$scope.enableTCR = enableTCR;
					$rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off
				});

				$scope.seatSelection = $scope.TripSummary.SelectedSeatInfo;
				$scope.$pax = $pax;
				$scope.$pax.passengers = [];
				$scope.selectedSegments = [];
				$scope.SelectedFlightInfo = [];
				$scope.IsPNRError = false;
				$scope.AddPassengerList = function (count, type) {
					for (var i = 0; i < count; i++) {
						$scope.$pax.add({ type: type, isUser: true });
					}
				};
				$scope.taxDetails = false;
				$scope.IsPNRCreated = false;
				$scope.IsServiceErrors = false;
				$scope.ServiceErrorMessage = '';
				$scope.IsContinuePayment = false;
				$scope.form = [];
				$scope.flag = false;
				$scope.errorType = null;
				$scope.Token = '';
				$scope.secondPhoneMandatory = false;
				$scope.thirdPhoneMandatory = false;
				$scope.firstPhoneMandatory = true;
				$scope.tripSummaryWhiteBG = true;

				$scope.secondCountryMandatory = false;
				$scope.thirdCountryMandatory = false;
				$scope.firstCountryMandatory = false;
				$scope.noExistingPhoneNumbers = false;

				$scope.holdReservationRemoved = false;

				$scope.paymentForm = {};
				var totalGiftCardsAllowed = 4;
				$scope.giftCardResponse = {};
				$scope.giftCardResponse.GiftCards = [];
				$scope.paymentForm.giftCardForm = {};
				$scope.giftCardFormData = {};
				$scope.paymentForm.isBarclayReturned = false;
				$scope.paymentForm.isAlipayPaymentSuccess = false;

				var maxPhoneLength = 10;
				var maxPhoneArray = [];
				var minPhoneLength = 0;
				var minPhoneArray = [];
				var countryCodeDropDown = 'not-set';


				$scope.getCurrencySymbol = function () {
					return (0).toLocaleString(
						'en-US',
						{
							style: 'currency',
							currency: $scope.currency,
							minimumFractionDigits: 0,
							maximumFractionDigits: 0
						}
					).replace(/\d/g, '').trim()
				}

				haGlobals(['paymentVm', 'defaultPhoneCountryCode'], function (paymentVm, defaultPhoneCountryCode) {
					$scope.UpdatePhoneDetailsValidation = paymentVm.ContactInfo;
					$scope.showphone2 = false;
					$scope.showphone3 = false;

					//-starting phone number
					$scope.PhoneDetails = $scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.PhoneDetails;
					countryCodeDropDown = $scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.CountryCodeDropDown;
					maxPhoneArray.length = 0;
					angular.forEach($scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.CountryCodeDropDown, function (o) {
						maxPhoneArray.push(o.MaxPhoneLength);
					});


					// Travel Coordinator Init
					if (paymentVm.ContactInfo.TravelCoordinatorInfo != null) {
						if (paymentVm.ContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails == null) {
							paymentVm.ContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails = {
								CountryCode: defaultPhoneCountryCode,
								Number: '',
								Type: ''
							};
						}
						else {
							if ($.isNumeric(paymentVm.ContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.CountryCode)) {
								var countryCode = $($scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.CountryCodeDropDown).filter(function () {
									return typeof (this) !== 'undefined' && this.Name === '+' + paymentVm.ContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.CountryCode;
								});

								if (countryCode.length > 0) {
									paymentVm.ContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.CountryCode = countryCode[0].Value;
								} else {
									paymentVm.ContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.CountryCode = defaultPhoneCountryCode;
								}
							}
							else {
								paymentVm.ContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.CountryCode = defaultPhoneCountryCode;
							}
						}
					}

					//maxPhoneArray = $scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.CountryCodeDropDown.map(function (o) { return o.MaxPhoneLength; });
					maxPhoneLength = Math.max.apply(this, maxPhoneArray);

					var filteredPhoneDetails = $.makeArray($($scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.CountryCodeDropDown).filter(function () {
						return typeof (this) !== 'undefined' && this.Value !== '';
					}));
					minPhoneArray.length = 0;
					angular.forEach(filteredPhoneDetails, function (o) {
						minPhoneArray.push(o.MinPhoneLength);
					});
					//minPhoneArray = $.makeArray($($scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.CountryCodeDropDown).filter(function () {
					//    return typeof (this) !== 'undefined' && this.Value !== '';
					//})).map(function (o) { return o.MinPhoneLength; });

					if ($scope.PhoneDetails.length === 0) {
						$scope.PhoneDetails.push({ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' });
						$scope.PhoneDetails.push({ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' });
						$scope.PhoneDetails.push({ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' });
						$scope.noExistingPhoneNumbers = true;
					}
					else if ($scope.PhoneDetails.length === 1) {
						$scope.PhoneDetails.push({ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' });
						$scope.PhoneDetails.push({ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' });
					}
					else if ($scope.PhoneDetails.length === 2) {
						$scope.PhoneDetails.push({ CountryCode: defaultPhoneCountryCode, Number: '', Type: '' });
						$scope.showphone2 = true;
					}
					else if ($scope.PhoneDetails.length === 3) {
						$scope.showphone2 = true;
						$scope.showphone3 = true;
					}

					// mock gift card VM
					//paymentVm.GiftCardState = 2;					

					$scope.haPaymentTypes = haPaymentTypesSvc;
					$scope.haPaymentTypes.updatePaymentTypes();
					// make credit/debit pre-selected per pbi 52009
					$scope.haPaymentTypes.paymentMethod = 'creditDebit';

					if (paymentVm.MaxGiftCardAllowed) {
						totalGiftCardsAllowed = paymentVm.MaxGiftCardAllowed;
					}
					if (paymentVm.IsBarclayReturn) {
						$scope.paymentForm.isBarclayReturned = true;
					}

					if (paymentVm.IsBarclayReturn) {
						$scope.paymentForm.isBarclayReturned = true;
					}

					if (isAlipayPaymentSuccess) {
						$scope.paymentForm.isAlipayPaymentSuccess = true;
						$scope.haPaymentTypes.paymentMethod = 'alipay';
						$scope.paymentMethod = 'alipay';
					}

				});


				minPhoneLength = Math.min.apply(this, minPhoneArray);

				$scope.phoneOneMinLength = minPhoneLength;
				$scope.phoneOneMaxLength = maxPhoneLength;

				$scope.phoneSecondMinLength = minPhoneLength;
				$scope.phoneSecondMaxLength = maxPhoneLength;

				$scope.phoneThirdMinLength = minPhoneLength;
				$scope.phoneThirdMaxLength = maxPhoneLength;

				$scope.tcPhoneMinLength = minPhoneLength;
				$scope.tcPhoneMaxLength = maxPhoneLength;

				// CDE custom CDE Submit Fail error
				if (window.sessionStorage.getItem('showCDESubmitFailError')) {
					window.sessionStorage.removeItem('showCDESubmitFailError');
					$scope.ShowCDEFormSubmitFailError = true;
				}

				$scope.showSeatMap = function () {
					haModal(haConfig.getTemplateUrl('VerticalSeatmap/ha-vertical-seatmap-preview.html'), {
						backdrop: 'true',
						id: 'ha-vertical-seatmap-preview-modal',
						extendScope: {
							previewSegments: $scope.PassengerTripSummary.allSegments,
							SignifiedMarket: $scope.SignificantMarket,
							enableTCR: $scope.enableTCR,
							disableSeatUpgrades: false // Show the complete seat map.
						}
					});
				};
				$scope.upgradeToMainCabin = function () {
					haPaymentAPI.upgradeToMainCabin()
						.then(function () {
							document.body.dispatchEvent(new CustomEvent('UpgradeToMainCabin', {
								'detail': {
									'pageName': window.digitalData.page.pageInfo.name
								}
							}));
							$window.location.href = '/Book/InflightOptions?#upgraded';
						});
				};

				$scope.getValueOfCountryCode = function (newValue) {
					if (newValue) {
						var countryCode = $(countryCodeDropDown).filter(function () {
							return typeof (this) !== 'undefined' && this.Name === newValue;
						});

						if (countryCode.length) {
							return countryCode[0].Value;
						}
						else {
							return "";
						}
					}
					else {
						return "";
					}
				};

				$scope.getMaxAndMinPhoneLength = function (newValue) {
					if (newValue && newValue !== '') {
						var countryCode = $(countryCodeDropDown).filter(function () {
							return typeof (this) !== 'undefined' && this.Value === newValue;
						});
						if (countryCode.length) {
							return { minLength: countryCode[0].MinPhoneLength, maxLength: countryCode[0].MaxPhoneLength };
						}
						else {
							return { minLength: minPhoneLength, maxLength: maxPhoneLength };
						}
					}
					else {
						return { minLength: minPhoneLength, maxLength: maxPhoneLength };
					}
				};

				$scope.$watch('PhoneDetails[0].CountryCode', function (newValue) {
					var minAndMaxPhoneLength = $scope.getMaxAndMinPhoneLength(newValue);
					$scope.phoneOneMaxLength = minAndMaxPhoneLength.maxLength;
					$scope.phoneOneMinLength = minAndMaxPhoneLength.minLength;
					$scope.setFirstPhonemandatory();
				});

				$scope.setFirstPhonemandatory = function () {
					if (($scope.PhoneDetails[0].CountryCode != null) &&
						($scope.PhoneDetails[0].Number != null && $scope.PhoneDetails[0].Number !== '')) {
						if (!($scope.secondPhoneMandatory && $scope.thirdPhoneMandatory)) {
							$scope.firstPhoneMandatory = true;
						}
					}
					else if ($scope.noExistingPhoneNumbers) {
						$scope.firstPhoneMandatory = true;
					}
					else {
						$scope.firstPhoneMandatory = false;
					}
				};

				$scope.$watch('PhoneDetails[0].Number', function (/* newValue */) {
					$scope.setFirstPhonemandatory();
				});

				$scope.$watch('PhoneDetails[1].CountryCode', function (newValue) {
					var minAndMaxPhoneLength = $scope.getMaxAndMinPhoneLength(newValue);
					$scope.phoneSecondMaxLength = minAndMaxPhoneLength.maxLength;
					$scope.phoneSecondMinLength = minAndMaxPhoneLength.minLength;
					$scope.setSecondPhonemandatory();
				});

				$scope.$watch('PhoneDetails[1].Number', function (/* newValue */) {
					$scope.setSecondPhonemandatory();
				});


				$scope.$watch('ContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.CountryCode', function (newValue) {
					var minAndMaxPhoneLength = $scope.getMaxAndMinPhoneLength(newValue);
					$scope.tcPhoneMaxLength = minAndMaxPhoneLength.maxLength;
					$scope.tcPhoneMinLength = minAndMaxPhoneLength.minLength;
				});

				/////////////////////////////////////////////////////////////////////
				// GIFT CARD LOGIC
				// giftCard Payment Method Logic
				$scope.giftCardFormData.cards = [
					{ "Number": "", "Pin": "", "Action": 0 }
				];

				$scope.setPaymentMethod = function (type) {
					$scope.paymentMethod = type;
				};

				$scope.$on('ancelaryStateChange', function () {
					// this is required to update the balance needed to be paid by CC when
					// the user adds/removes an ancillary (e.g.: trip ins, shuttle)
					if ($scope.PassengerTripSummary) {
						var grandTotal = $scope.PassengerTripSummary.grandTotal;
						var giftCardsTotal = $scope.giftCardResponse.GiftCards.reduce(function (sum, card) { return sum + card.AmountApplied; }, 0);
						$scope.giftCardResponse.BookingBalance = grandTotal - giftCardsTotal;
						$scope.giftCardResponse.ShowCreditCardBalance = $scope.giftCardResponse.BookingBalance
					}
				});

				$scope.addCard = function () {
					if (($scope.giftCardFormData.cards.length + $scope.giftCardResponse.GiftCards.length) < totalGiftCardsAllowed) {
						$scope.giftCardFormData.cards.push({ "Number": "", "Pin": "", "Action": 0 });
						if (($scope.giftCardFormData.cards.length + $scope.giftCardResponse.GiftCards.length) === totalGiftCardsAllowed) {
							$scope.maxCardsReached = isMaxedCardsReached();
						}
						$timeout(function () {
							$('input[name="number' + ($scope.giftCardFormData.cards.length - 1) + '"]').first().focus();
						}, 0);
					}
				};

				$scope.hasGiftCardAndBalance = function() {
					return $scope.giftCardResponse.GiftCards.length && $scope.giftCardResponse.BookingBalance > 0;
				};

				var isMaxedCardsReached = function () {
					if (($scope.giftCardFormData.cards.length + $scope.giftCardResponse.GiftCards.length) === totalGiftCardsAllowed) {
						return true;
					} else {
						return false;
					}
				};

				var submitCards = function (cardData) {
					$scope.checkingCards = true;

					// check for empty fields, remove from array if empty before submitting to the server
					if (cardData.GiftCards.length > 1) {
						angular.forEach(cardData.GiftCards, function (obj, index) {
							if (!obj.Number) {
								cardData.GiftCards.splice(index, 1);
							}
						});
					}

					haHttpService.POST('/myaccount/giftcard/ProcessGiftCard', cardData).success(function (data) {
						$scope.checkingCards = false;
						if (data.GiftCards) {
							$log.debug(data);
							$scope.giftCardResponse = data;
							$scope.giftCardFormData.cards = [];
							$scope.maxCardsReached = isMaxedCardsReached();
							$scope.hasAppliedGiftCard = true;

							// if you've deleted all your cards, bring the form back
							if ($scope.giftCardResponse.GiftCards.length === 0) {
								$scope.hasAppliedGiftCard = false;
								$scope.giftCardFormData.cards = [
									{ "Number": "", "Pin": "", "Action": 0 }
								];
							}

						} else {
							$log.warn('error submitting gift cards');
						}
					}).error(function (data, status) {
						$scope.checkingCards = false;
						$log.error('error saving travel goal: ' + status);
					});
				};

				$scope.applyGiftCards = function ($event) {
					$event.preventDefault();
					$scope.paymentForm.giftCardForm.$validate($scope.paymentform);
					if ($scope.paymentForm.giftCardForm.$valid) {
						var cardData = { "GiftCards": $scope.giftCardFormData.cards };
						if (cardData.GiftCards.length > 0) {
							submitCards(cardData);
						}
					} else {
						$('.ng-invalid:not(form):not(div):first').focus().select();
					}
				};

				$scope.removeGiftCard = function (card) {
					card.Action = 1;
					var cardData = { "GiftCards": [card] };
					submitCards(cardData);
				};

				$scope.showGiftCardModal = function (type) {
					$scope.giftCardModalType = type;
					haModal(haConfig.getTemplateUrl('ha-checkout-giftcard-modals.html'), {
						id: 'giftcard-modal',
						backdrop: 'true',
						scope: $scope
					});
				};

				function isPurchasingTripInsurance() {
					return $scope.TripSummary.TripInsurance &&
						($scope.TripSummary.TripInsurance.IsTripInsuranceSelected || $scope.TripSummary.IsTripInsuranceConfirmed) &&
						!$scope.IsTripInsurancePurchaseError;
				}

				function isPurchasingLeiGreeting() {
					return $scope.TripSummary.SelectedLeiGreeting &&
						$scope.TripSummary.SelectedLeiGreeting.isEligible &&
						!$scope.IsLeigreetingsPurchaseError;
				}

				function isPurchasingAirportShuttle() {
					return $scope.TripSummary.IsAirportShuttleSelected &&
						$scope.TripSummary.SelectedShuttleDetails != null &&
						$scope.TripSummary.SelectedShuttleDetails.isEligible &&
						!$scope.IsAirportShuttlePurchaseError;
				}

				$scope.hasInelegibleGiftCardItems = function() {
					return isPurchasingTripInsurance() || isPurchasingLeiGreeting() || isPurchasingAirportShuttle()
				};

				$scope.getGiftCardAncillaryMessage = function() {
					var list = []; // the property names added to this object must match the sitecore key names!
					function add(key, amount) {
						amount = $filter('localCurrency')(amount, $scope.currency);
						list.push($scs('GiftCard.ancillarylistformat',[$scs(key), amount]));
					}

					if (isPurchasingTripInsurance()) {
						add('passengertripsummary.tripinsurancetext', $scope.TripSummary.TripInsurance.InsuranceCost);
					}

					if (isPurchasingLeiGreeting()) {
						add('leigreetingancillary.leigreetinglabel', $scope.TripSummary.SelectedLeiGreeting.totalCost);
					}

					if (isPurchasingAirportShuttle()) {
						add('airportshuttleancillary.shuttlelabel', $scope.TripSummary.SelectedShuttleDetails.totalPrice);
					}


					return $scs('GiftCard.ancillarymessage', [list.join(', ')]);
				};

				// END GIFT CARD LOGIC
				/////////////////////////////////////////////////////////////////////


				$scope.setSecondPhonemandatory = function () {
					if (($scope.PhoneDetails[1].CountryCode != null) &&
						($scope.PhoneDetails[1].Number != null && $scope.PhoneDetails[1].Number !== '')) {
						$scope.secondPhoneMandatory = true;
						$scope.firstPhoneMandatory = false;
						$scope.thirdPhoneMandatory = false;
					}
					else {
						$scope.secondPhoneMandatory = false;
					}
				};

				$scope.setThirdPhonemandatory = function () {
					if (($scope.PhoneDetails[2].CountryCode != null) &&
						($scope.PhoneDetails[2].Number != null && $scope.PhoneDetails[2].Number !== '')) {
						$scope.thirdPhoneMandatory = true;
						$scope.secondPhoneMandatory = false;
						$scope.firstPhoneMandatory = false;
					}
					else {
						$scope.thirdPhoneMandatory = false;
					}
				};

				$scope.$watch('PhoneDetails[2].CountryCode', function (newValue) {
					var minAndMaxPhoneLength = $scope.getMaxAndMinPhoneLength(newValue);
					$scope.phoneThirdMaxLength = minAndMaxPhoneLength.maxLength;
					$scope.phoneThirdMinLength = minAndMaxPhoneLength.minLength;
					$scope.setThirdPhonemandatory();
				});

				$scope.$watch('PhoneDetails[2].Number', function (/* newValue */) {
					$scope.setThirdPhonemandatory();

				});

				$scope.$watch('PassengerTripSummary.grandTotal', function () {
					if (!$scope.IsChangeFlightBooking && !$scope.IsHoldBooking) {
						$scope.totalZero = $scope.PassengerTripSummary.grandTotal <= 0;
					}
					if (!$scope.IsChangeFlightBooking && $scope.IsHoldBooking && !$scope.HidePayment && $scope.SelectedHoldFare.HoldFee == 0) {
						$scope.totalZero = true;
					}
				});

				//Update Uplift offer when Insurance is selected
				$scope.$watch('TripSummary.TripInsurance.IsTripInsuranceSelected', function (newValue) {
					console.log(newValue);
					if (Upliftloaded && !$scope.IsChangeFlightBooking) {
						$timeout(function () {
							haUplift.selectUplift($scope.PassengerTripSummary.grandTotal, $scope.PassengerTripSummary, true);
						}, $rootScope.UpTimeout);

						$scope.validatePaymentType($scope.haPaymentTypes.paymentMethod);
					}
				});

				$scope.$watch('$root.UpToken', function (value) {
					//disable changing insurance when token is available
					if(value)
					disableInsurance(true);
				});
			
				// Reset the form pristine state whenever payment method changes
				// to clear out any field errors.
				var Upliftloaded = false;			
				$scope.$watch('haPaymentTypes.paymentMethod', function (newValue) {		 
					if (newValue) {
						$scope.paymentform.$setPristine();
					}
					$scope.validatePaymentType(newValue);
				});	

				$scope.validatePaymentType = function (newValue) {
					$("#payFullMethod").show();
					var monthlyContainer = $("#box-monthly");
					var fullContainer = $("#box-full");
					var confirmBooking = $("#confirmBooking");
					var confirmBookingAmount = $("#confirmBookingAmount"); 
					if (newValue == "uplift") {
						$("#payFullMethod").hide();
						if (monthlyContainer && fullContainer) {
							//hide complete purchase button when pay monthly option is selected
							fullContainer.css('border-color', '#a8a8a8');
							monthlyContainer.css('border-color', '#CE0C88');
							if (confirmBooking && confirmBookingAmount) {
								confirmBooking.hide();
								confirmBookingAmount.hide();
							}
						}
						$timeout(function () {
							//validate insurance 
							if ($scope.TripSummary.TripInsurance.IsTripInsuranceSelected != null) {
								//call uplift to show Uplift Billing iframe
								haUplift.selectPayMonthly($scope.TripSummary.TripInsurance.IsTripInsuranceSelected, true);
							} else if ($scope.TripSummary.ShowTripInsuranceBodyHTML == false || $scope.ShowTripInsuranceSection == false) {
								//proceed to uplift pay monthly iframe when insurance is disabled
								$scope.TripSummary.TripInsurance.IsTripInsuranceSelected = false;
								haUplift.selectPayMonthly($scope.TripSummary.TripInsurance.IsTripInsuranceSelected, true);
							} else {
								$timeout(function () {
									//show validation warings 
									var paymentform = 'paymentform';
									$scope.$eval(paymentform + '.$validate()');
								}, 0);
							}
						}, $rootScope.UpTimeout);
					} else if (Upliftloaded) {
						$("#payFullMethod").show();
						disableInsurance(false);
						//show complete purchase button when pay monthly is deselected
						$timeout(function () {
							haUplift.deselectPayMonthly(newValue, $scope.TripSummary.TripInsurance.IsTripInsuranceSelected);
							if(monthlyContainer && fullContainer) {
								fullContainer.css('border-color', '#CE0C88');
								monthlyContainer.css('border-color', '#a8a8a8');
								if (confirmBooking && confirmBookingAmount) {
									confirmBooking.show();
									confirmBookingAmount.show();
								}
							}
						}, $rootScope.UpTimeout);
						var banner = $('#barclaysOfferContain');

						if (banner) {
							banner.removeAttr("style")
						}
					}	
				}

				function disableInsurance(enable) {
					$("#tripinsyes").attr('disabled', enable);
					$("#tripinsno").attr('disabled', enable);
				}

				$scope.contactInfoSubmit = function () {
					$scope.checkPaymentForm = true;
					// make sure one of the phone fields has been filled out, else show "phone number is required" message
					if (($scope.PhoneDetails[0].CountryCode !== '' && $scope.PhoneDetails[0].Number != null && $scope.PhoneDetails[0].Number.length > 0) ||
						($scope.PhoneDetails[1].CountryCode !== '' && $scope.PhoneDetails[1].Number != null && $scope.PhoneDetails[1].Number.length > 0) ||
						($scope.PhoneDetails[2].CountryCode !== '' && $scope.PhoneDetails[2].Number != null && $scope.PhoneDetails[2].Number.length > 0)) {
						$scope.PhoneRequired = false;
					} else {
						$scope.PhoneRequired = true;
						// CS: Triggers a JS error
						//$('body, html').animate({ scrollTop: $('.phone-number-1').offset().top - 140 }, 'slow');
					}
				};
				$scope.UpdatePassengerList = function () {					
					if ($scope.TripSummary.AdultCount > 0) {
						$scope.AddPassengerList($scope.TripSummary.AdultCount, 'Adult');
					}
					if ($scope.TripSummary.ChildCount > 0) {
						$scope.AddPassengerList($scope.TripSummary.ChildCount, 'Child');
					}
					if ($scope.TripSummary.InfantCount > 0) {
						$scope.AddPassengerList($scope.TripSummary.InfantCount, 'Infant');
					}
				};

				$scope.UpdatePassengerList();

				$scope.ToggleTaxDetails = function () {
					$scope.taxDetails = !$scope.taxDetails;
				};

				//- Fetch content from SC for T&C popup
				$scope.showTermsAndConditionsModal = function (termsAndConditionsHTML) {
					haPaymentAPI.fetchModalContent(termsAndConditionsHTML)
						.success(function (data) {
							var content = data;
							haModal({
								id: 'results-help',
								backdrop: 'true',
								template: content
							});
						});
				};

				$scope.showHoldTermsAndConditionsModal = function () {
					haModal({
						id: 'hold-reservation-terms',
						modalClass: 'ha-content-modal',
						backdrop: 'true',
						template: angular.element('.terms-modal-content')
					});
				};

				$scope.scrollToInsuranceOffer = function($event) {
					$event.preventDefault();
					$('html, body').animate({
						scrollTop: $("[ha-ancillaries-trip-insurance]").offset().top
					}, 1000);
				};

				$scope.UpdateErrors = function (result) {

					if (result) {
						if (result.GiftCardStatus && result.GiftCardStatus.length > 0) {
							$scope.IsGiftCardFailed = result.IsPaymentAuthorized;
							$scope.GiftCardHeaderErrorMessage = result.GiftCardHeaderErrorMessage;
							$scope.ErrorType = 'INVALID_GIFTCARD';

							//looping gift card added in the UI grid
							angular.forEach($scope.giftCardResponse.GiftCards, function (giftCard) {
								//updating the gift card error messages
								for (var i = 0; i < result.GiftCardStatus.length; i++) {
									if (giftCard.UniqueGiftCardId === result.GiftCardStatus[i].GiftCardID) {
										giftCard.ErrorMessage = result.GiftCardStatus[i].GiftCardErrorMessage;
									}
								}
							}
							);
						}
						else if (result.IsServiceError) {
							$scope.IsServiceErrors = true;
							$scope.ServiceErrorMessage = result.ErrorMessage;


						}
						else{
							$scope.IsPaymentProcessing = false;
							$scope.ErrorType = 'INVALID_CREDITCARD';
						}
					}
					
					// clear form
					$('#termscheckbox').scope().Checkout.acceptTerms = false;
					$scope.$broadcast('ClearCCInfo');

					// if CDE iframe is present, refresh the iframe
					$('#formIFrame').attr('src', function (i, val) { return val; });

					$scope.$emit('closeModal');
					$('body, html').animate({
						scrollTop: 150
					}, 'slow');

				};

				$scope.displayInterstitialPayment = function () {
					//Initiate intertitial if not present
					if ($('#InterstitialPayment-modal').length === 0) {
						haModal(haConfig.getTemplateUrl('ha-payment-interstitial-template.html'), {
							id: 'InterstitialPayment-modal',
							backdrop: 'true',
							modalLock: true
						});
						$scope.interstitialDynamicResize();
					}
				};

				$scope.$on('CDEPaymentSubmitted', function () {
					$scope.displayInterstitialPayment();
				});

				$scope.closeInterstitialPayment = function () {
					$('#InterstitialPayment-modal').scope().$modalCancel();
					$timeout(function () {
						$('html, body').animate({ scrollTop: $('#formIFrame').offset().top - 50 }, 'fast');
					}, 100);
				};

				$scope.$on('CDEPaymentSubmitError', function () {
					$scope.closeInterstitialPayment();
				});

				$scope.CDESubmit = function () {
					var paymentform = 'paymentform';					
					if ($scope.haPaymentTypes.paymentMethod === 'creditDebit' ||
						($scope.haPaymentTypes.paymentMethod === 'giftCard' && $scope.giftCardResponse.GiftCards.length > 0 && $scope.giftCardResponse.BookingBalance > 0)) {
						$rootScope.$broadcast('CDESubmit');
					}
					else if ($scope.haPaymentTypes.paymentMethod === 'alipay') {
						//Check checkbox model and not entire form
						if ($('#termscheckbox').scope().Checkout.acceptTerms) {
							//API currently returns Ok with an IsSuccess bool to determine failure
							haPaymentAPI.getAliPaySignedUrl().success(function (data) {
								if (data.IsSuccess) {
									window.location = data.Data;
								} else {
									data.ErrorMessage = data.Message;
									$scope.handleExceptions(data, 'haPaymentAPI-getAliPaySignedUrl');
									$scope.ServiceErrorMessage = data.Message;
								}
							});
						}
						else {
							// manually turn on error displays
							$timeout(function () {
								$scope.$eval(paymentform + '.$validate()');
							}, 0);

							return false;
						}
					} else if ($scope.haPaymentTypes.paymentMethod === 'uplift') { 
						//confirm uplift payment 
						//Check checkbox model and not entire form
						if ($('#termscheckbox').scope().Checkout.acceptTerms) {
								if ($scope.IsPaymentProcessing) {
									return false;
								}
								$scope.IsPaymentProcessing = true;

								if (event) {
								    event.preventDefault();
								}
								$scope.form = $('#paymentform');
								
								//Initiate intertitial if not present
								$scope.displayInterstitialPayment();

								$timeout(function () {
									var paymentData = $($scope.form).serialize();									
									paymentData = paymentData + '&paymentMethod=uplift'
									$scope.handlePayment(paymentData);
								}, 0);
						}
						else {
							// manually turn on error displays
							$timeout(function () {
								$scope.$eval(paymentform + '.$validate()');
							}, 0);

							return false;
						}											
					} else {
						// submit form
						$timeout(function () {
							$('#' + paymentform).submit();
						}, 0);
					}

				}; 

				$scope.formSubmit = function ($event) {

					// prevent submit from happening multiple times
					
					if ($scope.IsPaymentProcessing) {
						return false;
					}
					$scope.IsPaymentProcessing = true;
					
					$event.preventDefault();
					$scope.form = $('#paymentform');

					//Initiate intertitial if not present
					$scope.displayInterstitialPayment();

					//handle indefintely spinning modal
					$timeout(function () {
						if (genericUnhandledBookingTimeoutGotoError) {
							window.location.href = '/book/error?ErrorType=Technical&ErrorCode=GenericUnhandledError';
						}
						else {
							$scope.ErrorType = 'GENERIC_UNHANDLED_ERROR';
							$('#termscheckbox').scope().Checkout.acceptTerms = false;
							$scope.$broadcast('ClearCCInfo');
							//$('#formIFrame').attr('src', function (i, val) { return val; });
							$scope.$emit('closeModal');
							$('body, html').animate({
								scrollTop: 150
							}, 'slow');
						}
					}, genericUnhandledBookingTimeoutInMilliSeconds);

					$timeout(function () {
						var paymentData = $($scope.form).serialize();
						$scope.handlePayment(paymentData);
					}, 0);
				};

				window.onresize = function () {
					$scope.interstitialDynamicResize();
				};
				
				$scope.handlePayment = function (paymentData) {					
					$scope.ValidateCreditCardCheck = true;
					if ($scope.IsChangeFlightBooking || $scope.IsHoldBooking || $scope.HidePayment || $scope.totalZero) {
						$scope.ValidateCreditCardCheck = false;
					}
					//Check if credit card number is valid
					if ($scope.ValidateCreditCardCheck) {
						haPaymentAPI.ValidatePaymentAuthorization(paymentData).success(function (result) {
							$scope.IsPaymentProcessing = false;
							if (!result.IsPaymentAuthorized && result.PaymentTokenValidator === 'PNRFailed') {
								window.location.href = '/book/error?ErrorType=PNRError';
							}
							else if (!result.IsPaymentAuthorized && result.PaymentTokenValidator === 'PaymentValidationFailed') {
								$scope.UpdateErrors(result);
							}
							else if (result.PaymentTokenValidator === 'PNRCreatedInSameSession') {
								window.location.href = '/book/error?ErrorType=PNRCreatedInSameSession';
							}
							else if (result.PaymentTokenValidator === 'NonHKStatusFound') {
								window.location.href = '/book/error?ErrorType=NonHKStatusFound';
							}
							else if (result.PaymentTokenValidator === 'ECERTERROR') {
								window.location.href = '/book/error?ErrorType=ECERTERROR&ErrorCode=' + result.ErrorMessage;
							}
							else if (result.PaymentTokenValidator === 'RedeemAwardAndPnrStatusError') {
								window.location.href = '/book/error?ErrorType=RedeemAwardAndPnrStatusError&ErrorCode=' + result.ErrorMessage;
							}
							else if (result.PaymentTokenValidator === 'InvalidETCO') {
								window.location.href = '/book/error?ErrorType=InvalidETCO&PathType=Book&ErrorCode=' + result.ErrorMessage;
							}
							else if (result.PaymentTokenValidator === 'InvalidGAFETCO') {
								window.location.href = '/book/error?ErrorType=InvalidGAFETCO&PathType=Book&ErrorCode=' + result.ErrorMessage;
							}
							else if (!result.IsPaymentAuthorized) {
								if (result.PaymentTokenValidator !== 'TokenEmpty') {
									$scope.UpdateErrors(result);
								}
							}
							else if (result.IsPaymentAuthorized) {
								//set the payment token
								$scope.Token = result.PaymentTokenValidator;

								//Air Availability Check only if PNRCreate on seat select flow is off
								if (!$scope.IsChangeFlightBooking && !$scope.IsPackageBooking && !$scope.IsHoldBooking) {
									$scope.ProcessPaymentForAsyncPNR(paymentData);
									$scope.IsProcessPayment = true;
								} else if ($scope.IsHoldBooking) {
									$scope.ProcessPaymentForHoldPNR(paymentData);
									$scope.IsProcessPayment = true;
								} else {
									haPaymentAPI.FlightAvailability(paymentData).success(function (result) {
										//Exception Handling
										if ($scope.handleExceptions(result, 'haPaymentAPI-checkFlightAvailability')) {
											return;
										}
										if (result.status === 1) {
											$scope.showFightsNoSeatsAvailableModal();
										} else if (result.status === 2) {
											$scope.showFightsScheduleMismatchModal();
										}
										else if (result.status === 0) {
											$scope.processPayment();
											$scope.IsProcessPayment = true;
										}
									}).error(function (result) {
										$scope.handleExceptions(data, 'haPaymentAPI-checkFlightAvailability');
										$scope.IsProcessPayment = false;
										$scope.ServiceErrorMessage = result;
									});
								}
							}
							else {
								$scope.IsPaymentProcessing = false;
								$scope.ErrorType = 'INVALID_CREDITCARD';
								$scope.$emit('closeModal');
								$('body, html').animate({
									scrollTop: 150
								}, 'slow');
							}
						}).error(function (result) {
							$scope.IsPaymentProcessing = false;
							$scope.IsProcessPayment = false;
							$scope.ServiceErrorMessage = result.ErrorMessage;
						});
					} else {
						$scope.IsPaymentProcessing = false;
						//Air Availability Check only if PNRCreate on seat select flow is off
						if ((!$scope.IsChangeFlightBooking && !$scope.IsPackageBooking && !$scope.IsHoldBooking && $scope.IsHoldRepurchase && !$scope.HasNIPass) || (($scope.HidePayment || $scope.totalZero) && !$scope.HasNIPass && !$scope.IsChangeFlightBooking && !$scope.IsHoldBooking)) {
							$scope.ProcessPaymentForAsyncPNR(paymentData);
							$scope.IsProcessPayment = true;
						} else if ($scope.IsHoldBooking && !$scope.HasNIPass) {
							$scope.ProcessPaymentForHoldPNR(paymentData).success(function () {
								$scope.IsProcessPayment = true;
							}).error(function (result) {
								$scope.IsPaymentProcessing = false;
								$scope.IsProcessPayment = false;
								$scope.ServiceErrorMessage = result.ErrorMessage;
							});
						} else if ($scope.HasNIPass) {
							$scope.ProcessNITPRedemption();
							$scope.IsProcessPayment = true;
						} else {
							haPaymentAPI.FlightAvailability(paymentData).success(function (result) {
								//Exception Handling
								if ($scope.handleExceptions(result, 'haPaymentAPI-checkFlightAvailability')) {
									return;
								}
								if (result.status === 1) {
									$scope.showFightsNoSeatsAvailableModal();
								} else if (result.status === 2) {
									$scope.showFightsScheduleMismatchModal();
								}
								else if (result.status === 0) {
									$scope.processPayment();
									$scope.IsProcessPayment = true;
								}
							}).error(function (result) {
								$scope.handleExceptions(data, 'haPaymentAPI-checkFlightAvailability');
								$scope.IsProcessPayment = false;
								$scope.ServiceErrorMessage = result;
							});
						}
					}
				};

				//Interstitial resize
				$scope.interstitialDynamicResize = function () {
					if (!$rootScope.isMobile) {
						$('.ha-modal#InterstitialPayment-modal .modalContainer').css({
							'width': $(window).width(),
							'height': $(window).height(),
							'padding-top': $(window).height() * 0.20
						});
					}
				};

				$scope.UpdateFlightSchedule = false;

				$rootScope.updateFlightSchedule = function () {
					$('#flightScheduleMismatch').hide();
					$scope.UpdateFlightSchedule = true;
					$scope.processPayment();
				};

				//Process payment
				$scope.processPayment = function () {
					// NEP: QUERY: Where do these globals come from? Is this a naming mistake?
					/* global userdata */
					/* jshint -W106 */
					userdata.load_data('user_data');

					/* jshint +W106 */
					haPaymentAPI.postPaymentProcess($($scope.form).serialize(), $scope.UpdateFlightSchedule, $scope.Token).success(function (result) {
						//Exception Handling
						if ($scope.handleExceptions(result, 'haPaymentAPI-postPaymentProcess')) {
							return;
						}
						if (result !== 'InvalidToken') {
							// $log.debug(result);
							if (result.IsPNRCreated) {
								window.location.href = '/book/Confirmation';
							} else {
								$scope.closeInterstitialPayment();
							}

						} else {
							$scope.closeInterstitialPayment();
						}


					}).error(function () {
						$scope.IsServiceErrors = true;
						$scope.handleExceptions('haPaymentAPI-postPaymentProcess');
					});
				};

				//Process Payment For Async PNR
				$scope.ProcessPaymentForAsyncPNR = function (paymentVm) {
					// NEP: QUERY: Where do these globals come from? Is this a naming mistake?
					/* global userdata */
					/* jshint -W106 */
					userdata.load_data('user_data');
					var paymentData = paymentVm || $($scope.form).serialize();

					/* jshint +W106 */
					haPaymentAPI.postProcessPaymentForAsyncPNR(paymentData, $scope.UpdateFlightSchedule, $scope.Token).success(function (result) {

						// Mpulse tracking - trip insurance purchase request.
						if ($scope.TripSummary.TripInsurance.IsTripInsuranceSelected) {
							if (window.BOOMR && BOOMR.version) {
								window.BOOMR.sendMetric("Alnz_Purchs_Rq", 1);
							}
						}

						// Strip out extra quotes from string results...
						if (typeof result === 'string' || result instanceof String) {
							result = result.replace(/"/g, '');
						}
						if (result.ErrorType === 'PaymentProcessError') {
							window.location.href = '/book/error?ErrorType=Technical&ErrorCode=' + result.ErrorMessage;
							return;
						}
						else if (result.ErrorType === 'ECERTERROR') {
							window.location.href = '/book/error?ErrorType=ECERTERROR&ErrorCode=' + result.ErrorMessage;
							return;
						}

						//Exception Handling
						if ($scope.handleExceptions(result, 'haPaymentAPI-ProcessPaymentForAsyncPNR')) {
							return;
						}

						if (result === 'InvalidETCO') {
							window.location.href = '/book/error?ErrorType=InvalidETCO&PathType=Book&ErrorCode=' + result.ErrorMessage;
						}
						else if (result === 'InvalidGAFETCO') {
							window.location.href = '/book/error?ErrorType=InvalidGAFETCO&PathType=Book&ErrorCode=' + result.ErrorMessage;
						}
						else if (result !== "InvalidToken") {
							// $log.debug(result);
							if (result.IsPNRCreated) {
								window.location.href = '/book/Confirmation';
							} else {
								$scope.closeInterstitialPayment();
							}

						} else {
							$scope.closeInterstitialPayment();
						}


					}).error(function () {
						$scope.IsServiceErrors = true;
						$scope.handleExceptions('haPaymentAPI-ProcessPaymentForAsyncPNR');
					});
				};

				//Process Payment For Hold PNR
				$scope.ProcessPaymentForHoldPNR = function (paymentVm) {
					// NEP: QUERY: Where do these globals come from? Is this a naming mistake?
					/* global userdata */
					/* jshint -W106 */
					userdata.load_data('user_data');

					var paymentData = paymentVm || $($scope.form).serialize();

					/* jshint +W106 */
					haPaymentAPI.postProcessPaymentForHoldPNR(paymentData, $scope.UpdateFlightSchedule, $scope.Token).success(function (result) {
						//Exception Handling
						if ($scope.handleExceptions(result, 'haPaymentAPI-ProcessPaymentForHoldPNR')) {
							return;
						}
						if ((result === 'InvalidToken') || !result.IsPNRCreated) {
							$scope.closeInterstitialPayment();
						} else {
							window.location.href = '/book/Confirmation';
						}

					}).error(function () {
						$scope.IsServiceErrors = true;
						$scope.handleExceptions('haPaymentAPI-ProcessPaymentForHoldPNR');
					});
				};

				//NITP Redemption flow
				$scope.ProcessNITPRedemption = function () {
					// NEP: QUERY: Where do these globals come from? Is this a naming mistake?
					/* global userdata */
					/* jshint -W106 */
					userdata.load_data('user_data');

					/* jshint +W106 */
					haPaymentAPI.postProcessNITPRedemption().success(function (result) {
						//Exception Handling
						if ($scope.handleExceptions(result, 'haPaymentAPI-ProcessNITPRedemption')) {
							return;
						}
						if (result !== 'InValidNITPRedemption') {
							// $log.debug(result);
							if (result.IsPNRCreated) {
								window.location.href = '/book/Confirmation';
							} else {
								$scope.closeInterstitialPayment();
							}

						} else {
							$scope.closeInterstitialPayment();
						}
					}).error(function () {
						$scope.IsServiceErrors = true;
						$scope.handleExceptions('haPaymentAPI-ProcessNITPRedemption');
					});
				};

				//Show Schedule Mismatch Popup
				$scope.showFightsScheduleMismatchModal = function () {
					haPaymentAPI.fetchScheduleMismatchPopup().success(function (content) {
						haModal({
							id: 'flightScheduleMismatch',
							backdrop: 'true',
							template: content,
							modalLock: true,
							cancel: {
								label: 'Close', fn: function () {
									window.location.href = '/book/FlightResults';
								}
							}
						});
					});
				};

				//Error page if all seats not available
				$scope.showFightsNoSeatsAvailableModal = function () {
					window.location.href = '/book/Error?ErrorType=SeatNotAvailable';
				};

				//Exception Handler
				$scope.handleExceptions = function (result) {
					if (result !== 'jsError') {
						if (result.ErrorCodeHandle === undefined && result.ErrorMessage != null && result.ErrorMessage !== '') {
							if (result.ErrorType === 'ECERTERROR') {
								window.location.href = '/book/error?ErrorType=ECERTERROR&ErrorCode=' + result.ErrorMessage;
								return true;
							} else {
								$scope.showErrorMessage = true;
								$scope.ErrorMessage = result.ErrorMessage;
								$scope.closeInterstitialPayment();
							}
						}
						else {
							switch (result.ErrorCodeHandle) {
								case 'SessionTimeOut':
									window.location.href = '/book/error?ErrorType=SessionTimeOut';
									break;
								default:
									if (result.ErrorCodeHandle !== undefined) {
										window.location.href = result.RedirectURL;
									}
									else {
										return false;
									}
									break;
							}
						}
					} else {
						window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
					}
				};

				// HOLD RESERVATION
				$scope.removePriceLock = function () {
					$scope.IsHoldBooking = false;
					$scope.holdReservationRemoved = true;
					
					haPaymentAPI.removePriceLockSession().success(function (result) {
						$log.debug(result);
						haPaymentTypesSvc.updatePaymentTypes();
					}).error(function () {
						$log.debug("Error in removing price lock session");
					});
					$('body, html').animate({ scrollTop: 135 }, 'slow');

					$rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off
					
					//toggle uplift offers when fare hold is removed
					$(document).ready(function () {
						setTimeout(function () {
							haPaymentAPI.getItineraryDetails().success(function (result) {
								haUplift.getUpliftConfig();
								haUplift.selectUplift($scope.PassengerTripSummary.grandTotal, $scope.PassengerTripSummary, true, result);
								haUplift.upReady();
							}).error(function () {
								$log.debug("itinerary not loaded");
							});

						}, $rootScope.UpTimeout);
					});
				};


				// localization replacement for days
				$scope.addDays = function (string, days) {
					var updatedString = string.replace('[days]', days);
					return updatedString;
				};

				// CANCEL PNR

				$scope.validNavigation = false;

				window.onbeforeunload = function () {
					if (!$scope.validNavigation) {
						// cannot use angular $http for synchronous ajax call
						$.ajax({
							type: "GET",
							url: "/Book/Payment/CancelTrip",
							async: false,
							cache: false
						});
					}
				};

				// Attach the event keypress to exclude the F5 refresh
				$(document).bind('keypress', function (e) {
					if (e.keyCode === 116) {
						$scope.validNavigation = true;
					}
				});

				// Attach the event submit for all forms in the page
				$("form").bind("submit", function () {
					$scope.validNavigation = true;
				});

				// Attach the event click for all inputs in the page
				$("input[type=submit]").bind("click", function () {
					$scope.validNavigation = true;
				});			

				$(document).ready(function () {
					// if this page is redirected from Alipay as a consequence of successful payment,
					// paymentVm should be already set in AlipayController	
					if ($scope.paymentForm.isAlipayPaymentSuccess) {
						// start interstitial right away
						$scope.displayInterstitialPayment();

						// create param to pass
						var param = {};
						// copy PaymentInfo into param
						param.PaymentInfo = Object.assign({}, $scope.PaymentInfo);
						// add payment method
						param.paymentMethod = $scope.paymentMethod;

						$timeout(function () {
							$scope.handlePayment($.param(param));
						}, 0);
					}					
				});

				//uplift call to show monthly offer for Checkout page
				$(document).ready(function () {
					setTimeout(function () {
						Upliftloaded = true;
						if (!$scope.IsChangeFlightBooking) {
							haPaymentAPI.getItineraryDetails().success(function (result) {
								haUplift.selectUplift($scope.PassengerTripSummary.grandTotal, $scope.PassengerTripSummary, true, result);
								haUplift.upReady();
							}).error(function () {
								$log.debug("itinerary not loaded");
							});
						} 						
					}, 2500);
				});
			}];		

		var HaCheckoutLink = function ($scope) {

			$scope.$watch('TripSummary.SelectedCar', function (SelectedCar) {
				$scope.SelectedCar = SelectedCar;
			});
			$scope.$watch('TripSummary.SelectedShuttleDetails', function (SelectedShuttleDetails) {
				$scope.SelectedShuttleDetails = SelectedShuttleDetails;
			});

			angular.forEach($scope.TripSummary.Trips, function (trip) {
				var segment = trip.Flights[0];
				segment.IsMileagePricing = trip.IsMileagePricing;

				for (var i = 0; i < segment.AvailBookingFares.length; i++) {
					segment.selectedSeatClass = segment.AvailBookingFares[i].Name;
					segment[segment.selectedSeatClass] = segment.AvailBookingFares[i];
				}
				$scope.selectedSegments.push(segment);
			});

		};
		
		return {
			restrict: 'A',
			scope: true,
			link: HaCheckoutLink,
			controller: HaCheckoutController
		};  
	}]);

})(angular);
;
(function (angular) {

	// Ha TermsCondition
	// --------------------------------------------
	//
	// * **Class:** haTermsConditions
	// * **Author:** Sayeed Mohammad
	//
	// This is the main module for display fare reules terms and conditions across all pages in the booking path

	'use strict';

	var module = angular.module('haTermsConditionsModule', ['haPaymentAPI']);

	module.directive('haTermsConditions', ['haPaymentAPI', function (haPaymentAPI) {

		var HaTermsConditionsController = [
			'$scope', 'haModal', function ($scope, haModal) {
				
				// fetch content from sitecore for terms and condition modal
				$scope.showTermsAndConditionsModal = function (termsAndConditionsHTML) {
					haPaymentAPI.fetchModalContent(termsAndConditionsHTML)
						.success(function (data) {
							var content = data;
							var fiScope = !(window.location.href.indexOf("inflightoptions") > -1)? $scope : '';
							haModal({
								id: 'terms-conditions',
								backdrop: 'true',
								template: content,
								scope: fiScope
							});
						});
				};		
			}];

		return {
			restrict: 'A',
			scope: true,
			controller: HaTermsConditionsController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Purchase Miles
	// --------------------------------------------
	//
	// * **Class:** HaPurchaseMiles
	// * **Author:** Curtis Floth
	//
	// Module for purchasing HA miles

	'use strict';

	var module = angular.module('haPurchaseMilesModule', ['haPurchaseMilesAPI', 'haHttpService']);

	module.directive('haPurchaseMilesSelect', ['haGlobals', 'haPurchaseMilesAPI', 'haHttpService', function (haGlobals, haPurchaseMilesAPI, haHttpService) {
		var HaPurchaseMilesSelectController = function ($scope, haModal) {
			$scope.SelectMiles = {
				MilesSelected: null,
				RecipientEmail: '',
				LastName: '',
				AccountNumber: '',
				HasSharedTravelers: true
			};
			$scope.Error = '';
			$scope.Validated = false;
			$scope.formClicked = false;

			haGlobals('jsonPurchaseMilesSelectModel', function (jsonPurchaseMilesSelectModel) {
				if (jsonPurchaseMilesSelectModel.MilesSelected > 0) {
				    $scope.SelectMiles.MilesSelected = jsonPurchaseMilesSelectModel.MilesSelected +'';
				}
				$scope.SelectMiles.RecipientEmail = jsonPurchaseMilesSelectModel.RecipientEmail;
			});

			haGlobals('errorMessage', function (errorMessage) {
				if (errorMessage !== '') {
					$scope.Error = errorMessage;
				}
			});

			$scope.formdata = {};
			$scope.formdata.sourceClient = 'self';

			$scope.getHelpContent = function () {
				haModal('', {
					id: 'purchase-miles-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			var travelersEndPoint = '/myaccount/travelerslist/gettravelerlist';

			$scope.safeApply = function (fn) {
				var phase = this.$root.$$phase;
				if (phase === '$apply' || phase === '$digest') {
					if (fn && (typeof (fn) === 'function')) {
						fn();
					}
				} else {
					this.$apply(fn);
				}
			};

			$scope.checkPurchaseMilesEligibility = function (event) {
				event.preventDefault();
				$scope.Error = '';
				$scope.busy = true;
				var iSGiftPurchaseSelected = false;
				if ($scope.formdata.sourceClient === 'member') {
					iSGiftPurchaseSelected = true;
				}
				haPurchaseMilesAPI.checkEligibility($scope.SelectMiles.MilesSelected, iSGiftPurchaseSelected, $scope.SelectMiles.AccountNumber, $scope.SelectMiles.LastName, $scope.SelectMiles.RecipientEmail)
				.success(function (response) {
					$scope.formClicked = false;
					if (response.IsSuccess) {
						$scope.Validated = true;
						$('#purchaseMilesSubmit').prop('disabled', false);
						$('form[name=purchaseMilesAmount]')[0].submit.click();
						$('#purchaseMilesSubmit').prop('disabled', true);
					}
					else {
						$scope.Error = response.Message;
						$scope.busy = false;
						$('body, html').animate({ scrollTop: 0 }, 'fast');
					}

				});
			};

			if ($scope.SelectMiles.HasSharedTravelers) {
				haHttpService.GET(travelersEndPoint).then(
					function (response) {
						if (response.data.TravelersList) {
							$scope.FilteredTravelersList = response.data.TravelersList;
						}
					},
					function (err) {
						console.log(err);
						$scope.SelectMiles.HasSharedTravelers = false;
					}
				);
			}

			$scope.travelerChosen = function (traveler) {
				$scope.$modalCancel();
				$scope.SelectMiles.LastName = traveler.LastName;
				$scope.SelectMiles.AccountNumber = traveler.HMAccountNo || '';
			};

			$scope.openSharedTravelers = function () {
				haModal({
					id: 'sharedTravelersModal',
					backdrop: 'true',
					scope: $scope,
					template: angular.element('#sharedTravelers')
				});
			};

		};

		HaPurchaseMilesSelectController.$inject = ['$scope', 'haModal'];

		return {
			restrict: 'A',
			scope: true,
			controller: HaPurchaseMilesSelectController
		};
	}]);

	module.directive('haPurchaseMilesPayment', function () {
		var HaPurchaseMilesController = function ($scope, $rootScope, haModal, haGlobals, $timeout) {
			$scope.AgentCode = '';
			$scope.PurchaseMilesPayment = { EmailAddress: '' };
			$scope.submitting = false;

			haGlobals('purchaseMilesPaymentJson', function (purchaseMilesPaymentJson) {
				$scope.PurchaseMilesPayment.EmailAddress = purchaseMilesPaymentJson.EmailAddress;
			});

			$scope.showPrivacyPolicy = function () {
				haModal('', {
					id: 'privacy-policy',
					backdrop: 'true',
					template: angular.element('.showPrivacyPolicy')
				});
			};

			$scope.showTerms = function () {
				haModal('', {
					id: 'terms-and-conditions',
					backdrop: 'true',
					template: angular.element('.showTerms')
				});
			};

			$scope.getHelpContent = function () {
				haModal('', {
					id: 'purchase-miles-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.onSubmitForm = function () {
				// ... all other internal processing on submit
				/* global userdata */
				/* jshint -W106 */
				userdata.load_data('user_data');
				/* jshint +W106 */
				$scope.submitting = true;
				return true;
			};

			$scope.cancelPayment = function () {
				/* global itinDetailsPath */
				window.location = itinDetailsPath;
			};

			// for PCI Payment Authorization
			$scope.haPaymentTypes = {
				paymentMethod: 'creditDebit'
			};

			$scope.CDESubmit = function () {
				var iframe = document.getElementById('formIFrame');
				if (iframe) {
					iframe.contentWindow.postMessage('valid', iframeOrigin);
				}
				$rootScope.$broadcast('CDESubmit');
			};

			$scope.$on('CDEPaymentSubmitted', function() {
				$scope.submitting = true;
			});

			$scope.$on('CDEPaymentSubmitError', function() {
				$scope.submitting = false;
				$timeout(function() {
					$('html, body').animate({ scrollTop: $('#formIFrame').offset().top - 50 }, 'fast');
				}, 100);
			});
			// end PCI
		};

		HaPurchaseMilesController.$inject = ['$scope', '$rootScope', 'haModal', 'haGlobals', '$timeout'];

		return {
			restrict: 'A',
			scope: true,
			controller: HaPurchaseMilesController
		};
	});

	module.directive('creditCardType', function () {
		return {
			require: 'ngModel',
			link: function ($scope, elm, attrs, ctrl) {
				ctrl.$parsers.unshift(function (value) {
					$scope.ccinfo.type =
						(/^5[1-5]|^2(?:2(?:2[1-9]|[3-9]\d)|[3-6]\d\d|7(?:[01]\d|20))/.test(value)) ? 'mastercard'
							: (/^4/.test(value)) ? 'visa'
							: (/^3[47]/.test(value)) ? 'amex'
							: (/^6011|65|64[4-9]|622(1(2[6-9]|[3-9]\d)|[2-8]\d{2}|9([01]\d|2[0-5]))/.test(value)) ? 'discover'
							: undefined;
					ctrl.$setValidity('invalid', !!$scope.ccinfo.type);
					return value;
				});
			}
		};
	});
	module.directive('cardExpiration', function () {
		return {
			require: 'ngModel',
			link: function (scope, elm, attrs, ctrl) {
				scope.$watch('[ccinfo.month,ccinfo.year]', function (value) {
					ctrl.$setValidity('invalid', true);
					if (+scope.ccinfo.year === +scope.currentYear && scope.ccinfo.month <= scope.currentMonth) {
						ctrl.$setValidity('invalid', false);
					}
					return value;
				}, true);
			}
		};
	});
	module.directive('haPurchaseMilesConfirmation', function () {
		/* global confirmationVM */
		var HaPurchaseMilesConfirmationController = function ($scope, haModal, $filter) {
			$scope.CurrentMilesBalance = confirmationVM.CurrentMilesBalance;
			var newBalanceText = 'Balance' + ': ' + $filter('number')($scope.CurrentMilesBalance);
			angular.element('#account-nav-list li:first-child').find('span.popover-link-secondary').text(newBalanceText);

			$scope.showFAQ = function () {
				haModal('', {
					id: 'purchase-miles-faq',
					backdrop: 'true',
					template: angular.element('.showFAQ')
				});
			};
			$scope.showTerms = function () {
				haModal('', {
					id: 'terms-and-conditions',
					backdrop: 'true',
					template: angular.element('.showTerms')
				});
			};

			$scope.getHelpContent = function () {
				haModal('', {
					id: 'purchase-miles-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.$on('haFormValidationSuccess', function () {
				return false;
			});

		};

		HaPurchaseMilesConfirmationController.$inject = ['$scope', 'haModal', '$filter'];

		return {
			restrict: 'A',
			scope: true,
			controller: HaPurchaseMilesConfirmationController
		};
	});

	module.filter('localPurchaseMilesCurrency', ['$compile', function () {
		var formats = {
			USD: '<span class="currency-symbol">$</span>{{ amount }}',
			AUD: '<span class="currency-symbol">$</span>{{ amount }} <span class="currency-type">AUD</span>',
			NZD: '<span class="currency-symbol">$</span>{{ amount }} <span class="currency-type">NZD</span>',
			CNY: '<span class="currency-symbol">¥</span>{{ amount }}',
			KRW: '<span class="currency-symbol">₩</span>{{ amount}}',
			JPY: '<span class="currency-symbol">¥</span>{{ amount }}',
			TWD: '<span class="currency-type">NT</span><span class="currency-symbol">$</span>{{ amount }}',
			MILES: '{{ amount }} <span class="currency-type">mi</span>'
		};

		var replaceNumberWithCommas = function (yourNumber) {
			//Seperates the components of the number
			var n = yourNumber.toString().split('.');
			//Comma-fies the first part
			n[0] = n[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
			//Combines the two sections
			return n.join('.');
		};

		var signCurrency = function (rawAmount, formatted) {
			if (rawAmount > -1) {
				return formatted + ' ' + angular.element('#MoreText').val();
			} else {
				return formatted.replace(/-([1-9]+)/g, function ($1, $2) {
					return $2;
				}) + ' ' + angular.element('#LessText').val();
			}
		};

		return function (amount, code, noDecimal, signed, disableRoundOff) {
			var rounded;
			var commaFormatted;
			if (code === 'CNY' || code === 'KRW' || code === 'JPY' || code === 'TWD') {
				disableRoundOff = false;
				noDecimal = true;
			}
			if (!disableRoundOff) {
				if (noDecimal) {
					rounded = Math.round(amount);
				} else {
					rounded = (Math.round(amount * 100) / 100).toFixed(2);
				}
			}
			else {
				rounded = amount;
			}

			if (rounded) {
				commaFormatted = replaceNumberWithCommas(rounded);
			}

			// get rid of decimal places if MILES
			if (code === 'MILES') {
				commaFormatted = commaFormatted.split('.')[0];
			}

			if (formats[code]) {
				var denominationFormatted = formats[code].replace(/\{\{.*\}\}/, commaFormatted);
				if (signed) {
					return signCurrency(amount, denominationFormatted);
				} else {
					//console.log(denominationFormatted);
					return denominationFormatted;
				}
			}
		};
	}]);
})(angular);
;
(function (angular) {

	// Ha Profile Settings
	// --------------------------------------------
	//
	// * **Class:** HaProfileSettings
	// * **Author:** Curtis Floth
	//
	// Profile & Settings modules

	'use strict';

	var module = angular.module('haProfileSettingsModule', ['haProfileSettingsAPI']);

	module.directive('haProfileSettings', ['$timeout', 'haGlobals', 'haProfileSettingsAPI', 'haModal', 'haSitecoreStrings', 'haReAuthService', function ($timeout, haGlobals, haProfileSettingsAPI, haModal, $scs, haReAuthService) {

		var haProfileSettingsController = function ($scope, $rootScope) {

			$scope.isProfileSettings = true;
			$scope.regform = {};

			haGlobals('ContactInfoJson', function (ContactInfoJson) {
				$scope.AccountDetail = ContactInfoJson;

				haGlobals('ContactInfoMsg', function (ContactInfoMsg) {
					if (ContactInfoMsg !== '') {
						if (ContactInfoMsg === true) {
							$scope.ShowProfileSettingsAlert = true;
							$scope.ProfileSettingsMessageType = 'success';
							haGlobals('successMsg', function (successMsg) {
								$scope.ProfileSettingsHeaderMessage = successMsg;
							});
						} else {
							$scope.ShowProfileSettingsAlert = true;
							$scope.ProfileSettingsMessageType = 'error';
							$scope.ProfileSettingsHeaderMessage = ContactInfoMsg;
						}
					}
				});
				$scope.passportForm = false;
				haGlobals('jsonPassportDetails', function (jsonPassportDetails) {
					$scope.PassportDetails = jsonPassportDetails;
					if (jsonPassportDetails.PassportNumber && jsonPassportDetails.PassportNumber !== undefined) {
						$scope.passportForm = true;
						$scope.PassportNumber = jsonPassportDetails.PassportNumber;
					} else {
						$scope.passportForm = false;
						$scope.PassportNumber = null;
					}
					if (jsonPassportDetails.Country && jsonPassportDetails.Country !== undefined) {
						$scope.Country = jsonPassportDetails.Country;
					} else {
						$scope.Country = null;
					}
					if (jsonPassportDetails.IssueDat && jsonPassportDetails.IssueDate !== undefined) {
						var passportIssueDate = jsonPassportDetails.IssueDate.split('/');
						if (passportIssueDate.length > 0) {
							$scope.IssueMon = passportIssueDate[0];
							$scope.IssueDay = passportIssueDate[1];
							$scope.IssueYear = passportIssueDate[2];
						}
					} else {
						$scope.IssueMon = null;
						$scope.IssueDay = null;
						$scope.IssueYear = null;
					}
					if (jsonPassportDetails.ExpiryDate && jsonPassportDetails.ExpiryDate !== undefined) {
						var passportExpiryDate = jsonPassportDetails.ExpiryDate.split('/');
						if (passportExpiryDate.length > 0) {
							$scope.ExpMon = passportExpiryDate[0];
							$scope.ExpDay = passportExpiryDate[1];
							$scope.ExpYear = passportExpiryDate[2];
						}
					} else {
						$scope.ExpMon = null;
						$scope.ExpDay = null;
						$scope.ExpYear = null;
					}
				});
			});

			$scope.showPassportForm = function () {
				if (!$scope.passportForm) {
					$scope.passportForm = true;
				}
			};


			function SortByOrder(a, b) {
				var aName = parseInt(a.SortOrder);
				var bName = parseInt(b.SortOrder);
				return ((aName < bName) ? -1 : ((aName > bName) ? 1 : 0));
			}

			haGlobals('jsonTravelPreferencesModel', function (jsonTravelPreferencesModel) {
				$scope.Interests = [];
				$scope.FavoriteDestinations = [];

				$scope.TravelPreferencesViewModel = jsonTravelPreferencesModel;
				$scope.RedressNumber = jsonTravelPreferencesModel.RedressNumber;
				$scope.RedressNoCountryCode = jsonTravelPreferencesModel.RedressNoCountryCode;
				$scope.KnownTravelerNumber = jsonTravelPreferencesModel.KnownTravelerNumber;
				$scope.KnownTravelerNoCountryCode = jsonTravelPreferencesModel.KnownTravelerNoCountryCode;
				$scope.SeatPreference = jsonTravelPreferencesModel.SeatPreference || '1';
				$scope.BookAvailablePremiumSeat = jsonTravelPreferencesModel.BookAvailablePremiumSeat;
				$scope.HomeAirport = jsonTravelPreferencesModel.HomeAirport;
				$scope.TravelPreferencesViewModel.InterestLists.sort(SortByOrder);
				for (var i = 0; i < $scope.TravelPreferencesViewModel.InterestLists.length; i++) {
					if ($scope.TravelPreferencesViewModel.InterestLists[i].IsChecked) {
						$scope.Interests.push($scope.TravelPreferencesViewModel.InterestLists[i].Value);
					}
					else {
						$scope.Interests.push(false);
					}
				}

				$scope.TravelPreferencesViewModel.FavoriteDestinationLists.sort(SortByOrder);
				for (i = 0; i < $scope.TravelPreferencesViewModel.FavoriteDestinationLists.length; i++) {
					if ($scope.TravelPreferencesViewModel.FavoriteDestinationLists[i].IsChecked) {
						$scope.FavoriteDestinations.push($scope.TravelPreferencesViewModel.FavoriteDestinationLists[i].Value);
					}
					else {
						$scope.FavoriteDestinations.push(false);
					}
				}
			});

			haGlobals('accountSettingsModel', function (accountSettingsModel) {
				$scope.AccountSettings = accountSettingsModel;
				$scope.AccountSettings.ConfirmNewPassword = '';

				haGlobals('ProfileSettingsMessage', function (ProfileSettingsMessage) {
					if (ProfileSettingsMessage !== '') {
						if (ProfileSettingsMessage === true) {
							$scope.ShowProfileSettingsAlert = true;
							$scope.ProfileSettingsMessageType = 'success';
							haGlobals('successMsg', function (successMsg) {
								$scope.ProfileSettingsHeaderMessage = successMsg;
							});
							setTimeout(function () {
								$('.account-pic').attr('src', accountSettingsModel.AvatarUrl);
							});
						} else {
							$scope.ShowProfileSettingsAlert = true;
							$scope.ProfileSettingsMessageType = 'error';
							$scope.ProfileSettingsHeaderMessage = ProfileSettingsMessage;
						}
					}
				});
			});

			haGlobals('EmailSubscriptionJson', function (EmailSubscriptionJson) {
				$scope.EmailSubscriptions = EmailSubscriptionJson;

				haGlobals('ProfileSettingsMessage', function (ProfileSettingsMessage) {
					if (ProfileSettingsMessage !== null && ProfileSettingsMessage !== '') {
						if (ProfileSettingsMessage === true) {
							$scope.ShowProfileSettingsAlert = true;
							$scope.ProfileSettingsMessageType = 'success';
							haGlobals('successMsg', function (successMsg) {
								$scope.ProfileSettingsHeaderMessage = successMsg;
							});
						} else {
							$scope.ShowProfileSettingsAlert = true;
							$scope.ProfileSettingsMessageType = 'error';
							$scope.ProfileSettingsHeaderMessage = ProfileSettingsMessage;
						}
					}
				});

				$scope.getUrlVars = function () {

					var vars = [];
					var hash;
					var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
					for (var i = 0; i < hashes.length; i++) {
						hash = hashes[i].split('=');
						vars.push(hash[0]);
						vars[hash[0]] = hash[1];

					}

					return vars;
				};

				var vars = $scope.getUrlVars();
				if (typeof (vars.EmailAddress) !== 'undefined') {
					$scope.EmailSubscriptions.EmailAddress = vars.EmailAddress;
					$scope.EmailSubscriptions.ZipCode = vars.ZipCode;
					$scope.ShowProfileSettingsAlert = true;
					$scope.ProfileSettingsMessageType = 'success';
					haGlobals('SubscribtionSuccessMessage', function (SubscribtionSuccessMessage) {
						$scope.ProfileSettingsHeaderMessage = SubscribtionSuccessMessage;
					});
				}
			});

			$scope.isSelected = true;

			$scope.IsReadOnly = true;

			$scope.$emit('$haProfileSettingsReady');

			$scope.$on('$haDataLoaded', function () {

				// console.log($scope.GenderLists);
				$scope.genderDropDownSource = [];
				$scope.Gender = '';
				angular.forEach($scope.GenderLists, function (item) {
					$scope.genderDropDownSource.push({name: item.Gender.Value, value: item.Gender.ID});
					if (item.Gender.Selected) {
						$scope.Gender = item.Gender.ID;
					}
				});

				// console.log($scope.genderDropDownSource);

			});

			$scope.UpdateTravelPreferenceSuccess = false;
			$scope.UpdateTravelPreferenceErrorMessage = '';
			$scope.disableUpdateTravelPreferenceSubmit = false;

			$scope.submitTravelPreferences = function ($event) {
				$event.preventDefault();
				$scope.UpdateTravelPreferenceSuccess = false;
				$scope.UpdateTravelPreferenceErrorMessage = '';
				$scope.disableUpdateTravelPreferenceSubmit = true;
				$scope.working = true;
				var form = $('#TravelPreferencesForm');

				haProfileSettingsAPI.updateTravelPreferences($(form).serialize()).success(function (response) {
					$scope.working = false;
					if (response.IsSuccess) {
						/* global successMsg */
						$scope.ProfileSettingsHeaderMessage = successMsg;
						$scope.ProfileSettingsMessageType = 'success';
						$scope.ShowProfileSettingsAlert = true;
						$scope.$$childHead.theForm.$dirty = false;
					}
					else {
						$scope.ProfileSettingsHeaderMessage = response.Message;
						$scope.ProfileSettingsMessageType = 'error';
						$scope.ShowProfileSettingsAlert = true;
					}
					$scope.disableUpdateTravelPreferenceSubmit = false;
					$('body, html').animate({scrollTop: $(form).offset().top}, 'slow');
				})
				.error(function (xhr, httpStatusCode) {
					$scope.working = false;
					if (httpStatusCode === 403 || httpStatusCode === 401) {
						$rootScope.$broadcast('SessionError');
					}
					$scope.disableUpdateTravelPreferenceSubmit = false;
				});
			};

			$scope.RedirectToLoginPage = function (response) {
				if (response != null && response.data != null && response.data.RedirectURL != null) {
					window.location.href = response.data.RedirectURL;
				}
				else {
					window.location.href = window.location.href;
				}
			};

			$scope.$on('responseError:UNAUTHORIZED', function (event, response) {
				$scope.RedirectToLoginPage(response);
			});

			$scope.$on('responseError:FORBIDDEN', function (event, response) {
				$scope.RedirectToLoginPage(response);
			});

			$scope.showUpgradeTipModal = function () {
				haModal('', {
					id: 'seat-upgrade-tip-modal',
					backdrop: 'true',
					template: angular.element('#upgradeTipContent'),
				});
			};
					
			$scope.updateProfile = function (event, formName) {
				event.preventDefault();
				var formScope = $('#' + formName).scope();
				formScope.submitForm(); // Satisfies ha-confirm-when-dirty

				if (formScope[formName].$valid) {
					$scope.parentFormName = formName;
					if ($scope.reAuthFields && haReAuthService.formRequiresReAuth($scope.reAuthFields)) {
						haModal('', {
							id: 're-authentication-modal',
							backdrop: 'true',
							template: angular.element('#reAuthModal'),
							scope: $scope
						});
					} else {
						formSubmitWithJs(formName);
					}
				}
			};

			$scope.reAuthAndUpdateProfile = function (formName, password) {
				formSubmitWithJs(formName, password);
			};

			$scope.$on('$destroy', haReAuthService.cleanUp());

			function formSubmitWithJs(formName, password) {
				var domForm = $('#' + formName);
				if (!!password) {
					domForm.append('<input name="AccountDetail.Password" type="hidden" ng-model="AccountDetail.Password" value="' + password + '"/>');
				}
				domForm[0].submit();
			};

		};


		haProfileSettingsController.$inject = ['$scope'];

		var haProfileSettingsLink = function ($scope, elem, attrs) {

			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.getIssueDate = function () {
				return $scope.IssueMon + '/' + $scope.IssueDay + '/' + $scope.IssueYear;
			};

			$scope.getExpDate = function () {
				return $scope.ExpMon + '/' + $scope.ExpDay + '/' + $scope.ExpYear;
			};

			if (attrs && attrs.reAuthFields) $scope.reAuthFields = attrs.reAuthFields.split(',');

			$timeout(registerReAuthFields, 0);

			function registerReAuthFields() {
				if ($scope.reAuthFields) {
					$scope.reAuthFields.forEach(function (fieldName) {
						haReAuthService.registerField(fieldName)
					});
				}
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: haProfileSettingsLink,
			controller: haProfileSettingsController
		};
	}]);

	module.directive('countryRegexPattern', function () {
		return {
			require: 'ngModel',
			link: function (scope, element, attrs, modelCtrl) {
				var zipRegex = new RegExp(attrs.countryRegexPattern);
				element.on('blur', function (/* evt */) {
					scope.ValidateZipCode();
				});

				scope.$watch(attrs.ngModel, function () {
					scope.ValidateZipCode();
				});

				scope.ValidateZipCode = function () {
					scope.safeApply(function () {
						if (element.val() && element.val() !== '') {
							if (!zipRegex.test(element.val())) {
								modelCtrl.$setValidity('CountryRegexPattern', false);
							} else {
								modelCtrl.$setValidity('CountryRegexPattern', true);
							}
						} else {
							modelCtrl.$setValidity('CountryRegexPattern', true);
						}
					});
				};

				scope.safeApply = function (fn) {
					var phase = this.$root.$$phase;
					if (phase === '$apply' || phase === '$digest') {
						if (fn && (typeof (fn) === 'function')) {
							fn();
						}
					} else {
						this.$apply(fn);
					}
				};


			}
		};
	});

})(angular);
;
(function (angular) {

	// Ha Payment Methods
	// --------------------------------------------
	//
	// * **Class:** HaPaymentMethods
	// * **Author:** Curtis Floth
	//
	// Payment methods management module

	'use strict';

	var module = angular.module('haPaymentMethodsModule', ['haPaymentMethodsAPI']);

	module.directive('haPaymentMethods', ['haGlobals', '$window', 'haPaymentMethodsAPI', 'haConfig', 'haSitecoreStrings',
		function (haGlobals, $window, haPaymentMethodsAPI, haConfig, $scs) {

			var HaPaymentMethodsController = function ($scope, haModal, $rootScope, haConfig) {
				$scope.PaymentMethods = [];
				$scope.MaxPaymentMethods = 6;
				$scope.maxCardsReachedMsg = angular.element('#maxCardsReachedMsg').text();
				$scope.CCTypes = [];

				haGlobals('jsonPaymentMethodsModel', function (jsonPaymentMethodsModel) {
					// console.log('model ///////////////////////');
					// console.log(jsonPaymentMethodsModel);
					angular.forEach(jsonPaymentMethodsModel.PaymentInfos, function (item) {
						item.CCInfo.UpdatedDate = new Date(parseInt(item.CCInfo.UpdatedDate.substr(6)));
					});
					$scope.PaymentMethods = jsonPaymentMethodsModel.PaymentInfos;
					// mocked expired card
					/*
					 $scope.PaymentMethods.push({
					 BillingAddress: {},
					 CCInfo: {
					 CCId: 1234567,
					 CCV: 0,
					 CardNumber: "------------1234",
					 CardType: 'AX',
					 CreateDate: "/Date(1424483379000)/",
					 ExpirationMonth: "2",
					 ExpirationYear: "2015",
					 FirstName: "Peter",
					 IsDefaultCard: false,
					 LastName: "Haltest",
					 NickName: "Pete-Amex",
					 UpdatedDate: new Date()
					 });
					 // mocked promo model
					 $scope.CreditCardPromo = {
					 IsUserEligible: true,
					 Title: 'Credit Card Offer',
					 Headline: 'Earn 35,000 Bonus Miles',
					 CallToAction: 'Learn More and Apply',
					 LinkURL: 'http://google.com',
					 BackgroundImageURL: 'http://placehold.it/315x192'
					 }
					 */
					$scope.MaxPaymentMethods = jsonPaymentMethodsModel.MaxPaymentMethods;
					$scope.CCTypes = jsonPaymentMethodsModel.CCTypesDropDown;
				});

				haGlobals('errorMessage', function (errorMessage) {
					if (errorMessage !== '') {
						$scope.PaymentMethodsErrorType = 'error';
						$scope.PaymentMethodsHeaderMessage = errorMessage;
						$scope.ShowPaymentMethodsAlert = true;
					}
				});

				$scs.get('PaymentMethods').then(function (data) {
					$scope.strings = data;
				});

				$scope.visitApplyLink = function () {
					// this needs to be updated with a link from sitecore
					$window.location = '~/';
				};

				$scope.GetCardName = function (cardType) {
					if ($scope.CCTypes.length > 0) {
						var item = $($scope.CCTypes).filter(function () {
							return this.Value === cardType;
						}).get(0);
						if (item) {
							return item.Name;
						} else {
							return cardType;
						}
					}
					return cardType;
				};

				$scope.truncateCC = function (ccNumber) {
					return ccNumber.substr(ccNumber.length - 4);
				};

				$scope.termsStart = function() {
					$rootScope.$broadcast('termsModalStart');
					var termsName = 'termsAndConditions';

					haModal(haConfig.getTemplateUrl('ha-kisa-terms-modal.html'), {
						id: 'kisaTermsModal',
						backdrop: true,
						modalLock: true,
						scope: $scope,
						extendScope: {
							scContent: $window[termsName],
							termsName: termsName
						}
					});
				};
				$rootScope.$on('termsModalSuccess', function() {
					$scope.addPaymentMethod();
				});

				$scope.addPaymentMethod = function() {
					if ($scope.PaymentMethods.length >= $scope.MaxPaymentMethods) {
						$scope.PaymentMethodsErrorType = 'info';
						$scope.PaymentMethodsHeaderMessage = $scope.maxCardsReachedMsg.trim();
						$scope.ShowPaymentMethodsAlert = true;
						return;
					}
					else if ($scope.PaymentMethods.length > 0) {
						// pre-populate billing address
						$rootScope.BillingAddress = $scope.PaymentMethods[0].BillingAddress;
					}
					else {
						$rootScope.BillingAddress = {};
					}

					haPaymentMethodsAPI.PaymentMethodsAddInitialize().success(function (response) {
						if (response.IsSuccess) {
							$scope.CDEIFrameSrc = '/PaymentAuthorization';
							if ($scope.$switch('Global:EnableVIC') || $scope.$root.vicilicious) {
								$scope.CDEIFrameSrc += '?vicilicious=true';
							}
							haModal(haConfig.getTemplateUrl('ha-payment-method-add-modal.html'), {
								id: 'add-Payment',
								backdrop: 'false',
								scope: $scope
							});
					}
					}).error(function (xhr, httpStatusCode) {
						if (httpStatusCode === 403) {
							$rootScope.$broadcast('SessionError');
						}
					});
				};

				$scope.addEditPaymentMethod = function (payment) {
					if (typeof (payment) === 'undefined') {
						if ($scope.PaymentMethods.length >= $scope.MaxPaymentMethods) {
							$scope.PaymentMethodsErrorType = 'info';
							$scope.PaymentMethodsHeaderMessage = $scope.maxCardsReachedMsg.trim();
							$scope.ShowPaymentMethodsAlert = true;
							return;
						}
						else if ($scope.PaymentMethods.length > 0) {
							// pre-populate billing address
							$rootScope.BillingAddress = $scope.PaymentMethods[0].BillingAddress;
						}
						else {
							$rootScope.BillingAddress = {};
						}

					haModal('/MyAccount/PaymentMethods/PaymentMethodAddEdit', {
						id: 'add-edit-Payment',
						backdrop: 'false'
					});

					} else {

						// send ccID for editing, attach random number to avoid cache
						haModal('/MyAccount/PaymentMethods/PaymentMethodAddEdit?ccID=' + payment.CCInfo.CCId + '&rand=' + (Math.floor(Math.random() * 9999) + 1), {
							id: 'add-edit-Payment',
							backdrop: 'false'
						});
					}
				};

				$scope.RedirectToLoginPage = function (response) {
					if (response != null && response.data != null && response.data.RedirectURL != null) {
						window.location.href = response.data.RedirectURL;
					}
					else {
						window.location.href = window.location.href;
					}
				};

				$scope.$on('responseError:UNAUTHORIZED', function (event, response) {
					$scope.RedirectToLoginPage(response);
				});

				$scope.$on('responseError:FORBIDDEN', function (event, response) {
					$scope.RedirectToLoginPage(response);
				});

				$scope.$on('PaymentMethodDeleted', function (event, data) {
					var ccID = data.ccID;
					var response = data.response;
					$scope.ShowMessage(response);
					var item = $($scope.PaymentMethods).filter(function () {
						return this.CCInfo.CCId === ccID;
					}).get(0);
					if (item) {
						var index = $scope.PaymentMethods.indexOf(item);
						$scope.PaymentMethods.splice(index, 1);
					}
				});

				$scope.$on('PaymentMethodAdded', function (event, data) {
					var response = data.response;
					$scope.ShowMessage(response);
					if (response.Data.CCInfo.IsDefaultCard) {
						angular.forEach($scope.PaymentMethods, function (item) {
							item.CCInfo.IsDefaultCard = false;
						});
					}
					response.Data.CCInfo.UpdatedDate = new Date(parseInt(response.Data.CCInfo.UpdatedDate.substr(6), 10));
					$scope.PaymentMethods.push(response.Data);
				});

				$scope.$on('PaymentMethodModified', function (event, data) {
					var response = data.response;
					$scope.ShowMessage(response);

					var item = $($scope.PaymentMethods).filter(function () {
						return this.CCInfo.CCId === response.Data.CCInfo.CCId;
					}).get(0);
					if (item) {
						var index = $scope.PaymentMethods.indexOf(item);
						$scope.PaymentMethods.splice(index, 1);
						if (response.Data.CCInfo.IsDefaultCard) {
							angular.forEach($scope.PaymentMethods, function (item) {
								item.CCInfo.IsDefaultCard = false;
							});
						}
						response.Data.CCInfo.UpdatedDate = new Date(parseInt(response.Data.CCInfo.UpdatedDate.substr(6), 10));
						$scope.PaymentMethods.push(response.Data);
					}

				});

				$scope.$on('SessionError', function () {
					window.location.href = window.location.href;
				});

				$scope.ShowMessage = function (response) {
					$scope.PaymentMethodsErrorType = 'success';
					$scope.PaymentMethodsHeaderMessage = response.Message;
					$scope.ShowPaymentMethodsAlert = true;
					$scope.$broadcast('$showAlert');
				};

				$scope.checkCardExpiration = function (month, year) {
					var today = new Date();
					var expDate = new Date();
					expDate.setFullYear(year, month, 1);
					if (expDate < today) {
						return true;
					} else {
						return false;
					}
				};


				$window.addEventListener("message", function ($event) {
					// Do we trust the sender of this message?
					if ($event.origin !== iframeOrigin) {
						// not sent from our site
						return;
					}

					if ($event.data === 'cancel') {
						// close modal
						$rootScope.$broadcast('$modalCancel');

					} else if ($event.data === 'submitFail') {
						// close modal and show error
						$scs.get('ReviewAndPay.cdeformsubmitfailerror').then(function (error) {
							$rootScope.$broadcast('$modalCancel');
							$scope.PaymentMethodsErrorType = 'error';
							$scope.PaymentMethodsHeaderMessage = error;
							$scope.ShowPaymentMethodsAlert = true;
						});
						
					} else if ($event.data.substring(0, 7) === 'resize:') {
						// resize iframe height
						var list = $event.data.split(':');
						if (list.length === 2) {
							// resize the iframe height
							angular.element('#formIFrame').height(list[1]);
						}
					} else if ($event.data.substring(0, 9) === 'complete:') {
						// submit has completed, get the response
						var responseStr = $event.data.substring(9, $event.data.length);
						var response = JSON.parse(responseStr);

						haPaymentMethodsAPI.PaymentMethodsAdd(response).success(function (response) {
							if (response.IsSuccess) {
								$rootScope.$broadcast('$modalCancel');
								$rootScope.$broadcast('PaymentMethodAdded', {
									'response': response
								});
							} else {
								$rootScope.$broadcast('$modalCancel');
								$scope.PaymentMethodsErrorType = 'error';
								$scope.PaymentMethodsHeaderMessage = response.Message;
								$scope.ShowPaymentMethodsAlert = true;
							}
						}).error(function (xhr, httpStatusCode) {
							if (httpStatusCode === 403 || httpStatusCode === 401) {
								$rootScope.$broadcast('SessionError');
							}
						});
					}
				});
			};

			HaPaymentMethodsController.$inject = ['$scope', 'haModal', '$rootScope', 'haConfig'];

			return {
				restrict: 'A',
				scope: true,
				controller: HaPaymentMethodsController
			};
		}
	]);

	module.directive('haPaymentMethodsAddEdit', ['haGlobals', 'haPaymentMethodsAPI', '$timeout',
		function (haGlobals, haPaymentMethodsAPI, $timeout) {

			var ccBulletEncoded = '&bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull; ';
			var ccBulletDecoded = $('<div/>').html(ccBulletEncoded).text();

			var HaPaymentMethodsAddEditController = function ($scope, haModal, $rootScope) {

				$scope.truncateCC = function (ccNumber) {
					return ccNumber.substr(ccNumber.length - 4);
				};

				$scope.isEdit = false;
				$scope.errorMessage = '';
				$scope.CCInfo = {};
				$scope.BillingAddress = {};

				$scope.ExpirationMonthsDatasource = [];

				haGlobals('jsonPaymentMethodsCCInfoModel', function (jsonPaymentMethodsCCInfoModel) {
					$scope.CCInfo = jsonPaymentMethodsCCInfoModel.CCInfo;
					$scope.BillingAddress = jsonPaymentMethodsCCInfoModel.BillingAddress;

					if ($scope.BillingAddress && $scope.BillingAddress.Country) {
					$timeout(function () {
						$scope.$broadcast('AddressInfoDetails', {
								'AddressInfo': $scope.BillingAddress
						});
					});
					}

					if ($scope.CCInfo.CCId > 0) {
						$scope.isEdit = true;
						$scope.CCInfo.MaskedCardNumber = ccBulletDecoded + $scope.truncateCC($scope.CCInfo.CardNumber);
					}
					});

				$scope.CCTypes = [];
				haGlobals('ccTypes', function (ccTypes) {
					$scope.CCTypes = ccTypes;
				});

				$scope.ExpirationMonths = [];
				haGlobals('ccExpirationMonths', function (ccExpirationMonths) {
					angular.forEach(ccExpirationMonths, function (item) {
						if (item.Value !== '') {
							$scope.ExpirationMonths.push({
								name: item.Name,
								value: item.Value
							});
							$scope.ExpirationMonthsDatasource.push({
								name: item.Name,
								value: item.Value
							});
						}
					});
				});

				$scope.$watch('CCInfo.ExpirationYear', function (newValue) {
					var year = parseInt(newValue, 10);
					var currentYear = new Date().getFullYear();

					if (year === currentYear) {
						var currentMonth = new Date().getMonth() + 1;
						$scope.ExpirationMonths = [];
						angular.forEach($scope.ExpirationMonthsDatasource, function (item) {
							if (parseInt(item.value, 10) >= currentMonth) {
								$scope.ExpirationMonths.push(item);
								if (parseInt($scope.CCInfo.ExpirationMonth) < currentMonth) {
									$scope.CCInfo.ExpirationMonth = '';
								}
							}
						});
					} else {
						$scope.ExpirationMonths = $scope.ExpirationMonthsDatasource;
					}
				});

				$scope.DeleteCreditCard = function (ccID) {
					var alertMsg = $('#PaymentDeleteConfirmation').text();
					if (confirm(alertMsg)) {
						haPaymentMethodsAPI.PaymentMethodsDelete(ccID).success(function (response) {
							if (response.IsSuccess) {
								$scope.$emit('closeModal');
								$rootScope.$broadcast('PaymentMethodDeleted', {
									'response': response,
									'ccID': ccID
								});
							} else {
								$scope.errorMessage = response.Message;
							}
						}).error(function (xhr, httpStatusCode) {
							if (httpStatusCode === 403) {
								$rootScope.$broadcast('SessionError');
							}
						});
					}
				};

				$scope.GetCardName = function (cardType) {
					if ($scope.CCTypes.length > 0) {
						var item = $($scope.CCTypes).filter(function () {
							return this.Value === cardType;
						}).get(0);
						if (item) {
							return item.Name;
						} else {
							return cardType;
						}
					}
					return cardType;
				};

				$scope.submit = function ($event) {

					$event.preventDefault();
					var form = $('#ManagePaymentForm');

					haPaymentMethodsAPI.PaymentMethodsAddEdit($(form).serialize()).success(function (response) {
						if (response.IsSuccess) {
							$scope.$emit('closeModal');
							var broadCastMethod = '';
							if ($scope.CCInfo.CCId === 0) {
								broadCastMethod = 'PaymentMethodAdded';
							} else {
								broadCastMethod = 'PaymentMethodModified';
							}
							$rootScope.$broadcast(broadCastMethod, {
								'response': response
							});
						} else {
							$scope.errorMessage = response.Message;
							$scope.$broadcast('$showAlert');
							//ScrollTop to the error panel
							$('#add-edit-Payment').animate({scrollTop: 0}, 'slow');
						}
					}).error(function (xhr, httpStatusCode) {
						if (httpStatusCode === 403 || httpStatusCode === 401) {
							$rootScope.$broadcast('SessionError');
						}
					});
				};
			};

			HaPaymentMethodsAddEditController.$inject = ['$scope', 'haModal', '$rootScope'];

			return {
				restrict: 'A',
				scope: true,
				controller: HaPaymentMethodsAddEditController
			};
		}
	]);

})(angular);
;
(function (angular) {

    // Ha Receipt
    // --------------------------------------------
    //
    // * **Class:** HaReceipt
    // * **Author:** Josh Nielsen
    //
    // A receipt showing fares and taxes

    'use strict';

    var module = angular.module('haReceiptModule', []);

    module.directive('haReceipt', ['$http', function ($http) {

        var HaReceiptController = function ($rootScope, $scope, $pax) {
            $scope.$emit('grandRoundTripTotalPrice');

            $scope.$pax = $pax;
            $scope.IsTripInsuranceSelected = false;

            $scope.currency = $rootScope.$currency
            // Load package data...
            // haGlobals('HA', function(HA) {
            //     if (HA.packageReceipt != null) {
            //         // isolate response data
            //         var packageReceiptData = HA.packageReceipt

            //         $scope.packageReceipt = true;

            //         // if hotel
            //         if (packageReceiptData.BookedHotel) {
            //             $scope.packageHotel = {};
            //             $scope.packageHotel.name = packageReceiptData.BookedHotel.Name;
            //             $scope.packageHotel.checkInDate = packageReceiptData.BookedHotel.CheckInDate;
            //             $scope.packageHotel.checkOutDate = packageReceiptData.BookedHotel.CheckOutDate;
            //             $scope.packageHotel.rooms = packageReceiptData.BookedHotel.SelectedRooms;
            //             $scope.packageHotel.address = packageReceiptData.BookedHotel.Address;
            //         } else {
            //             $scope.packageHotel = false;
            //         }

            //     } else {
            //         $scope.packageReceipt = false;
            //     }
            // });


            $scope.AddPassengers = function (fareTypes) {
                var pax = [];
                angular.forEach(fareTypes, function (faretype) {
                    if (faretype.Name !== 'Empty' && faretype.Name !== '') {
                        pax.push({
                            type: faretype.Name,
                            isUser: true
                        });
                    }
                });
                $scope.$pax.add(pax);
            };

            $scope.$on('calcTaxes', function () {
                $scope.calcTaxes();
            });

            $scope.addTax = function (tax) {
                var findTax = function (description) {
                    var existingTax;
                    angular.forEach($scope.taxes, function (tax) {
                        if (tax.description === description) {
                            existingTax = tax;
                        }
                    });

                    return existingTax;
                };

                var existingTax = findTax(tax.Description);

                if (existingTax) {
                    existingTax.amount = Number(existingTax.amount) + Number(tax.Amount);
                } else {
                    $scope.taxes.push({
                        DisplayDescription: tax.DisplayDescription,
                        description: tax.Description,
                        amount: tax.Amount
                    });
                }
            };


            $scope.getFarePerTraveller = function (segments, passengerType, noCoerce) {
                var fare;
                var fareAmount = 0.0;
                var fareClass;
                // use selectedSeatClass if set otherwise default to first seat class in list

                angular.forEach(segments, function (segment) {
                    fareClass = $scope.getFareClass(segment);
                    if ($scope.PricingType === 'SegmentFare') {
                        angular.forEach(segment.AvailBookingFares, function (bookingFare) {
                            if (bookingFare.Name.toLowerCase() === fareClass.toLowerCase()) {
                                // try for fareType based on passengerType but fall back to default (Adult) if undefined
                                var origFareType;
                                if (passengerType.toLowerCase() === 'adult') {
                                    origFareType = bookingFare.FareTypes[0];
                                } else if (passengerType.toLowerCase() === 'child') {
                                    origFareType = bookingFare.FareTypes[1];
                                } else if (passengerType.toLowerCase() === 'infant') {
                                    origFareType = bookingFare.FareTypes[2];
                                } else {
                                    origFareType = bookingFare.FareTypes[0];
                                }

                                if (noCoerce) {
                                    fare = origFareType;
                                } else {
                                    fare = origFareType ? origFareType : bookingFare.FareTypes[0];
                                }

                                fareAmount = parseFloat(fare.TotalBaseFare) + parseFloat(fareAmount);
                            }
                        });
                    } else {

                        if (segment.IsRoundTripFare === true) {
                            if (segment.RoundTripFare.BookingClassFare != null) {
                                angular.forEach(segment.RoundTripFare.BookingClassFare.FareTypes, function (fareType) {
                                    if ((passengerType != null) && (fareType.Name.toLowerCase() === passengerType.toLowerCase())) {
                                        fareAmount = parseFloat(fareType.TotalBaseFare) + parseFloat(fareAmount);
                                    }
                                });
                            }
                        }
                        if (segment.isMulticityFare) {
                            var counter = 0;
                            var flightDetails = {};
                            if (segment.selectedSeatClass.toLowerCase() === 'coach') {
                                flightDetails = segment.MulticityStepThroughFares.CoachCellFlightDetails;
                            }
                            else if (segment.selectedSeatClass.toLowerCase() === 'first') {
                                flightDetails = segment.MulticityStepThroughFares.FirstCellFlightDetails;
                            }
                            else if (segment.selectedSeatClass.toLowerCase() === 'business') {
                                flightDetails = segment.MulticityStepThroughFares.FirstCellFlightDetails;
                            }
                            else {
                                flightDetails = segment.MulticityStepThroughFares.CoachCellFlightDetails;
                            }

                            angular.forEach(flightDetails, function (fltDetail) {
                                var matched = true;
                                angular.forEach(fltDetail.AvailBookingFares, function (availfare) {
                                    if ($scope.selectedSegments[counter].selectedSeatClass.toLowerCase() === availfare.Name.toLowerCase() && matched) {
                                        //var fareType = availfare.FareTypes[0] !== undefined ? availfare.FareTypes[0] : '';
                                        var fareType = availfare.FareTypes.length > 0 && availfare.FareTypes !== undefined ? availfare.FareTypes : '';
                                        if (fareType !== '') {
                                            // angular.forEach($pax.passengers, function (passenger) {
                                            // if (passenger.type.toLowerCase() === passengerType.toLowerCase()) {
                                            angular.forEach(fareType, function (fare) {
                                                if (passengerType.toLowerCase() === fare.Name.toLowerCase()) {
                                                    fareAmount = parseFloat(fare.TotalBaseFare) + parseFloat(fareAmount);
                                                }
                                            });
                                            // }
                                            //});
                                        }
                                        matched = false;
                                    }
                                });
                                counter++;
                            });
                        }
                    }
                });

                return fareAmount;
            };

            $scope.getChangeFlightBaseFareDiff = function (segments, passengerType, count, currency) {
                var fare;
                var fareAmount = 0.0;
                var fareClass;
                if (segments && segments.length > 0) {
                    var segment = segments[segments.length - 1];
                    fareClass = $scope.getFareClass(segment);
                    angular.forEach(segment.AvailBookingFares, function (bookingFare) {

                        if (bookingFare.Name.toLowerCase() === fareClass.toLowerCase()) {
                            if (passengerType.toLowerCase() === 'adult') {
                                fare = bookingFare.FareTypes[0];
                            } else if (passengerType.toLowerCase() === 'child') {
                                fare = bookingFare.FareTypes[1];
                            }
                            fareAmount = parseFloat(fare.ExchangeInfo.BaseFareDifference) + parseFloat(fareAmount);
                            if (currency === 'KRW') {
                                if (passengerType.toLowerCase() === 'adult') {
                                    fareAmount = parseFloat(fareAmount) - parseFloat(fare.ExchangeInfo.FuelSurchargeDifference);
                                } else if (passengerType.toLowerCase() === 'child') {
                                    fareAmount = parseFloat(fareAmount) - parseFloat(fare.ExchangeInfo.FuelSurchargeDifference);
                                }
                            }

                        }
                    });
                }


                return fareAmount * count;
            };

            $scope.getSelectedFareType = function (segments) {
                var fareClass;
                var selectedBookingFare;
                if (segments && segments.length > 0) {
                    var segment = segments[segments.length - 1];
                    fareClass = $scope.getFareClass(segment);
                    angular.forEach(segment.AvailBookingFares, function (bookingFare) {
                        if (bookingFare.Name.toLowerCase() === fareClass.toLowerCase()) {
                            selectedBookingFare = bookingFare;
                        }
                    });
                }
                return selectedBookingFare;
            };

            $scope.$watch('selectedSegments', function() {
                $scope.selectedFareType = $scope.getSelectedFareType($scope.selectedSegments);
            });

            $scope.getChangeFlightTaxDiff = function (segments, passengerType, count) {
                var fare;
                var fareAmount = 0.0;
                var fareClass;
                if (segments && segments.length > 0) {
                    var segment = segments[segments.length - 1];
                    fareClass = $scope.getFareClass(segment);
                    angular.forEach(segment.AvailBookingFares, function (bookingFare) {
                        if (bookingFare.Name.toLowerCase() === fareClass.toLowerCase()) {
                            if (passengerType.toLowerCase() === 'adult') {
                                fare = bookingFare.FareTypes[0];
                            } else if (passengerType.toLowerCase() === 'child') {
                                fare = bookingFare.FareTypes[1];
                            }
                            fareAmount = parseFloat(fare.ExchangeInfo.TaxDifference) + parseFloat(fareAmount);
                        }
                    });
                }
                return fareAmount * count;
            };


            $scope.getChangeFlightTotalFareDiff = function (segments) {

                var fare;
                var fareAmount = 0.0;
                var penaltyFee = 0.0;
                var fareClass;

                if (segments && segments.length > 0) {
                    var segment = segments[segments.length - 1];
                    fareClass = $scope.getFareClass(segment);
                    angular.forEach(segment.AvailBookingFares, function (bookingFare) {
                        if (bookingFare.Name.toLowerCase() === fareClass.toLowerCase()) {

                            angular.forEach($pax.passengers, function (passenger) {

                                if (passenger.type.toLowerCase() === 'adult') {
                                    fare = bookingFare.FareTypes[0];
                                } else if (passenger.type.toLowerCase() === 'child') {
                                    fare = bookingFare.FareTypes[1];
                                }

                                if ($scope.isFlightResult && typeof ($scope.isFlightResult) !== 'undefined') {
                                    penaltyFee = fare.ExchangeInfo.IsFeewaiverAppliedToItinerary ? 0.0: parseFloat(fare.ExchangeInfo.PenaltyFee);
                                } else
                                {
                                   penaltyFee = parseFloat(fare.ExchangeInfo.PenaltyFee);
                                }

                                fareAmount = (parseFloat(fare.ExchangeInfo.BaseFareDifference) + parseFloat(fare.ExchangeInfo.FuelSurchargeDifference) + parseFloat(fare.ExchangeInfo.TaxDifference) + penaltyFee + parseFloat(fare.ExchangeInfo.ExternalServiceFee)) + parseFloat(fareAmount);
                            });
                        }
                    });
                }
                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.TotalPremiumSeatAmount && !$scope.$parent.TripSummary.HasPremiumSeatFareDiff) {
                    fareAmount = parseFloat($scope.$parent.TripSummary.TotalPremiumSeatAmount) + parseFloat(fareAmount);
                }

                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.PremiumSeatFareDiff && $scope.$parent.TripSummary.HasPremiumSeatFareDiff) {
                    fareAmount = parseFloat($scope.$parent.TripSummary.PremiumSeatFareDiff) + parseFloat(fareAmount);
                }
                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.changeFeeWaiver && !$scope.$parent.TripSummary.changeFeeWaiver.IsSameMemberShipType && $scope.$parent.TripSummary.changeFeeWaiver.TotalWaivedFee && $scope.$parent.TripSummary.changeFeeWaiver.IsChangeFeeWaived) {
                    fareAmount = parseFloat(fareAmount) - parseFloat($scope.$parent.TripSummary.changeFeeWaiver.TotalWaivedFee);
                }

                return fareAmount;
            };

            $scope.getChangeFee = function (segments, passengerType, count) {
                var fare;
                var fareAmount = 0.0;
                var fareClass;
                if (segments && segments.length > 0) {
                    var segment = segments[segments.length - 1];
                    fareClass = $scope.getFareClass(segment);
                    angular.forEach(segment.AvailBookingFares, function (bookingFare) {
                        if (bookingFare.Name.toLowerCase() === fareClass.toLowerCase()) {
                            if (passengerType.toLowerCase() === 'adult') {
                                fare = bookingFare.FareTypes[0];
                            } else if (passengerType.toLowerCase() === 'child') {
                                fare = bookingFare.FareTypes[1];
                            }

                            if ($scope.isFlightResult && typeof ($scope.isFlightResult) !== 'undefined') {
                                if (Number(fare.ExchangeInfo.WaivedChangeFee) > -1) {
                                    fareAmount = parseFloat(fare.ExchangeInfo.WaivedChangeFee) + parseFloat(fareAmount);
                                }
                                else {
                                    fareAmount = parseFloat(fare.ExchangeInfo.PenaltyFee) + parseFloat(fareAmount);
                                }
                            }
                            else {
                                fareAmount = parseFloat(fare.ExchangeInfo.PenaltyFee) + parseFloat(fareAmount);
                            }

                        }
                    });
                }
                return fareAmount * count;
            };

            $scope.getFuelSurchargeDifference = function (segments, passengerType, count, currency) {
                var fare;
                var fareAmount = 0.0;
                var fareClass;
                if (segments && segments.length > 0 && currency === 'KRW') {
                    var segment = segments[segments.length - 1];
                    fareClass = $scope.getFareClass(segment);
                    angular.forEach(segment.AvailBookingFares, function (bookingFare) {
                        if (bookingFare.Name.toLowerCase() === fareClass.toLowerCase()) {
                            if (passengerType.toLowerCase() === 'adult') {
                                fare = bookingFare.FareTypes[0];
                            } else if (passengerType.toLowerCase() === 'child') {
                                fare = bookingFare.FareTypes[1];
                            }
                            fareAmount = parseFloat(fare.ExchangeInfo.FuelSurchargeDifference) + parseFloat(fareAmount);

                        }
                    });
                }
                return fareAmount * count;
            };


            $scope.getExternalServiceFee = function (segments, passengerType, count) {
                var fare;
                var fareAmount = 0.0;
                var fareClass;
                if (segments && segments.length > 0) {
                    var segment = segments[segments.length - 1];
                    fareClass = $scope.getFareClass(segment);
                    angular.forEach(segment.AvailBookingFares, function (bookingFare) {
                        if (bookingFare.Name.toLowerCase() === fareClass.toLowerCase()) {
                            if (passengerType.toLowerCase() === 'adult') {
                                fare = bookingFare.FareTypes[0];
                            } else if (passengerType.toLowerCase() === 'child') {
                                fare = bookingFare.FareTypes[1];
                            }
                            fareAmount = parseFloat(fare.ExchangeInfo.ExternalServiceFee) + parseFloat(fareAmount);
                        }
                    });
                }
                return fareAmount * count;
            };


            $scope.getFareClass = function (segment) {
                var seatClass = segment.selectedSeatClass || segment.AvailBookingFares[0].Name.toLowerCase();
                return seatClass;
            };

            $scope.getPassengerType = function (passenger) {
                var passengerType;
                if (passenger.age >= 2 && passenger.age <= 12) { // is Child
                    passengerType = 'Child';
                } else if (passenger.age < 2) {
                    passengerType = 'Infant';
                } else {
                    passengerType = 'Adult';
                }
                return passengerType;
            };

            $scope.UpdateClasses = function () {

                angular.forEach($scope.selectedSegments, function (segment) {
                    var classSelected;
                    classSelected = segment.selectedSeatClass;
                    var classSelectedDisplay;
                    var classExtraComfort;
                    var classCoach;
                    var classFirst;
                    var selectedAvailBookingFare;
                    if (classSelected) {
                        angular.forEach(segment.AvailBookingFares, function (bookingfare) {
                            if (bookingfare.Name.toLowerCase() === classSelected.toLowerCase()) {
                                classSelectedDisplay = bookingfare.DisplayClassName;
                                selectedAvailBookingFare = bookingfare;
                            }
                            if (bookingfare.Name.toLowerCase() === 'extracomfort') {
                                classExtraComfort = bookingfare.DisplayClassName;
                            }
                            if (bookingfare.Name.toLowerCase() === 'coach') {
                                classCoach = bookingfare.DisplayClassName;
                            }
                            if (bookingfare.Name.toLowerCase() === 'first' || bookingfare.Name.toLowerCase() === 'business') {
                                classFirst = bookingfare.DisplayClassName;
                            }
                        });
                    }
                    angular.forEach(segment.Legs, function (leg) {
                        if (classSelected != null && classSelected.toLowerCase() === 'extracomfort') {

                            if (leg.IsExtraComfortAvailable) {
                                leg.selectedClassForDisplay = classExtraComfort;
                            }
                            else if (classCoach) {
                                leg.selectedClassForDisplay = classCoach;
                            }
                        }
                        else if (selectedAvailBookingFare) {
                            if (selectedAvailBookingFare.IsMixedCabin) {
                                if (leg.FirstClass) {
                                    var mixedCabinClass = leg.FirstClass;
                                    if (mixedCabinClass.toLowerCase() === 'coach') {
                                        leg.selectedClassForDisplay = classCoach;
                                    }
                                    else {
                                        leg.selectedClassForDisplay = classFirst;
                                    }
                                }
                            }
                            else {
                                leg.selectedClassForDisplay = classSelectedDisplay;
                            }
                        }

                    });

                });

            };

            $scope.UpdateClasses();


            $scope.calcTaxes = function () {
                var total = 0;
                $scope.taxes = [];
                $scope.isMileageBooking = false;

                $scope.UpdateClasses();

                angular.forEach($scope.selectedSegments, function (segment) {

                    if ($scope.PricingType === 'SegmentFare') {

                        var seatClass = segment[segment.selectedSeatClass];
                        if (seatClass) {
                            angular.forEach($pax.passengers, function (passenger) {
                                var passengerType = passenger.type; //$scope.getPassengerType(passenger);

                                // fareType defaults to Adult if Child fare is unavailble
                                var fare;
                                if (passengerType === 'Adult') {
                                    fare = segment[segment.selectedSeatClass].FareTypes[0];
                                } else if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length > 1) {
                                    fare = segment[segment.selectedSeatClass].FareTypes[1];
                                } else if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length === 1) {
                                    fare = segment[segment.selectedSeatClass].FareTypes[0];
                                } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length > 2) {
                                    fare = segment[segment.selectedSeatClass].FareTypes[2];
                                } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length === 2) {
                                    fare = segment[segment.selectedSeatClass].FareTypes[1];
                                }
                                if (fare) {
                                    angular.forEach(fare.Taxes, function (tax) {
                                        $scope.addTax(tax);
                                    });

                                    total += Number(fare.TotalTaxes);
                                }
                                if (segment.IsMileagePricing === true) {
                                    $scope.isMileageBooking = true;
                                }
                            });
                        }


                    } else {
                        if (segment.IsRoundTripFare === true) {
                            if (segment.RoundTripFare.BookingClassFare != null) {
                                angular.forEach(segment.RoundTripFare.BookingClassFare.FareTypes, function (fareType) {

                                    angular.forEach($pax.passengers, function (passenger) {
                                        if (passenger.type.toLowerCase() === fareType.Name.toLowerCase()) {
                                            angular.forEach(fareType.Taxes, function (tax) {
                                                $scope.addTax(tax);
                                            });
                                            total += Number(fareType.TotalTaxes);
                                        }

                                    });
                                });
                            }
                        }
                        if (segment.isMulticityFare) {
                            var counter = 0;
                            var flightDetails = {};
                            if (segment.selectedSeatClass.toLowerCase() === 'coach') {
                                flightDetails = segment.MulticityStepThroughFares.CoachCellFlightDetails;
                            }
                            else if (segment.selectedSeatClass.toLowerCase() === 'first') {
                                flightDetails = segment.MulticityStepThroughFares.FirstCellFlightDetails;
                            }
                            else if (segment.selectedSeatClass.toLowerCase() === 'business') {
                                flightDetails = segment.MulticityStepThroughFares.FirstCellFlightDetails;
                            }
                            else {
                                flightDetails = segment.MulticityStepThroughFares.CoachCellFlightDetails;
                            }

                            angular.forEach(flightDetails, function (fltDetail) {
                                var matched = true;
                                angular.forEach(fltDetail.AvailBookingFares, function (availfare) {
                                    if ($scope.selectedSegments[counter].selectedSeatClass.toLowerCase() === availfare.Name.toLowerCase() && matched) {
                                        //var fareType = availfare.FareTypes[0] !== undefined ? availfare.FareTypes[0] : '';
                                        var fareType = availfare.FareTypes.length > 0 && availfare.FareTypes !== undefined ? availfare.FareTypes : '';
                                        if (fareType !== '') {
                                            angular.forEach(fareType, function (fare) {
                                                angular.forEach($pax.passengers, function (passenger) {
                                                    if (passenger.type.toLowerCase() === fare.Name.toLowerCase()) {
                                                        angular.forEach(fare.Taxes, function (tax) {
                                                            $scope.addTax(tax);
                                                        });
                                                        total += Number(fare.TotalTaxes);
                                                    }
                                                });
                                            });
                                        }
                                        matched = false;
                                    }
                                });
                                counter++;
                            });
                        }
                    }
                });

                $scope.totalTaxes = total.toFixed(2);
            };

            $scope.calcTaxesHeader = function () {
                var total = 0;
                $scope.isMileageBooking = false;

                // Override taxes with Package pricing...
                if ($scope.SelectedHotel && $scope.SelectedHotel.PackageTaxes) {
                    total = Number($scope.SelectedHotel.PackageTaxes);
                    return total.toFixed(2);
                }

                angular.forEach($scope.selectedSegments, function (segment) {

                    if ($scope.PricingType === 'SegmentFare') {

                        var seatClass = segment[segment.selectedSeatClass];
                        if (seatClass) {
                            angular.forEach($pax.passengers, function (passenger) {
                                var passengerType = passenger.type; //$scope.getPassengerType(passenger);

                                // fareType defaults to Adult if Child fare is unavailble
                                var fare;
                                if (passengerType === 'Adult') {
                                    fare = segment[segment.selectedSeatClass].FareTypes[0];
                                } else if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length > 1) {
                                    fare = segment[segment.selectedSeatClass].FareTypes[1];
                                } else if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length === 1) {
                                    fare = segment[segment.selectedSeatClass].FareTypes[0];
                                } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length > 2) {
                                    fare = segment[segment.selectedSeatClass].FareTypes[2];
                                } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length === 2) {
                                    fare = segment[segment.selectedSeatClass].FareTypes[1];
                                }
                                if (fare) {
                                    total += Number(fare.TotalTaxes);
                                }
                                if (segment.IsMileagePricing === true) {
                                    $scope.isMileageBooking = true;
                                }
                            });
                        }


                    } else {
                        if (segment.IsRoundTripFare === true) {
                            if (segment.RoundTripFare.BookingClassFare != null) {
                                angular.forEach(segment.RoundTripFare.BookingClassFare.FareTypes, function (fareType) {

                                    angular.forEach($pax.passengers, function (passenger) {
                                        if (passenger.type.toLowerCase() === fareType.Name.toLowerCase()) {
                                            total += Number(fareType.TotalTaxes);
                                        }
                                    });
                                });
                            }
                        }
                        if (segment.isMulticityFare) {
                            var counter = 0;
                            var flightDetails = {};
                            if (segment.selectedSeatClass.toLowerCase() === 'coach') {
                                flightDetails = segment.MulticityStepThroughFares.CoachCellFlightDetails;
                            }
                            else if (segment.selectedSeatClass.toLowerCase() === 'first') {
                                flightDetails = segment.MulticityStepThroughFares.FirstCellFlightDetails;
                            }
                            else if (segment.selectedSeatClass.toLowerCase() === 'business') {
                                flightDetails = segment.MulticityStepThroughFares.FirstCellFlightDetails;
                            }
                            else {
                                flightDetails = segment.MulticityStepThroughFares.CoachCellFlightDetails;
                            }

                            angular.forEach(flightDetails, function (fltDetail) {
                                var matched = true;
                                angular.forEach(fltDetail.AvailBookingFares, function (availfare) {
                                    if ($scope.selectedSegments[counter].selectedSeatClass.toLowerCase() === availfare.Name.toLowerCase() && matched) {
                                        //var fareType = availfare.FareTypes[0] !== undefined ? availfare.FareTypes[0] : '';
                                        var fareType = availfare.FareTypes.length > 0 && availfare.FareTypes !== undefined ? availfare.FareTypes : '';
                                        if (fareType !== '') {
                                            angular.forEach(fareType, function (fare) {
                                                angular.forEach($pax.passengers, function (passenger) {
                                                    if (passenger.type.toLowerCase() === fare.Name.toLowerCase()) {
                                                        total += Number(fare.TotalTaxes);
                                                    }
                                                });
                                            });
                                        }
                                        matched = false;
                                    }
                                });
                                counter++;
                            });
                        }
                    }
                });

                return total.toFixed(2);
            };

            $scope.calculateTaxes = function (d) {

                $scope.calcTaxes();

                $scope.UpdateClasses();

                if (d === 'Calculate') {
                    $scope.$parent.CalculateTaxesVar = '';
                    $scope.displayTaxes = $scope.taxes;
                }
                return $scope.totalTaxes;
            };


            $scope.ToggleTaxDetail = function () {
                if ($scope.taxDetails) {
                    $scope.taxDetails = false;
                } else {
                    $scope.taxDetails = true;
                }
            };

            $scope.addTripInsuranceToItinerary = function () {
                $scope.IsTripInsuranceSelected = true;
                if ($scope.IsTripInsuranceSelected) {

                    $http.post('/Book/Payment/AddTripInsuranceToItinerary').success(function (result) {
                        $scope.$parent.TripSummary.FinalCost = result.TotalTripCost;
                        $scope.grandRoundTripTotalPrice();
                        //console.log(result);
                    }).error(function () {
                        console.error('Error in updating selected trip insurance');
                    });
                   $('body, html').animate({scrollTop: 135}, 'slow');
                }
            };

            $scope.grandTotal = function () {
                var total = 0;
                angular.forEach($scope.selectedSegments, function (segment) {
                    var seatClass = segment[segment.selectedSeatClass];
                    if (seatClass) {
                        angular.forEach($pax.passengers, function (passenger) {
                            var passengerType = passenger.type; //$scope.getPassengerType(passenger);

                            // fareType defaults to Adult if Child fare is unavailble
                            var fareType = seatClass.FareTypes[0] ? passengerType : 'Adult';

                            var fare = segment[segment.selectedSeatClass].FareTypes[0];

                            if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length > 1) {
                                fare = segment[segment.selectedSeatClass].FareTypes[1];
                                fareType = 'Child';
                            } else if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length === 1) {
                                fare = segment[segment.selectedSeatClass].FareTypes[0];
                                fareType = 'Child';
                            } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length > 2) {
                                fare = segment[segment.selectedSeatClass].FareTypes[2];
                                fareType = 'Infant';
                            } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length === 2) {
                                fare = segment[segment.selectedSeatClass].FareTypes[1];
                                fareType = 'Infant';
                            }

                            if (segment.IsMileagePricing === false) {
                                total += (Number(fare.TotalDisplayFare) + Number(fare.TotalTaxes));
                            } else {
                                total += Number(fare.TotalDisplayFare);
                            }
                        });
                    }
                });
                return total.toFixed(2);
            };

            $scope.grandTotalPrice = function () {
                var total = 0;
                angular.forEach($scope.selectedSegments, function (segment) {
                    var seatClass = segment[segment.selectedSeatClass];
                    if (seatClass) {
                        angular.forEach($pax.passengers, function (passenger) {
                            var passengerType = passenger.type; //$scope.getPassengerType(passenger);

                            // fareType defaults to Adult if Child fare is unavailble
                            var fare;
                            if (passengerType === 'Adult') {
                                fare = segment[segment.selectedSeatClass].FareTypes[0];
                            }
                            if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length > 1) {
                                fare = segment[segment.selectedSeatClass].FareTypes[1];

                            } else if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length === 1) {
                                fare = segment[segment.selectedSeatClass].FareTypes[0];

                            } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length > 2) {
                                fare = segment[segment.selectedSeatClass].FareTypes[2];

                            } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length === 2) {
                                fare = segment[segment.selectedSeatClass].FareTypes[1];
                            }

                            if (fare) {
                                if (segment.IsMileagePricing === false) {
                                    total += Number(fare.TotalDisplayFare);
                                } else {
                                    total += Number(fare.TotalTaxes);
                                }
                            }
                        });
                    }
                });

                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.TotalCostToPurchaseMiles) {
                    total += Number($scope.$parent.TripSummary.TotalCostToPurchaseMiles);
                }
                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.TotalPremiumSeatAmount) {
                    total += Number($scope.$parent.TripSummary.TotalPremiumSeatAmount);
                }

                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.UnselectedExtraComfortSeatsDowngradeAmount) {
                    total -= Number($scope.$parent.TripSummary.UnselectedExtraComfortSeatsDowngradeAmount);
                }

                return total.toFixed(2);
            };

            $scope.grandRoundTripTotalPrice = function () {
                var total = 0;
                //if ($scope.selectedSegments.length <= 1) { // Can't have this check because End-on-End will use RoundTrip pricing for all trip types
                //    return;
                //}
                angular.forEach($scope.selectedSegments, function (segment) {


                    if (segment.IsRoundTripFare === true) {
                        if (segment.RoundTripFare.BookingClassFare != null) {
                            angular.forEach(segment.RoundTripFare.BookingClassFare.FareTypes, function (fareType) {
                                angular.forEach($pax.passengers, function (passenger) {
                                    if (passenger.type.toLowerCase() === fareType.Name.toLowerCase()) {
                                        total += Number(fareType.TotalDisplayFare);
                                    }
                                });

                            });
                        }
                    } else if (segment.isMulticityFare) {

                        var counter = 0;
                        var flightDetails = {};
                        if (segment.selectedSeatClass.toLowerCase() === 'coach') {
                            flightDetails = segment.MulticityStepThroughFares.CoachCellFlightDetails;
                        }
                        else if (segment.selectedSeatClass.toLowerCase() === 'first') {
                            flightDetails = segment.MulticityStepThroughFares.FirstCellFlightDetails;
                        }
                        else if (segment.selectedSeatClass.toLowerCase() === 'business') {
                            flightDetails = segment.MulticityStepThroughFares.FirstCellFlightDetails;
                        }
                        else {
                            flightDetails = segment.MulticityStepThroughFares.CoachCellFlightDetails;
                        }

                        angular.forEach(flightDetails, function (fltDetail) {

                            var matched = true;
                            angular.forEach(fltDetail.AvailBookingFares, function (availfare) {
                                if ($scope.selectedSegments[counter].selectedSeatClass.toLowerCase() === availfare.Name.toLowerCase() && matched) {

                                    var fareType = availfare.FareTypes.length > 0 && availfare.FareTypes !== undefined ? availfare.FareTypes : '';
                                    if (fareType !== '') {
                                        angular.forEach(fareType, function (fare) {
                                            angular.forEach($pax.passengers, function (passenger) {
                                                if (fare.Name.toLowerCase() === passenger.type.toLowerCase()) {
                                                    total += Number(fare.TotalDisplayFare);
                                                }
                                            });
                                        });
                                    }

                                    matched = false;
                                }

                            });

                            counter++;

                        });
                    }

                });

                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.TotalCostToPurchaseMiles) {
                    total += Number($scope.$parent.TripSummary.TotalCostToPurchaseMiles);
                }
                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.TotalPremiumSeatAmount) {
                    total += Number($scope.$parent.TripSummary.TotalPremiumSeatAmount);
                }

                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.UnselectedExtraComfortSeatsDowngradeAmount) {
                    total -= Number($scope.$parent.TripSummary.UnselectedExtraComfortSeatsDowngradeAmount);
                }

                // Override grand total with Package pricing...
                if ($scope.SelectedHotel && $scope.SelectedHotel.PackageGrandTotal) {
                    total = Number($scope.SelectedHotel.PackageGrandTotal);
                }

                if ($scope.IsTripInsuranceSelected) {
                    total = Number($scope.$parent.TripSummary.FinalCost);
                }

                return total.toFixed(2);
            };

            $scope.grandTotalMileage = function () {
                var total = 0;
                angular.forEach($scope.selectedSegments, function (segment) {
                    var seatClass = segment[segment.selectedSeatClass];
                    if (seatClass) {
                        angular.forEach($pax.passengers, function (passenger) {
                            var passengerType = passenger.type; //$scope.getPassengerType(passenger);

                            // fareType defaults to Adult if Child fare is unavailble
                            var fare;
                            if (passengerType === 'Adult') {
                                fare = segment[segment.selectedSeatClass].FareTypes[0];
                            }
                            if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length > 1) {
                                fare = segment[segment.selectedSeatClass].FareTypes[1];
                            } else if (passengerType === 'Child' && segment[segment.selectedSeatClass].FareTypes.length === 1) {
                                fare = segment[segment.selectedSeatClass].FareTypes[0];
                            } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length > 2) {
                                fare = segment[segment.selectedSeatClass].FareTypes[2];
                            } else if (passengerType === 'Infant' && segment[segment.selectedSeatClass].FareTypes.length === 2) {
                                fare = segment[segment.selectedSeatClass].FareTypes[1];
                            }

                            if (fare) {
                                if (segment.IsMileagePricing === true) {
                                    total += Number(fare.TotalDisplayFare);
                                }
                            }
                        });
                    }
                });

                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.TotalPremiumSeatAmount && !$scope.$parent.TripSummary.IsMileagePricing) {
                    total += Number($scope.$parent.TripSummary.TotalPremiumSeatAmount);
                }

                if ($scope.$parent && $scope.$parent.TripSummary && $scope.$parent.TripSummary.UnselectedExtraComfortSeatsDowngradeAmount && !$scope.$parent.TripSummary.IsMileagePricing) {
                    total -= Number($scope.$parent.TripSummary.UnselectedExtraComfortSeatsDowngradeAmount);
                }


                return total.toFixed(2);
            };


            $scope.inFlightOptions = function () {
                var options = [];
                if ($scope.seatSelection == null || $scope.seatSelection.Legs == null) {
                    return;
                }
                angular.forEach($scope.seatSelection.Legs, function (leg) {
                    if ((leg.SeatSelections != null) && (leg.SeatSelections.InFlightOptions != null) && (leg.SeatSelections.InFlightOptions.length > 0)) {
                        angular.forEach(leg.SeatSelections.InFlightOptions, function (inFlightOption) {
                            options = options.concat(inFlightOption);
                        });
                    }
                });

                if (options.length === 0 && $scope.seatSelection.InFlightOptions != null && $scope.seatSelection.InFlightOptions.length > 0) {
                    angular.forEach($scope.seatSelection.InFlightOptions, function (inFlightOption) {
                        options = options.concat(inFlightOption);
                    });
                }
                return options;
            };
        };

        HaReceiptController.$inject = ['$rootScope', '$scope', 'haPassengersService'];

        var HaReceiptLink = function ($scope) {

            $scope.exampleMethod = function () {
                return $scope;
            };
        };

        return {
            restrict: 'A',
            scope: true,
            link: HaReceiptLink,
            controller: HaReceiptController
        };
    }]);
})(angular);
;
(function (angular) {

	// Ha Receipt
	// --------------------------------------------
	//
	// * **Class:** HaReceipt
	// * **Author:** Josh Nielsen
	// * **Modified By:** Mari Yamaha
	//
	// A receipt showing fares and taxes

	'use strict';

	var module = angular.module('haReceiptEndOnEndModule', []);

	module.directive('haReceiptEndOnEnd', function () {

		var HaReceiptEndOnEndController = function ($rootScope, $scope, $pax) {

			$scope.totalTaxes = 0; // to display total taxes
			$scope.taxes = []; // to list tax details

			$scope.$pax = $pax;

			$scope.$on('calcTaxes', function () {
				$scope.calcTaxes();
			});

			// method to return the fare per traveller
			$scope.getFarePerTraveller = function (fareDetail, paxTypeNum, isCompanionEcert) {
				var fare;
				if (isCompanionEcert != null && isCompanionEcert) {
					// find the fare for the passenger
					angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
						if (+fareType.PaxType === +paxTypeNum && fareType.IsCompanion) {
							fare = fareType.TotalBaseFare;
						}
					});

					// if no fare found, default to WEB
					if (!fare) {
						angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
							if (+fareType.PaxType === 0 && fareType.IsCompanion) {
								fare = fareType.TotalBaseFare;
							}
						});
					}

					return fare;
				}


				// find the fare for the passenger
				angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
					if (+fareType.PaxType === paxTypeNum && !fareType.IsCompanion) {
						fare = fareType.TotalBaseFare;
					}
				});

				// if no fare found, default to WEB
				if (!fare) {
					angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
						if (+fareType.PaxType === 0 && !fareType.IsCompanion) {
							fare = fareType.TotalBaseFare;
						}
					});
				}

				return fare;
			};

			// method to calculate the tax
			$scope.calcTaxes = function () {
				var total = 0;
				var found;
				var isCompanionTaxAdded = false;
				$scope.taxes = [];

				var fareDetail = $scope.TripSummary.FareDetails.AvailGridTrips.AvailGridFareDetail;

				angular.forEach($pax.passengers, function (passenger) {

					// first search for the passenger fare type
					found = false;
					if (passenger.type === 'Adult' && isCompanionTaxAdded) {
						angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
							if ($scope.getPaxTypeNum(passenger.type) === fareType.PaxType && !fareType.IsCompanion) {
								angular.forEach(fareType.AvailGridTaxes, function (tax) {
									$scope.addTax(tax);
								});
								total += Number(fareType.TotalTax);
								found = true;
							}
						});
					} else {
						angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
							if ($scope.getPaxTypeNum(passenger.type) === fareType.PaxType && fareType.IsCompanion) {
								angular.forEach(fareType.AvailGridTaxes, function (tax) {
									$scope.addTax(tax);
								});
								total += Number(fareType.TotalTax);
								found = true;
								isCompanionTaxAdded = true;
							}
						});
					}

					// if not found, default to WEB type
					if (!found) {
						angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
							if (+fareType.PaxType === 0) {
								angular.forEach(fareType.AvailGridTaxes, function (tax) {
									$scope.addTax(tax);
								});
								total += Number(fareType.TotalTax);
								found = true;
							}
						});
					}

				});

				$scope.totalTaxes = total.toFixed(2);
			};

			// helper method to consolidate tax
			$scope.addTax = function (tax) {
				var findTax = function (taxCode) {
					var existingTax;
					angular.forEach($scope.taxes, function (tax) {
						if (tax.TaxCode === taxCode) {
							existingTax = tax;
						}
					});

					return existingTax;
				};

				var existingTax = findTax(tax.TaxCode);

				if (existingTax) {
					existingTax.TaxAmount = Number(existingTax.TaxAmount) + Number(tax.TaxAmount);
				} else {
					$scope.taxes.push({
						Description: tax.Description,
						TaxAmount: tax.TaxAmount,
						TaxCode: tax.TaxCode
					});
				}
			};

			// toggle tax detail list on off
			$scope.ToggleTaxDetail = function () {
				if ($scope.taxDetails) {
					$scope.taxDetails = false;
				} else {
					$scope.taxDetails = true;
				}
			};

			// get the grand total price
			$scope.grandTotalPrice = function () {
				var total = 0;
				var found;
				var isCompanionTotalAdded = false;
				var fareDetail = $scope.TripSummary.FareDetails.AvailGridTrips.AvailGridFareDetail;

				angular.forEach($pax.passengers, function (passenger) {
					found = false;
					// first search for the passenger fare type
					if (passenger.type === 'Adult' && isCompanionTotalAdded) {
						angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
							if (+$scope.getPaxTypeNum(passenger.type) === +fareType.PaxType && !fareType.IsCompanion) {
								total += Number(fareType.TotalAmount);
								found = true;
							}
						});
					}
					else {
						angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
							if (+$scope.getPaxTypeNum(passenger.type) === +fareType.PaxType && fareType.IsCompanion) {
								total += Number(fareType.TotalAmount);
								found = true;
								isCompanionTotalAdded = true;
							}
						});
					}

					// if not found, default to WEB type
					if (!found) {
						angular.forEach(fareDetail.AvailGridFareTypes, function (fareType) {
							if (+fareType.PaxType === 0) {
								total += Number(fareType.TotalAmount);
							}
						});
					}
				});

				return total.toFixed(2);
			};

			// sort paxType by paxNum
			$scope.getPaxTypeNum = function (paxType) {
				switch (paxType) {
					case 'Adult':
						return 0;
					case 'Child':
						return 1;
					case 'Infant':
						return 2;
					default:
						return false;
				}
			};
		};

		HaReceiptEndOnEndController.$inject = ['$rootScope', '$scope', 'haPassengersService'];

		var HaReceiptEndOnEndLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaReceiptEndOnEndLink,
			controller: HaReceiptEndOnEndController
		};
	});
})(angular);
;
(function (angular) {

	// Ha Account Registration
	// --------------------------------------------
	//
	// * **Class:** HaAccountRegistration
	// * **Author:** Cory Shaw
	//
	// Contains templates and controllers for all of the registration screens

	'use strict';

	var module = angular.module('haAccountRegistrationModule', ['haEmailAPI', 'haUsernameAPI', 'haUtilsModule']);

	module.directive('haAccountRegistration', [
		'$log',
		'$window',
		'$timeout',
		'haGlobals',
		'haUtils',
		'haHttpService',
		'haGeoDataSvc',

		function ($log, $window, $timeout, haGlobals, haUtils, haHttpService, geo) {

			var HaAccountRegistrationController = function ($scope, haModal) {

				var defaultCountry;
				var defaultCountryCode;
				haGlobals(['defaultCountry', 'defaultPhoneCountryCode'], function (dc, dpcc) {
					defaultCountry = dc;
					defaultCountryCode = dpcc;
				});

				// HMRegistration2.cshtml
				haUtils.attachViewModelToScopeAsVM($scope, 'HMRegistrationViewModel', function (VM) {
					VM.AccountDetail = VM.AccountDetail || {};
					if (VM.AccountDetail.Country == null) {
						VM.AccountDetail.Country = defaultCountry;
					}
				});

				$scope.expand = {HM: false};
				$scope.shouldAddHM = function () {
					if ($scope.VM.MemberSubscriptions.ENewsLetters ||
						$scope.VM.MemberSubscriptions.HMPartners) {
						$scope.expand.HM = true;
					}
				};

				$scope.shouldClearSubs = function () {
					$timeout(function () {
						if (!$scope.expand.HM && $scope.VM != null) {
							$scope.VM.MemberSubscriptions.ENewsLetters = false;
							$scope.VM.MemberSubscriptions.HMPartners = false;
						}
					}, 10);
				};

				// HMOptionalRegistration.cshtml & HMUpgrade.cshtml
				haUtils.attachViewModelToScopeAsVM($scope, 'WebOnlyHMRegistrationViewModel', function (VM) {
					VM.AccountDetail = VM.AccountDetail || {};
					if (VM.AccountDetail.Country == null) {
						VM.AccountDetail.Country = defaultCountry;
					}
					$scope.IsReadOnly = (VM.MemberPersonalInfo != null) &&
						(VM.MemberPersonalInfo.FirstName != null) &&
						(VM.MemberPersonalInfo.LastName != null);
					// Is FirstName prefilled?
					//(LastName is assumed the same as both are requried.)
					$scope.namePrefilled = false;
					if ($scope.VM.MemberPersonalInfo.FirstName != null &&
						$scope.VM.MemberPersonalInfo.FirstName.length > 0) {
						$scope.namePrefilled = true;
					}
					$scope.shouldAddHM();
				});

				// Fetch list of Countries
				haGlobals('countryData', function (countryData) {
					$scope.countries = geo.setCountryData(countryData);
				});

				$scope.init = function () {
					$timeout(function () {
						if ($scope.VM.AccountDetail != null && $scope.VM.AccountDetail.EmailAddress != null) {
							angular.element('input[name="AccountDetail.EmailAddress"]').blur();
						}
					}, 10);
				};

				$scope.emailDupe = { allowEmailDupe: false, display: false };

				$scope.getUrlVars = function () {
					var vars = [];
					var hash;
					var hashes = $window.location.href.replace('amp;', '').slice($window.location.href.indexOf('?') + 1).split('&');
					for (var i = 0; i < hashes.length; i++) {
						hash = hashes[i].split('=');
						vars.push(hash[0]);
						vars[hash[0]] = hash[1];
					}
					return vars;
				};

				var vars = $scope.getUrlVars();
				if (typeof (vars.EmailAddress) !== 'undefined') {
					if (typeof ($scope.AccountDetail) !== 'undefined') {
						$scope.AccountDetail.EmailAddress = vars.EmailAddress;
					}
					$scope.ShowHMRegistrationAlert = true;
					$scope.HMRegistrationMessageType = 'success';
					haGlobals('SubscribtionSuccessMessage', function (SubscribtionSuccessMessage) {
						$scope.HMRegistrationHeaderMessage = SubscribtionSuccessMessage;
					});
				}

				if (typeof (vars.Token) !== 'undefined') {
					$scope.IsReadOnly = true;
				}

				$scope.showPrivacyPolicy = function () {
					haModal('/Shared/Modal/GetModalContent?fieldName=ViewPrivacyPolicy&itemId=HMRegistration', {
						id: 'registration-privacy',
						backdrop: 'true'
					});
				};

				$scope.showTerms = function () {
					haModal('/Shared/Modal/GetModalContent?fieldName=ViewTermsFullText&itemId=HMRegistration', {
						id: 'registration-privacy',
						backdrop: 'true'
					});
				};

				var stripDropdowns = function (json) {
					delete json.CountryStateDropDown;
					delete json.SecurityQuestions.SecurityQAPlaceHolder;
					delete json.SecurityQuestions.SecurityQuestionsDropDown;
					delete json.PhoneDetailList.CountryCodeDropDown;
					delete json.PhoneDetailList.PhoneTypeDropDown;
					delete json.MemberPersonalInfo.GenderDropDown;
					delete json.MemberPersonalInfo.SuffixDropDown;
					delete json.MemberPersonalInfo.DOBMonthDropDown;
					delete json.MemberPersonalInfo.DOBDayDropDown;
					delete json.MemberPersonalInfo.DOBYearDropDown;

					return json;
				};

				// Custom submit handler to disable button after the first click
				$scope.onSubmit = function (e, form) {
					e.preventDefault();
					$scope.submitting = false;
					if (form.$valid) {
						$scope.submitting = true;
						window[form.$name].submit();
					}
				};

				$scope.getDOB = function () {
					return $scope.VM.dobMonth + '/' + $scope.VM.dobDay + '/' + $scope.VM.dobYear;
				};

				$scope.submitUpgrade = function (form) {
					if (form.$valid) {
						// Strip out junk. TODO: Move this crap from the VM in ViewBag?
						var json = stripDropdowns(angular.copy($scope.VM));
						json.IsHMChecked = true;
						// Assigning DOB from GetDOB method as ha-member-dob is not setting the value properly.
						json.MemberPersonalInfo.DateOfBirth = $scope.getDOB();

						if ($scope.VM.MemberAddress.City != null) {
							json.MemberAddress.CityKey = $scope.VM.MemberAddress.City.Key;
							json.MemberAddress.City = $scope.VM.MemberAddress.City.DisplayName;
						}
						else if ($scope.VM.MemberAddress.CityName != null) {
							json.MemberAddress.City = $scope.VM.MemberAddress.CityName;
						}
						if ($scope.VM.MemberAddress.State != null) {
							json.MemberAddress.StateKey = $scope.VM.MemberAddress.State.Key;
							json.MemberAddress.State = $scope.VM.MemberAddress.State.Name;
						}
						if ($scope.VM.MemberAddress.Country != null) {
							json.MemberAddress.CountryKey = $scope.VM.MemberAddress.Country.Key;
							json.MemberAddress.Country = $scope.VM.MemberAddress.Country.Name;
						}
						var error = function (error) {
							$log.debug(error.data);
							$scope.ShowHMRegistrationAlert = true;
							$scope.HMRegistrationMessageType = 'error';
							haGlobals('HMUpgradeErrorMessage', function (msg) {
								$scope.HMUpgradeErrorMessage = msg;
							});
						};

						haHttpService.POST('/MyAccount/WebOnlyOrHMRegistration/HMUpgradeJson', json)
						.then(function (response) {
							$log.debug(response.data);
							if (response.data.RedirectUrl != null) {
								$scope.ShowHMRegistrationAlert = true;
								$scope.HMRegistrationMessageType = 'success';
								haGlobals('SubscribtionSuccessMessage', function (SubscribtionSuccessMessage) {
									$scope.HMRegistrationHeaderMessage = SubscribtionSuccessMessage;
								});
								$timeout(function () {
									$window.location = response.data.RedirectUrl;
								}, 750);
							} else {
								$scope.HMUpgradeMessage = response.data.Message;
								error(response);
							}
						}, error);
					}
				};

				$scope.restrictMinors = true; // Do not allow minors to sign up on HM Registration page

				$scope.$watch('registration["MemberPersonalInfo.isMinor"].$modelValue', function (newVal) {
					if (!!newVal && !$scope.minorDobSelected) {
						$scope.minorDobSelected = newVal;
					}
				});
			};

			HaAccountRegistrationController.$inject = ['$scope', 'haModal'];

			return {
				restrict: 'A',
				scope: true,
				controller: HaAccountRegistrationController
			};
		}
	]);

	module.directive('emailDupeCheck', ['$q', 'haEmailAPI', function ($q, haEmailAPI) {
		return {
			require: 'ngModel',
			scope: {
				allowDupeEmail: '=allowEmailDupe',
				displayDupe: '=displayDupe'
			},
			link: function (scope, elem, attrs, ctrl) {
				var canceler;
				scope.previousValue = '';
				scope.safeApply = function (fn) {
					var phase = this.$root.$$phase;
					if (phase === '$apply' || phase === '$digest') {
						if (fn && (typeof (fn) === 'function')) {
							fn();
						}
					} else {
						this.$apply(fn);
					}
				};

				scope.$watch(function () {
					return ctrl.$modelValue;
				}, function (/* newValue */) {
					scope.safeApply(function () {
						ctrl.$setValidity('emailUnique', true);
						scope.displayDupe = false;
						scope.previousValue = '';
						if (canceler) {
							canceler.resolve();
						}
						canceler = $q.defer();
					});
				});

				scope.$watch(function () {
					return scope.allowDupeEmail;
				}, function (newValue, oldValue) {
					if (newValue !== oldValue) {
						scope.safeApply(function () {
							if (newValue === true) {
								scope.displayDupe = true;
								ctrl.$setValidity('emailUnique', true);
							}
							else if (newValue === false) {
								scope.displayDupe = true;
								ctrl.$setValidity('emailUnique', false);
							}
						});
					}
				});

				elem.on('blur', function (/* evt */) {
					if (!scope.isValid() && scope.previousValue !== elem.val()) {
						scope.$apply(function () {
							scope.previousValue = elem.val();
							ctrl.$setValidity('emailUniqueAjax', false);

							if (canceler) {
								canceler.resolve();
							}
							canceler = $q.defer();

							var email = elem.val();
							haEmailAPI.emailAvailabilityCheck(email, canceler)
							.then(function (emailAvailable) {
								scope.allowDupeEmail = undefined;
								ctrl.$setValidity('emailUnique', emailAvailable);
								ctrl.$setValidity('emailUniqueAjax', true);
								scope.$emit('validateForm');
							});
						});
					}
				});

				scope.isValid = function () {
					if (elem.controller('ngModel').$error) {
						var isError = false;
						var obj = elem.controller('ngModel').$error;
						for (var prop in obj) {
							if (obj.hasOwnProperty(prop)) {
								if (prop !== 'emailUniqueAjax' && prop !== 'emailUnique' && obj[prop] === true) {
									isError = true;
									break;
								}
							}
						}
						return isError;
					}
					else {
						return false;
					}
				};
			}
		};
	}]);

	module.directive('haDuplicateUsernameCheck', ['$q', 'haUtils', 'haUsernameAPI', function ($q, haUtils, haUsernameAPI) {
		return {
			require: 'ngModel',
			link: function ($scope, $elem, $attrs, ngModelCtrl) {

				var checkAvailability = haUtils.debounce(function (username, timeoutPromise) {
					haUtils.safeApply($scope, function () {
						ngModelCtrl.$setValidity('duplicateUsernameChecked', false);
						haUsernameAPI.checkAvailability(username, timeoutPromise)
						.then(function (isAvailable) {
							ngModelCtrl.$setValidity('duplicateUsername', isAvailable);
							ngModelCtrl.$setValidity('duplicateUsernameChecked', true);
						});
					});
				}, 150);

				// Reset and cancel
				var canceler;
				var valSet;
				$scope.$watch(function () {
					return ngModelCtrl.$modelValue;
				}, function (/* newVal, oldVal */) {
					if (valSet) {
						if (canceler) {
							canceler.resolve();
						}
						canceler = $q.defer();
						ngModelCtrl.$setValidity('duplicateUsername', true);
						if (ngModelCtrl.$error != null && !ngModelCtrl.$error.required && !ngModelCtrl.$error.pattern) {
							checkAvailability($elem.val(), canceler);
						}
					} else {
						valSet = true;
					}
				});

			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Account Sign In
	// --------------------------------------------
	//
	// * **Class:** HaAccountSignIn
	// * **Author:** Cory Shaw
	//
	// Controller for the login page


	'use strict';

	var module = angular.module('haAccountSignInModule', ['haLoginAPI']);

	module.directive('haAccountSignIn', ['haConfig', 'haGlobals', 'haLoginAPI', 'haSitecoreStrings', '$locale', '$timeout', '$window', '$rootScope',
		function (haConfig, haGlobals, haLoginAPI, $scs, $locale, $timeout, $window, $rootScope) {
			// globals
			var jsonConfirmIdentityModel = $window.jsonConfirmIdentityModel;
			var url = location.href;

			var haAccountSignInController = function ($scope, haModal) {

				$scope.$locale = $locale;
				//Login Model
				$scope.loginModel = {
					UserName: '',
					Password: '',
					RememberMe: false
				};

				//resetpassword model
				$scope.resetpasswordModel = {
					Password: ''
				};

				//forgotpassword model
				$scope.forgotpasswordModel = {
					usernamehmno: ''
				};


				//resetpassword model
				$scope.regform = {
					Password: ''
				};

				$scope.isWorking = false;
				$scope.showResetMessage = false;
				$scope.ShowButtonOrSpinnerOrError = false;
				$scope.LoginHeaderMessage = '';

				$scope.getUrlVars = function () {
					var vars = [];
					var hash;
					var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
					for (var i = 0; i < hashes.length; i++) {
						hash = hashes[i].split('=');
						vars.push(hash[0]);
						vars[hash[0]] = hash[1];
					}
					return vars;
				};

				$scope.AccountSignInError = function (errorMessage, errorType) {
					$scope.ShowLoginHeaderAlert = true;
					$scope.LoginErrorType = errorType;
					$scope.LoginHeaderMessage = errorMessage;
				};
				
				$scope.getNativeAppBannerStringsForCheckIn = function () {
					if (!$rootScope.isMobile) {
						$scs.get('CHECK_IN_NATIVE_APP_BANNER').then(function (data) {
							$scope.nativeAppBannerCookie = 'checkInOrConfirmationNativeAppBanner'; // same as confirmation page
							$scope.nativeAppBannerHeader = data.header;
							$scope.nativeAppBannerLinkText = data.linktext;
							$scope.nativeAppBannerCookieDays = data.cookiedays;
							$scope.checkInNativeAppBannerEnabled = !!data.enable;
						});
					}
				};

				var vars = $scope.getUrlVars();

				if (typeof (vars.isRedirect) !== 'undefined') {
					$scope.ShowLoginHeaderAlert = true;
					$scope.LoginErrorType = 'success';
					haGlobals('SubscribtionSuccessMessage', function (SubscribtionSuccessMessage) {
						$scope.LoginHeaderMessage = SubscribtionSuccessMessage;
					});
				}

				if (vars.indexOf("MaxPasswordChangeAttemptsExceeded") !== -1) {
					$scope.AccountSignInError($window.tooManyPasswordChangeAttemptsMessage, 'error');
				}

				if ((/manage\/check-in/i).test(url)) {
					$scope.getNativeAppBannerStringsForCheckIn();
				}

				haGlobals(['IsUpdateEmailPreferences', 'emailPreferenceLogin', 'SubscribtionSuccessMessage', 'loginModel', 'CM_UserProfileSwitch'],
					function (IsUpdateEmailPreferences, emailPreferenceLogin, SubscribtionSuccessMessage, loginModel, CM_UserProfileSwitch) {
						if (loginModel.UserName != null) {
							$scope.loginModel.UserName = loginModel.UserName;
							$scope.loginModel.RememberMe = true;
						}
						if (IsUpdateEmailPreferences === 'True' && typeof (vars.isRedirect) === 'undefined') {
							$scope.AccountSignInError(emailPreferenceLogin, 'info');
						} else if (typeof (vars.isRedirect) !== 'undefined') {
							$scope.AccountSignInError(SubscribtionSuccessMessage, 'success');
						}
						$scope.CM_UsernameSwitch = CM_UserProfileSwitch; //TODO: to be removed once username switch has to be removed
					}
				);

				haGlobals('jsonValidateSecurityAnswersModel', function (jsonValidateSecurityAnswersModel) {
					$scope.AccountNo = jsonValidateSecurityAnswersModel.AccountNo;
					$scope.SecurityQuestions = jsonValidateSecurityAnswersModel.SecurityQuestions;
					$scope.FirstName = jsonValidateSecurityAnswersModel.FirstName;
					$scope.LastName = jsonValidateSecurityAnswersModel.LastName;
					$scope.PhoneNumber = jsonValidateSecurityAnswersModel.PhoneNumber;
					$scope.ZipCode = jsonValidateSecurityAnswersModel.ZipCode;
					$scope.Country = jsonValidateSecurityAnswersModel.Country;
				});

				haGlobals('jsonForgotUsernameHMNumberModel', function (jsonForgotUsernameHMNumberModel) {
					$scope.EmailIdOrHMNumber = jsonForgotUsernameHMNumberModel.EmailIdOrHMNumber;
					$scope.FirstName = jsonForgotUsernameHMNumberModel.FirstName;
				});

				haGlobals('jsonRetriveUsernameHMNumberModel', function (jsonRetriveUsernameHMNumberModel) {
					$scope.EmailId = jsonRetriveUsernameHMNumberModel.EmailId;
				});

				haGlobals('jsonForgotEmailMemberDetailsModel', function (jsonForgotEmailMemberDetailsModel) {
					$scope.AccountNo = jsonForgotEmailMemberDetailsModel.AccountNo;

					$scope.FirstName = jsonForgotEmailMemberDetailsModel.FirstName;
					$scope.LastName = jsonForgotEmailMemberDetailsModel.LastName;
					$scope.PhoneNumber = jsonForgotEmailMemberDetailsModel.PhoneNumber;
					$scope.ZipCode = jsonForgotEmailMemberDetailsModel.ZipCode;

					if (jsonForgotEmailMemberDetailsModel.Country != null) {
						$scope.Country = jsonForgotEmailMemberDetailsModel.Country;
						// } else if (jsonForgotEmailMemberDetailsModel.CountryDropDown.length > 0) {
						//     $scope.Country = jsonForgotEmailMemberDetailsModel.CountryDropDown[0].Value;
					}
				});

				haGlobals(['isPasswordLinkExpired', 'passwordExpired'], function (isPasswordLinkExpired, passwordExpired) {
					if (isPasswordLinkExpired === 'true') {
						$scope.ShowForgotPasswordAlert = true;
						$scope.ForgotPasswordErrorType = 'error';
						$scope.ForgotPasswordHeaderMessage = passwordExpired;
					}
				});

				haGlobals('subscribedEmailAddress', function (subscribedEmailAddress) {
					$scope.emailaddress = subscribedEmailAddress;
				});

				haGlobals('EmailOnlyPreferencesJson', function (EmailOnlyPreferencesJson) {
					$scope.VM = EmailOnlyPreferencesJson;
				});

				haGlobals('jsonResetEmailAddressModel', function (jsonResetEmailAddressModel) {
					$scope.EmailAddress = jsonResetEmailAddressModel.EmailAddress;
					$scope.ConfirmEmailAddress = jsonResetEmailAddressModel.ConfirmEmailAddress;
				});

				$scope.CreateUsername = function () {

					// NEP: FIXME: Not used.
					// var data = { 'username': this.forgot.username.$modelValue };
					haLoginAPI.CreateUsername(this.forgot.username.$modelValue).success(function (results) {
						if (results.ResponseBaseModel.IsSuccess === true && results.UserAccountInformationViewModel !== undefined) {

							//if WO Account
							if (results.UserAccountInformationViewModel.AccountType === 1) {
								// NEP: FIXME: Not defined. Should not look like constructor.
								/* global ShowModalPopup */
								/* jshint -W064 */
								ShowModalPopup('WOLogin');
								/* jshint +W064 */
							}
							//if HM Account
							else if (results.UserAccountInformationViewModel.AccountType === 2) {
								//ShowModalPopup('HMLogin');
								window.location.href = '/';
							} else {
								window.alert('Invalid AccountType');
							}

						} else {
							$scope.ShowCreateUsernameAlert = true;
							$scope.CreateUsernameErrorType = 'error';
							/* global usernameExists */
							$scope.CreateUsernameHeaderMessage = usernameExists;
						}
					}, function (errorMessage) {
						window.alert(errorMessage);
					});

				};

				// FORM SUBMIT FUNCTIONS

				$scope.submitLogin = function () {
					$scope.ShowButtonOrSpinnerOrError = true;
					$('#ErrorMessage').hide();

					var form = $('#login');
					//$.param($scope.loginModel)
					haLoginAPI.login($(form).serialize()).success(function (response) {
						response = response.loginResponse;
						if (response.IsSuccess) {

							haGlobals(['IsUpdateEmailPreferences', 'redirectEmailSubscriptionsPath'],
								function (IsUpdateEmailPreferences, redirectEmailSubscriptionsPath) {
									if (IsUpdateEmailPreferences === 'True') {
										window.location.href = redirectEmailSubscriptionsPath;
										return;
									}
								}
							);
							window.location.href = response.RedirectURL;
						} else {
							if (response.RedirectURL) {
								window.location.href = response.RedirectURL;
								return;
							}
							$scope.ShowButtonOrSpinnerOrError = false;
							$scope.AccountSignInError(response.TranslateServiceError, 'error');
							$scope.loginModel.Password = '';
							$('#password').focus();
						}
					}).error(function (err) {
						$scope.ShowButtonOrSpinnerOrError = false;
						$scope.AccountSignInError(err, 'error');
						$scope.loginModel.Password = '';
					});
				};


				$scope.submitResetPassword = function ($event) {
					$event.preventDefault();
					$scope.ShowButtonOrSpinnerOrError = true;
					haLoginAPI.UpdatePassword($.param($scope.regform)).success(function (results) {
						$scope.ShowButtonOrSpinnerOrError = false;
						if (results.IsSuccess === true) {
							window.location.href = results.RedirectUrl;
							return;
						} else {
							$scope.ShowResetPasswordAlert = true;
							$scope.ResetPasswordErrorType = 'error';
							$scope.ResetPasswordHeaderMessage = results.TranslateServiceError;
						}
					});
				};

				$scope.submitResetEmailAddress = function ($event) {
					$event.preventDefault();
					$scope.ShowButtonOrSpinnerOrError = true;
					var form = $('#resetemailaddress');
					haLoginAPI.ResetEmailAddress($(form).serialize()).success(function (results) {
						$scope.ShowButtonOrSpinnerOrError = false;
						if (results)
							window.location.href = results.RedirectUrl;
						return;
					});
				};

				$scope.SubmitResetEmail = function () {

					if (this.forgot.$valid) {
						var email = this.forgot.emailtwo.$modelValue;
						haLoginAPI.ResetEmail(email).success(function (results) {
							if (results.IsSuccess) {
								haGlobals('redirectLoginPath', function (redirectLoginPath) {
									window.location.href = redirectLoginPath;
								});
							}
						});
					} else {
						if (this.forgot.email.$error.required) {
							$scope.showResetMessage = true;
							$scope.ResetEMailErrorType = 'error';
							/* global EmptyNewEmail */
							$scope.ResetEMailHeaderMessage = EmptyNewEmail;
						} else if (this.forgot.email.$error.email) {
							$scope.showResetMessage = true;
							$scope.ResetEMailErrorType = 'error';
							/* global NewEmailAddressValid */
							$scope.ResetEMailHeaderMessage = NewEmailAddressValid;
						} else if (this.forgot.emailtwo.$error.required) {
							$scope.showResetMessage = true;
							$scope.ResetEMailErrorType = 'error';
							/* global EmptyConfirmEMail */
							$scope.ResetEMailHeaderMessage = EmptyConfirmEMail;
						} else if (this.forgot.emailtwo.$error.pwmatch) {
							$scope.showResetMessage = true;
							$scope.ResetEMailErrorType = 'error';
							/* global EmailNotMatch */
							$scope.ResetEMailHeaderMessage = EmailNotMatch;
						}
					}

				};

				$scope.UpdatePassword = function () {
					haLoginAPI.UpdatePassword($scope.forgot.password1.$modelValue)
						.success(function (results) {
							if (results.IsSuccess === true) {
								window.location.href = '/?login=1';
							} else {
								$('#LoginError').html(results.Message);
								$('#LoginError').show();
							}

						}, function (err) {
							console.log(err);
							alert(err);
						});

				};

				$scope.UnsubscribeEmailAddressSubmit = function (event) {

					if (this.emailunsubscribe.EmailAddress.$error.email === true || this.emailunsubscribe.EmailAddress.$error.required === true) {
						return false;
					}

					if (event === '1') {
						$scope.IsUnsubscribeAll = false;
					} else {
						$scope.IsUnsubscribeAll = true;
					}

					$scope.EmailAddress = this.emailunsubscribe.EmailAddress.$viewValue;

					haLoginAPI.UnsubscribeEmailAddressSubmit(
						$scope.EmailAddress,
						$scope.UnSubscriptionID,
						$scope.IsUnsubscribeAll
					)
						.success(function (results) {
							if (results.IsSuccess === true) {
								$scope.EmailUnsubscribePasswordAlert = true;
								$scope.EmailUnsubscribeErrorType = 'success';

								haGlobals('SuccessHeaderMessage', function (SuccessHeaderMessage) {
									$scope.EmailUnsubscribeHeaderMessage = SuccessHeaderMessage;
								});
							} else {
								$scope.EmailUnsubscribePasswordAlert = true;
								$scope.EmailUnsubscribeErrorType = 'error';
								$scope.EmailUnsubscribeHeaderMessage = results.Message;
							}

						}, function (err) {
							console.log(err);
							alert(err);
						});
				};


				$scope.UpdateEmailPreferences = function () {

					var EmailAddress = $scope.EmailAddress;
					var UnSubscriptionID = $scope.UnSubscriptionID;
					var IsUnsubscribeAll = $scope.IsUnsubscribeAll;
					if (EmailAddress == null || EmailAddress === '' || EmailAddress === 'undefined') {
						return false;
					}
					haLoginAPI.UpdateEmailPreferencesSubmit(EmailAddress, UnSubscriptionID, IsUnsubscribeAll).success(function (results) {
						if (results.IsSuccess === true) {
							$scope.EmailUnsubscribePasswordAlert = true;
							$scope.EmailOnlyErrorType = 'success';
							haGlobals('SuccessHeaderMessage', function (SuccessHeaderMessage) {
								$scope.EmailOnlyHeaderMessage = SuccessHeaderMessage;
							});
						} else {
							$scope.EmailUnsubscribePasswordAlert = true;
							$scope.EmailOnlyErrorType = 'error';
							haGlobals('HeaderErrorMessage', function (HeaderErrorMessage) {
								$scope.EmailOnlyHeaderMessage = HeaderErrorMessage;
							});
						}

					}, function (err) {
						console.log(err);
						alert(err);
					});

				};


				$scope.SaveEmailOnlyPreferences = function () {

					haLoginAPI.EmailOnlyPreferencesSubmit($scope.VM)
						.then(function (results) {
							if (results.IsSuccess === true) {
								$scope.EmailOnlyPreferences = results.Result.EmailOnlyPreferencesVM;
								$scope.formNotValid = false;
							} else {
								$('#errorMessages').html(results.Result.Message).show();
								$scope.formNotValid = true;
							}

						}, function (err) {
							console.log(err);
						});
				};

				$scope.IsUserNameAvailable = false;

				// *** END HA CODE ***

				$scope.showMemberBenefits = function () {
					haModal(haConfig.getTemplateUrl('ha-member-benefits-modal.html'), {
						id: 'member-benefits',
						backdrop: 'true'
					});
				};

				$scope.$on('haAlertClosed', function (event, alertId) {
					if (alertId === 'ForgotPasswordAlert') {
						$scope.ShowForgotPasswordAlert = false;
					}
				});


				//whitetiger doesn't support this functionality
				$scope.ConfirmIdentityEmailOnlySubscription = function () {
					if (this.forgot.$valid) {
						var zipcode = this.forgot.zipcode.$modelValue;
						var emailaddress = jsonConfirmIdentityModel.EmailAddress;
						haLoginAPI.AuthenticateEmailOnly(emailaddress, zipcode).success(function (results) {
							if (results.IsSuccess) {
								window.location = results.RedirectUrl;
							} else {
								$scope.AccountSignInError(results.TranslateServiceError, 'error');
							}
						});
					}
				};


				$scope.UnsubscribeEmailAddressLoad = function () {
					$scope.UnSubscriptionID = vars.type;
					$scope.EmailAddress = jsonEmailUnsubscrioptionModel.EmailAddress;
					haGlobals(['EmailSubscription1', 'EmailSubscription2', 'EmailSubscription3'],
						function (EmailSubscription1, EmailSubscription2, EmailSubscription3) {
							if (vars.type === '1') {
								$scope.Type = EmailSubscription1;
							} else if (vars.type === '2') {
								$scope.Type = EmailSubscription2;
							} else if (vars.type === '3') {
								$scope.Type = EmailSubscription3;
							}
						});
				};


				$scope.getUrlVars = function () {
					var vars = [];
					var hash;
					var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
					for (var i = 0; i < hashes.length; i++) {
						hash = hashes[i].split('=');
						vars.push(hash[0]);
						vars[hash[0]] = hash[1];
					}
					return vars;
				};

				$scope.Cancel = function () {
					window.location.href = '/';
				};

				$scope.ConfirmIdentityLoad = function (emailAddress) {
					window.location.href = '/EmailOnly/ConfirmIdentityLoad?accountNoOrEmail=' + emailAddress;
				};


				$scope.submitManageTripForm = function (Form) {
					if (Form.$valid) {
						$scope.searchingTrip = true;
						$(Form).submit();
					}
				};

			};

			haAccountSignInController.$inject = ['$scope', 'haModal'];

			return {
				restrict: 'A',
				scope: true,
				controller: haAccountSignInController
			};
		}
	]);
})(angular);
;
(function (angular) {

	// Ha Booking Hero
	// --------------------------------------------
	//
	// * **Class:** HaBookingHero
	// * **Author:** Josh Nielsen
	//
	// Hero banner that displays current booking progress

	'use strict';

	var module = angular.module('haBookingHeroModule', []);

	module.directive('haBookingHero', function () {

		var HaBookingHeroController = function ($scope) {
			$scope.$emit('$haBookingHeroReady');
		};

		HaBookingHeroController.$inject = ['$scope'];

		var HaBookingHeroLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaBookingHeroLink,
			controller: HaBookingHeroController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Itinerary
	// --------------------------------------------
	//
	// * **Class:** HaReviewItinerary
	// * **Author:** Josh Nielsen
	//
	// Itinerary review page module

	'use strict';

	var module = angular.module('haItineraryModule', ['haHttpService']);
    module.directive('haItinerary', ['$rootScope', 'haGlobals', 'haConfig', 'haUtils', 'haHttpService','haAncillariesAPI','haUplift', function ($rootScope, haGlobals, haConfig, haUtils, http, haAncillariesAPI, haUplift) {

		var HaItineraryController = function($scope, haModal, $pax) {
			$scope.IsItineraryLoaded = false;

			$rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off

			$scope.UpdatePassengerList = function() {
				if ($scope.TripSummary.AdultCount > 0) {
					$scope.AddPassengerList($scope.TripSummary.AdultCount, 'Adult');
				}
				if ($scope.TripSummary.ChildCount > 0) {
					$scope.AddPassengerList($scope.TripSummary.ChildCount, 'Child');
				}
				if ($scope.TripSummary.InfantCount > 0) {
					$scope.AddPassengerList($scope.TripSummary.InfantCount, 'Infant');
				}
			};

		    /* Commented this section because we want the session transfer done on button click
			var getSPARedirectionUrl = function (url) {
			    return http.GET(url)
					.then(function (response) {
					    var data = response.data;
					    if (typeof data === "string" && data !== "") {
					        return data;
					    } else {
					        return null;
					    }
					});
			};

			var spaRedirectPath = getSPARedirectionUrl('/Book/Itinerary/GetBookingSpaRedirectPath')
            */
			
			var init = function (itineraryDetails) {
				$.extend($scope, itineraryDetails);
				$scope.TripSummary.TotalPremiumSeatAmount = 0;
				$scope.TripSummary.UnselectedExtraComfortSeatsDowngradeAmount = 0;
				$scope.$emit('$haItineraryReady');
				$scope.$pax = $pax;
				$scope.$pax.passengers = [];
				$scope.selectedSegments = [];
				$scope.currency = $scope.TripSummary.currency;
				//$scope.selectedSegments = $scope.PNRs[0].Trips;
				$scope.PricingType = $scope.TripSummary.PricingType;
				$scope.AddPassengerList = function(count, type) {
					for (var i = 0; i < count; i++) {
						$scope.$pax.add({ type: type, isUser: true });
					}
				};
				$scope.taxDetails = false;

				$scope.UpdatePassengerList();

				 //uplift call to show monthly offer for Itinerary page
				$(document).ready(function () {
					setTimeout(function () {
						haUplift.selectUplift(itineraryDetails.TripSummaryTotal, $scope.PassengerTripSummary, false); 
					}, $rootScope.UpTimeout); 
				});
			};

			haGlobals('itineraryDetails', init);

			$scope.showItineraryHelp = function() {
				haModal(haConfig.getTemplateUrl('ha-help-book-itinerary-modal.html'),
					{
						id: 'itinerary-help',
						backdrop: 'true'
					});
			};

			$scope.ToggleTaxDetails = function() {
				$scope.taxDetails = !$scope.taxDetails;
			};

			//check if MCB is selected
			if (!$scope.IsChangeFlightBooking) {
				$scope.isMainCabinBasicSelected = $scope.PassengerTripSummary.AvailGridTrips.filter(function(item) {
						return item.CabinName === 'MAINCABINBASIC';
					}).length >
					0;
			}


			$("#btn_review_itinerary_continue, #book_held_itinerary_continue_button").on("click",
				function (event) {
				    event.preventDefault();
				    http.GET('/Book/Itinerary/GetBookingSpaRedirectPath')
                        .then(function (response) {
                            var data = response.data;
                            if (typeof data === "string" && data !== "") {
								var redirectUrl = data;
								window.location.href = redirectUrl;								                             
                            }
                            else {
                                $("#itineraryForm").submit();
                            }
                        });
				});

		};

		HaItineraryController.$inject = ['$scope', 'haModal', 'haPassengersService'];

		var HaItineraryLink = function ($scope) {
			$scope.IsItineraryLoaded = false;
			if (($scope.TripSummary.SelectedHotel != null) &&
				(typeof $scope.TripSummary.SelectedHotel.HotelId === 'string')) {
				$scope.SelectedHotel = $scope.TripSummary.SelectedHotel;
			}

			$scope.$watch('TripSummary.SelectedCar', function (SelectedCar) {
				$scope.SelectedCar = SelectedCar;
			});

			angular.forEach($scope.PNRs, function (trip) {
				$scope.IsOrbitzPkgEligible = trip.Ancillaries.IsOrbitzPkgEligible;
			});

			angular.forEach($scope.TripSummary.Trips, function (trip) {
				var segment = trip.Flights[0];
				segment.IsMileagePricing = trip.IsMileagePricing;

				for (var i = 0; i < segment.AvailBookingFares.length; i++) {
					segment.selectedSeatClass = segment.AvailBookingFares[i].Name;
					segment[segment.selectedSeatClass] = segment.AvailBookingFares[i];
				}
				$scope.selectedSegments.push(segment);
				$scope.IsItineraryLoaded = true;
			});
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaItineraryLink,
			controller: HaItineraryController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Itinerary Details Pax Info
	// --------------------------------------------
	//
	// * **Class:** haItineraryDetailsPaxInfo
	// * **Author:** Arjun Chatterjee
	//
	// This is the itinerary details confirmation page once you payment

	'use strict';

	var editTravelerIndex;
	var passengersList;

	var module = angular.module('haItineraryDetailsPaxInfoModule', ['haItineraryAPI']);

	module.directive('haItineraryDetailsPaxInfo', ['$timeout', 'haModal', '$log', '$window', 'haUtils', 'haItineraryAPI',
		function ($timeout, haModal, $log, $window, haUtils, haItineraryAPI) {
		
		var HaItineraryDetailsPaxInfoController = function ($scope, $rootScope, haModal) {
			
			var modifyPaxModalId = 'my-trips-modify-pax';
			$scope.modifyPax = function () {
				$rootScope.editTravelerSuccess = false;
				$rootScope.Passengers = $scope.itinVM.Travellers;
				haModal('/MyAccount/EditPassengerInfo/EditPassengerInfo', {
					id: modifyPaxModalId
				});
			};

			if (PartnerAirlinesContent != null) {
				$scope.partnerAirlinesContent = PartnerAirlinesContent;
			}

			$rootScope.editPax = function (traveler, index) {
				//console.log('edit pax: '+index);
				$rootScope.editingPax = true;
				$rootScope.hasError = false;
				$rootScope.editTravelerSuccess = false;
				$rootScope.EditTraveler = angular.copy(traveler);
				$rootScope.EditTraveler.index = index;
				$rootScope.EditTraveler.HasHMNumber = (traveler.HMNumber && traveler.HMNumber.length > 0) ? 'Yes' : 'No';
				angular.forEach($rootScope.Passengers, function (val, i) {
					if (i === index) {
						$rootScope.Passengers[i].Editing = true;
					} else {
						$rootScope.Passengers[i].Editing = false;
					}
				});

				editTravelerIndex = index;
				$timeout(function () {
					$rootScope.$apply();
					haModal({
						id: 'update-pax-info',
						backdrop: 'true',
						templateUrl: '/editPaxMobile.html',
						scope: $scope
					});
				}, 0);
			};


			$scope.showReshopModal = function () {

				// Open the modal pending
				$scope.displayReshopModal();

				$scope.loading = true;

				$timeout(function () {
					haItineraryAPI.IsEligibleForChangeFlight().then(
						function (results) {
							results = results.data || results;
							if (results.IsEligible) {
								$scope.GetReceiptInfo();
							}
							else {
								// check the message, if it is for the infant then call new method.
								if (!results.ErrorCode && haUtils.checkForInfant($scope.VM) && haUtils.checkIfTicketedInfant($scope.VM)) {
									$scope.GetReceiptInfo();
								} else {
									$scope.Message = results.Message;
									$scope.closeReshopModal();
								}
							}
						},
						function (/* results */) {
							$scope.Message = 'Unable to determine eligibility';
							//console.log("Error Returned From Service");
							$scope.closeReshopModal();
						}
					);
				}, 0);
			};

			$rootScope.saveTraveler = function (traveler, $event) {
				$timeout(function () {
					$rootScope.errorMsg = '';
					$rootScope.hasError = false;
					var travelDate = new Date(traveler.TravelDate);
					travelDate = (travelDate.getMonth() + 1) + '/' + travelDate.getDate() + '/' + travelDate.getFullYear();
					traveler.HMNumber = traveler.HasHMNumber === 'Yes' ? traveler.HMNumber : null;
					$rootScope.currentTraveler = angular.copy(traveler);
					$rootScope.currentTraveler.TravelDate = travelDate;

					var formElement = $($event.target);
					var form = formElement.scope()[formElement.attr('name')];
					if (form.$valid) {

						$rootScope.working = true; // spinner, disable fields
						var reservationCode = $scope.VM.ReservationCode != null ? $scope.VM.ReservationCode : $scope.VM.confItinVM.ReservationCode;
						// Update travler info to the back end.
						haItineraryAPI.updatePassengerInfo(reservationCode, $rootScope.currentTraveler).success(function (results) {
							$scope.$broadcast('$modalCancel', 'update-pax-info');

							if (results.Travellers != null && results.Travellers.length > 0 && results.IsSuccess) {
								//$rootScope.Passengers[$rootScope.EditTraveler.index] = traveler;
								$rootScope.Passengers = results.Travellers;
								angular.forEach($rootScope.Passengers, function (pax, index) {
									if (pax.DOB != null) {
										pax.DOB = new Date(parseInt(pax.DOB.substr(6)));
									}
									pax.TravelDate = traveler.TravelDate;
									if (results.Tickets != null && results.Tickets.length > 0) {
										pax.HMNumber = results.Tickets[index].HawaiianMiles;
										$rootScope.Passengers[index].HMNumber = results.Tickets[index].HawaiianMiles;
										if ($scope.VM.Tickets != null) {
											$scope.VM.Tickets[index].HawaiianMiles = results.Tickets[index].HawaiianMiles;
										}
										if ($scope.itinVM.Tickets != null) {
											$scope.itinVM.Tickets[index].HawaiianMiles = results.Tickets[index].HawaiianMiles;
										}
										if ($scope.itinVM.Travellers != null) {
											$scope.itinVM.Travellers[index].HMNumber = results.Tickets[index].HawaiianMiles;
										}
									}

								});

								passengersList = results.Travellers;
								$rootScope.working = false;
								$rootScope.Passengers[$rootScope.EditTraveler.index].Editing = false;
								$rootScope.editingPax = false;
								$rootScope.editTravelerSuccess = true;
								$rootScope.hasError = false;
							}
							else {
								$rootScope.working = false;
								$rootScope.editTravelerSuccess = false;
								if (results.ErrorMsgDescription.length > 0) {
									$rootScope.errorMsg = results.ErrorMsgDescription;
									$rootScope.hasError = true;
								}
							}

						})
							.error(function (results) {
								$rootScope.working = false;
								$rootScope.editTravelerSuccess = false;
								if (results.ErrorMsgDescription.length > 0) {
									$rootScope.errorMsg = results.ErrorMsgDescription;
									$rootScope.hasError = true;
								}
							});
						$('.ha-modal').animate({ scrollTop: 0 }, 'slow');
						$timeout(function () {
							$rootScope.$apply();
						}, 0);


					}
					else {
						$rootScope.working = false;
						$rootScope.editTravelerSuccess = false;
					}
				}, 0);
			};

			$rootScope.cancelEditPax = function () {
				$rootScope.hasError = false;
				$rootScope.editingPax = false;
				$('.ha-modal').animate({ scrollTop: 0 }, 'slow');
			};

			// on modal close, reset modal
			$rootScope.$on('haModalClosed', function (event, data) {
				if (data === modifyPaxModalId) {
					$rootScope.working = false;
					$rootScope.hasError = false;
					$rootScope.editTravelerSuccess = false;
					$rootScope.editingPax = false;
					for (var i = 0; i < $rootScope.Passengers.length; i++) {
						$rootScope.Passengers[i].Editing = false;
					}
				}
			});
		};

			HaItineraryDetailsPaxInfoController.$inject = ['$scope', '$rootScope', 'haModal'];

		return {
			restrict: 'A',
			scope: true,
			link: function ($scope) {
				$scope.itinVM = $scope.VM.confItinVM ? $scope.VM.confItinVM : $scope.VM ? $scope.VM : {};

				$scs.get('Itinerary_Details_Info.ChangeSeatsErrorMessage').then(function (result) {
					$scope.changeSeatsErrorMsg = result;
				});

				// add flattened flight and seat list
				$scope.itinVM.flightAndSeat = [];
				angular.forEach($scope.itinVM.Segments, function (segment) {
					if (segment.Flights && segment.Flights.length > 0) {
						angular.forEach(segment.Flights, function (flight) {
							var seats = [];
							angular.forEach($scope.itinVM.Seats, function (seat) {
								if (seat.ArrivalCityCode === flight.ArrivalCityCode && seat.DepartureCityCode === flight.DepartureCityCode &&
									seat.SegmentNumber === flight.SegmentNumber) {
									seats[seat.TravellerNameNumber] = seat;
								}
							});
							$scope.itinVM.flightAndSeat.push({ flightInfo: flight, seats: seats });
						});
					} else {
						$scope.itinVM.flightAndSeat.push(null);
					}
				});

				/*
					GROUP PAX CHANGE/SELECT SEATS
				*/

				// max num of pax that can change/select seats at a time
				var MAX_PAX_CHG_SEATS = 7;
				var GROUP_PAX_MODAL_ID = 'SelectGroupPaxModal';

				// create "hidden" names for pax
				function makeHiddenName(first, last) {
					var hiddenName = last.substr(0, 3).toUpperCase();
					hiddenName += ', ' + first.substr(0, 1).toUpperCase() + '.';
					return hiddenName;
				}
				function makeNameToMatch(first, last) {
					var name = first.trim() + ' ' + last.trim();
					name = name.toLowerCase();
					return name;
				}

				// launch modal to change/select seats for group PNR
				$scope.selectTravelersForSeating = function (intent) {

					$scope.VM.selectTravelersIntent = intent || 'view seats';
					$scope.VM.numSelectedGroupPax = 0
					$scope.VM.groupPaxModifyState = 0;
					$scope.resetFindTraveler();
					$scope.findTravelerExpanded = false;
					$scope.exceededPaxAlert = false;

					angular.forEach($scope.Passengers, function (pax) {
						pax.hiddenName = makeHiddenName(pax.FirstName, pax.LastName);
						if (pax.checked) {
							$scope.VM.numSelectedGroupPax++;
						}
					});

					haModal({
						id: GROUP_PAX_MODAL_ID,
						backdrop: 'true',
						templateUrl: '/selectGroupPaxModal.html',
						scope: $scope
					});
					$log.debug('Passengers', $scope.Passengers); //TODO: remove
				};

				$scope.removePax = function (paxId) {
					angular.forEach($scope.Passengers, function (pax) {
						if (pax.TravellerNameNumber === paxId) {
							pax.checked = false;
							$scope.VM.numSelectedGroupPax--;
							pax.DOBDay_input = '';
							pax.DOBMonth_input = '';
						}
					});
				};

				$scope.setForm = function (form) {
					$scope.dobForm = form;
				};
				$scope.resetDOBValidity = function (paxId) {
					angular.forEach($scope.Passengers, function (pax) {
						if (pax.TravellerNameNumber === paxId) {
							$scope.dobForm['dob-month-' + paxId].$setValidity('dob', true);
							$scope.dobForm['dob-day-' + paxId].$setValidity('dob', true);
						}
					});
					$scope.validationErrorAlert = false;
				};

				// handle pax checkbox click
				$scope.toggleGroupPax = function (idx, evt) {

					if (!$scope.Passengers[idx]) { return; }
					$scope.exceededPaxAlert = false;

					if (evt.target.checked) { // add pax
						if ($scope.VM.numSelectedGroupPax < MAX_PAX_CHG_SEATS) {
							$scope.VM.numSelectedGroupPax++;
						} else { // pax limit reached, uncheck
							$scope.Passengers[idx].checked = false;
							$scope.exceededPaxAlert = true;
						}
					} else { // remove pax
						$scope.Passengers[idx].checked = false;
						$scope.VM.numSelectedGroupPax--;
					}
				};

				$scope.selectedPaxPredicate = function (pax) {
					return pax.checked;
				};

				$scope.showTravelersPredicate = function (pax) {
					if (!$scope.VM.IsGroupPNR) {
						return true;
					} else {
						if ($scope.VM.showSelectedPax && pax.checked) {
							return true;
						} else {
							return false;
						}
					}
				};

				$scope.resetGroupPaxSelection = function () {
					angular.forEach($scope.Passengers, function (pax) {
						pax.checked = false;
					});
					$scope.VM.showSelectedPax = false;
					// re-open modal
					$scope.selectTravelersForSeating();
				};

				$scope.resetFindTraveler = function () {
					$scope.findTraveler = {
						firstName: '',
						lastName: '',
						state: 'find'
					};
				};

				// continue to next step in select/change seat process
				$scope.groupPaxNext = function () {
					if ($scope.VM.groupPaxModifyState < 1) {
						$scope.VM.groupPaxModifyState++;
						$scope.resetFindTraveler();
						$scope.findTravelerExpanded = false;
						$scope.exceededPaxAlert = false;
					} else {
						// validate
						var valid = true;
						angular.forEach($scope.Passengers, function (pax) {
							if (pax.checked && (!pax.DOBDay_input || !pax.DOBMonth_input || pax.DOBDay !== pax.DOBDay_input || pax.DOBMonth !== pax.DOBMonth_input)) {
								var id = pax.TravellerNameNumber;
								$scope.dobForm['dob-month-' + id].$setDirty();
								$scope.dobForm['dob-month-' + id].$setValidity('dob', false);
								$scope.dobForm['dob-day-' + id].$setDirty();
								$scope.dobForm['dob-day-' + id].$setValidity('dob', false);
								valid = false;
							}
						});
						if (valid) {
							$scope.VM.showSelectedPax = true;
							if ($scope.VM.selectTravelersIntent === 'view seats') {
								$scope.$root.$broadcast('$modalCancel', GROUP_PAX_MODAL_ID);
							} else {
								$scope.changeSeatsForSelectedPax();
							}
						}
						else {
							$scope.validationErrorAlert = true;
						}
					}
				};
				// go back
				$scope.groupPaxPrevious = function () {
					$scope.VM.groupPaxModifyState--;
				};

				// return array of days for corresponding month selected
				$scope.daysForMonth = function (month) {
					var days = [],
						mon = month || '01',
						// use leap year to get max Feb days
						numDaysInMonth = moment('2016-' + mon, 'YYYY-MM').daysInMonth();
					for (var i = 1; i <= numDaysInMonth; i++) {
						if (i.toString().length === 1) {
							i = "0" + i;
						}
						days.push(i + '');
					}
					return days;
				};

				// change seats
				$scope.changeSeatsForSelectedPax = function () {
					if ($scope.VM.showSelectedPax) {
						// submit
						var travellerList = [];
						angular.forEach($scope.Passengers, function (pax) {
							if (pax.checked) {
								travellerList.push(pax.TravellerNameNumber);
							}
						});
						haItineraryAPI.GroupPNRSelectTravellers(travellerList).success(function (result) {
							if (result && result.IsSuccess && result.RedirectURL) {
								window.location.href = result.RedirectURL;
							} else {
								$scope.VM.groupChangeSeatsError = true;
							}
						})
							.error(function () {
								$scope.VM.groupChangeSeatsError = true;
							});
					}
					else {
						$scope.selectTravelersForSeating('change seats');
					}
				};

				// FIND TRAVELER

				$scope.expandFindTraveler = function () {
					if (!$scope.findTravelerExpanded) {
						// make sure pax limit isn't reached
						if ($scope.VM.numSelectedGroupPax < MAX_PAX_CHG_SEATS) {
							$scope.findTravelerExpanded = true;
							$scope.resetFindTraveler();
						} else {
							$scope.exceededPaxAlert = true;
						}
						return;
					}
					$scope.findTravelerExpanded = false;
				};

				$scope.searchForTraveler = function () {
					$scope.searchedTraveler = $scope.findTraveler.firstName.trim() + ' ' + $scope.findTraveler.lastName.trim();
					var nameToMatch = makeNameToMatch($scope.findTraveler.firstName, $scope.findTraveler.lastName);
					$scope.searchedTravelerHiddenName = makeHiddenName($scope.findTraveler.firstName, $scope.findTraveler.lastName);
					var foundCount = 0, foundIndex;
					// find/check searched traveler
					angular.forEach($scope.Passengers, function (pax, i) {
						var name = makeNameToMatch(pax.FirstName, pax.LastName);
						if (name === nameToMatch) {
							foundCount++;
							foundIndex = i;
						}
					});
					if (foundCount > 0) {
						if (foundCount > 1) {
							$scope.findTraveler.state = 'multiple';
						} else {
							if ($scope.VM.numSelectedGroupPax < MAX_PAX_CHG_SEATS) {
								$scope.findTraveler.state = 'found';
								if (!$scope.Passengers[foundIndex].checked) {
									$scope.Passengers[foundIndex].checked = true;
									$scope.VM.numSelectedGroupPax++;
								}
							}
						}
					} else {
						$scope.findTraveler.state = 'notFound';
					}
				};

				$scope.selectTravelerWithDOB = function () {
					$scope.searchedTraveler = $scope.findTraveler.firstName.trim() + ' ' + $scope.findTraveler.lastName.trim();
					var nameToMatch = makeNameToMatch($scope.findTraveler.firstName, $scope.findTraveler.lastName);
					angular.forEach($scope.Passengers, function (pax, i) {
						var name = makeNameToMatch(pax.FirstName, pax.LastName);
						if (name === nameToMatch && $scope.findTraveler.DOBMonth === pax.DOBMonth && $scope.findTraveler.DOBDay === pax.DOBDay && $scope.VM.numSelectedGroupPax < MAX_PAX_CHG_SEATS) {
							$scope.findTraveler.state = 'found';
							if (!pax.checked) {
								pax.checked = true;
								$scope.VM.numSelectedGroupPax++;
							}
						}
					});
					$scope.findTraveler.showNoMatchMessage = false;
					if ($scope.findTraveler.state !== 'found') {
						$scope.findTraveler.showNoMatchMessage = true;
					}
				};

				$scope.formatString = function (str, array) {
					str = str.replace(/&quot;/g, '\"');
					return haUtils.formatDynamicString(str, array);
				};

				$scope.showChangeSeatsPage = function () {
					if (haUtils.checkForInfant($scope.VM)) {
						if (haUtils.checkIfTicketedInfant($scope.VM)) {
							$window.location.href = '/my-account/my-trips/select-or-upgrade-seats';
						} else {
							$scope.infantChangeSeatsError = true;
						}
					} else {
						$window.location.href = '/my-account/my-trips/select-or-upgrade-seats';
					}
				};

			},
			controller: HaItineraryDetailsPaxInfoController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Flight Hop
	// --------------------------------------------
	//
	// * **Class:** HaFlightHop
	// * **Author:** Josh Nielsen
	//
	// Graphic and metadata describing the different legs of a flight

	'use strict';

	var module = angular.module('haFlightHopModule', []);

	module.directive('haFlightHop', function () {

		var gray40 = '#717072';

		var HaFlightHopController = ['$scope', function ($scope) {

			var getTotalDuration = function (legs) {
				var totalMin = 0;
				//Durations come in like: "5h 50m" or "10h" or "45m"
				legs.forEach(function (leg) {
					var durations = /^(\d+h ?)?(\d+m)?$/.exec(leg.Duration).slice(1);
					leg.totalMin = (parseInt(durations[0] || 0, 10) * 60);//the hours converted to minutes
					leg.totalMin += parseInt(durations[1] || 0, 10);//add the minutes
					totalMin += leg.totalMin;
				});

				return totalMin;
			};

			$scope.flightHop = function (canvas, legs, bgColor) {
				var ctx = canvas.getContext('2d');

				// clear everything
				ctx.clearRect(0, 0, canvas.width, canvas.height);

				// draws airplane png with x,y as its center point
				var drawPlane = function (x, y) {
					//var img = new Image();
					//img.src = '/Content/assets/modules/ha-flight-hop/images/airplane_small_opaque.png';
					//img.onload = function () {
					//    var offsetX = x - (img.width / 2);
					//    var offsetY = y - (img.height / 2);
					//    ctx.drawImage(img, offsetX, offsetY);
					//};

					ctx.beginPath();
					ctx.rect(x - 17, y - 18, 32, 30);
					ctx.fillStyle = bgColor;
					ctx.fill();

					ctx.font = "18px ha-icon";
					ctx.fillStyle = gray40;
					ctx.fillText(String.fromCharCode('0xe61e'), x - 10, y + 6);
				};

				var adjustRatios = function (ratios, min, underMinCount, remainderRatioSum) {
					var adjusted = [];
					var remainderRatio = 1 - (min * underMinCount);
					for (var i = 0; i < ratios.length; i++) {
						var ratio = ratios[i];
						if (ratio > min) {
							ratio = (ratio / remainderRatioSum) * remainderRatio;
						}

						adjusted.push(ratio);
					}

					return adjusted;
				};

				var getDurationRatios = function (legs, totalMin) {
					var minDurationRatio = 0.20;
					var durationRatios = [];
					// get durationRatios
					var underMinCount = 0;
					var remainderRatioSum = 0;

					for (var i = 0; i < legs.length; i++) {
						var durRatio = legs[i].totalMin / totalMin;

						if (durRatio < minDurationRatio) {
							durRatio = minDurationRatio;
							underMinCount++;
						} else {
							remainderRatioSum += durRatio;
						}

						durationRatios.push(durRatio);
					}

					// adjust ratios if needed
					if (underMinCount) {
						durationRatios = adjustRatios(durationRatios, minDurationRatio, underMinCount, remainderRatioSum);
					}

					return durationRatios;

				};

				var drawFlightLegs = function (legs, bgColor) {
					// get total duration
					var totalMin = getTotalDuration(legs);

					ctx.strokeStyle = bgColor;
					ctx.fillStyle = gray40;

					var nodeRadius = 5;
					var startNodeX = nodeRadius;

					var durationRatios = getDurationRatios(legs, totalMin);

					for (var i = 0; i < legs.length; i++) {
						var durationRatio = durationRatios[i];
						var drawableWidth = (canvas.width - (nodeRadius * 2));
						var legWidth = drawableWidth * durationRatio;

						// legWidth will be split evenly between legs regardless of duration
						// var legWidth = drawableWidth / legs.length;

						// the width of a leg based on duration
						// var durationWidth = drawableWidth * durationRatio;

						// draw starting node circle
						var startNodeY = canvas.height - nodeRadius;

						// draw leg arc
						var arcX = startNodeX + (Math.round(legWidth / 2));
						var multiplier = 1 + ((legWidth / drawableWidth) * 0.8); // to scale the arc so it fits on the canvas at any flight duration
						// var multiplier = 0.8 + ((drawableWidth / durationWidth) * 0.1); // to scale the arc so it fits on the canvas at any flight duration
						var arcY = legWidth * multiplier;
						var endNodeX = startNodeX + legWidth;
						var endNodeY = startNodeY;

						drawLegArc(arcX, arcY, startNodeX, startNodeY, endNodeX, endNodeY, bgColor);
						drawNode(startNodeX, startNodeY, nodeRadius, bgColor);

						// insert and position html for flight leg metadata
						$scope.insertFlightData(arcX, legs[i]);

						// if there is a layover
						if ((i > 0) && (i < legs.length)) {
							// insert and position html for layover metadata
							$scope.insertLayoverData(startNodeX, legs[i - 1], legs[i]);
						}

						// move startNode to next position
						startNodeX += Math.round(legWidth);

						drawNode(endNodeX, endNodeY, nodeRadius, bgColor);
					}
				};

				var drawNode = function (x, y, radius, bgColor) {
					var startAngle = 0;
					var endAngle = Math.PI * 2;
					var antiClockwise = false;

					ctx.beginPath();
					ctx.arc(x, y, radius, startAngle, endAngle, antiClockwise);
					ctx.fillStyle = gray40;
					ctx.fill();

					ctx.lineWidth = 3;
					ctx.strokeStyle = bgColor;

					ctx.stroke();
					ctx.closePath();
				};

				var drawLegArc = function (arcX, arcY, startNodeX, startNodeY, endNodeX, endNodeY) {
					var xDiff = (startNodeX - arcX);
					var yDiff = (startNodeY - arcY);
					var arcRadius = Math.sqrt((xDiff * xDiff) + (yDiff * yDiff)); // get radius using pythagorean theorem (a2 + b2 = c2)
					var arcStartAngle = Math.atan2(startNodeY - arcY, startNodeX - arcX);
					var arcEndAngle = Math.atan2(endNodeY - arcY, endNodeX - arcX);
					var arcAntiClockwise = false;

					ctx.beginPath();
					ctx.arc(arcX, arcY, arcRadius, arcStartAngle, arcEndAngle, arcAntiClockwise);

					ctx.lineWidth = 1.2;
					ctx.strokeStyle = gray40;
					ctx.stroke();
					ctx.closePath();

					// draw airplane
					drawPlane(arcX, arcY - arcRadius);
				};

				drawFlightLegs(legs, bgColor);

				return $scope;
			};

			$scope.flightHopNoCanvas = function (el, legs) {
				var $el = angular.element(el);
				$el.empty();

				var drawNode = function (x) {
					$el.append('<div class="flight-hop-node" style="left: ' + x + 'px"></div>');
				};

				// draws airplane png with x,y as its center point
				var drawPlane = function (x, y) {
					var img = new Image();
					img.src = '/Content/assets/modules/ha-flight-hop/images/airplane_small_opaque.png';
					img.onload = function () {
						var offsetX = x - (img.width / 2);
						var $img = angular.element(img);
						$img.css({position: 'absolute', left: offsetX, top: y - 7});
						$el.append($img);
					};
				};

				var drawLeg = function (startNodeX, startNodeY, endNodeX) {
					var $legEl = angular.element('<div class="flight-hop-leg"></div>');
					var width = endNodeX - startNodeX;
					$legEl.css({position: 'absolute', left: startNodeX, width: width});
					$el.append($legEl);
					// draw airplane
					drawPlane(startNodeX + (width / 2), startNodeY);
				};

				var drawFlightLegs = function (legs) {
					var nodeRadius = 5;
					var startNodeX = nodeRadius;
					var i;
					for (i = 0; i < legs.length; i++) {
						var drawableWidth = (el.width() - (nodeRadius * 2));

						// legWidth will be split evenly between legs regardless of duration
						var legWidth = drawableWidth / legs.length;

						// draw starting node circle
						var startNodeY = el.height() - nodeRadius;
						drawNode(startNodeX, startNodeY, nodeRadius);

						var endNodeX = startNodeX + legWidth;
						var endNodeY = startNodeY;
						drawLeg(startNodeX, startNodeY, endNodeX, endNodeY);

						// insert and position html for flight leg metadata
						var middleX = startNodeX + nodeRadius + (legWidth / 2);
						$scope.insertFlightData(middleX, legs[i]);

						// if there is a layover
						if ((i > 0) && (i < legs.length)) {
							// insert and position html for layover metadata
							$scope.insertLayoverData(startNodeX + nodeRadius, legs[i - 1], legs[i]);
						}

						// move startNode to next position
						startNodeX += Math.round(legWidth);

						// if its the last leg
						if (i === (legs.length - 1)) {
							// draw closing node circle
							drawNode(endNodeX, endNodeY, nodeRadius);
						}
					}
				};

				drawFlightLegs(legs);

				return $scope;
			};
		}];

		var HaFlightHopLink = function ($scope, element, $attrs) {
			var metadataEl = element.find('.trip-illustration .metadata');
			/*Updated*/
			$scope.$watch('$viewContentLoaded', function () {
				$scope.resizeCanvas();
			});

			$scope.$on('$haFlightHopReady', function () {
				$scope.resizeCanvas();
			});

			angular.element(window).on('resize', function () {
				metadataEl.empty();
				$scope.resizeCanvas();
			});

			$scope.resizeCanvas = function () {
				if (window.Modernizr.canvas) {
					var canvas = element.find('canvas');
					var parent = canvas.parent();
					canvas.attr({width: parent.width()});
					$scope.flightHop(canvas[0], $scope.segment.Hops, $attrs.bgColor || '#717072');
				} else {
					var el = element.find('.no-canvas-fallback');
					$scope.flightHopNoCanvas(el, $scope.segment.Hops);
				}
			};

			$scope.insertFlightData = function (left, leg) {
				/*jshint multistr: true */
				var html =
					'<div class="flight-info" style="left: ' + left + 'px">' +
						'<div class="flight-number">' + leg.Carrier + ' ' + leg.FlightNumber + '</div>' +
						'<div class="flight-duration">' + leg.Duration + '</div>' +
					'</div>';

				metadataEl.append(html);
			};

			$scope.insertLayoverData = function (left, arrivingLeg /* , departingLeg */) {
				/*jshint multistr: true */
				var html = '';
				if (arrivingLeg.ISOverNightStay) {
					html =
						'<div class="layover-info" style="left: ' + left + 'px">' +
							'<div class="layover-city-code">' + arrivingLeg.ArrivalCityCode + '</div>' +
							'<div class="layover-duration">' + arrivingLeg.StopOverTime + '</div>' +
							'<div class="layover-overnight">(' + $attrs.overnightText + ')</div>' +
						'</div>';
				} else {
					html =
						'<div class="layover-info" style="left: ' + left + 'px">' +
							'<div class="layover-city-code">' + arrivingLeg.ArrivalCityCode + '</div>' +
							'<div class="layover-duration">' + arrivingLeg.StopOverTime + '</div>' +
						'</div>';
				}

				metadataEl.append(html);
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaFlightHopLink,
			controller: HaFlightHopController
		};
	});

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haGlobalFooterModule', []).directive('haGlobalFooter', ['$rootScope', function ($rootScope) {
		return {
			restrict: 'A',
			scope: true,
			link: function () {
				$('.back-to-top').click(function () {
					//Desktop back to top link and focus skip to content for screens reader
					if(!$rootScope.isMobile){
						window.scrollTo({ top: 0, behavior: 'smooth' });
						$('#skipToContent').focus();
					}

					//Mobile back to top link
					if($rootScope.isMobile){
						window.scrollTo(0, 0);
					}
				});
			},
			controller: ['$scope', function ($scope) {

				// notion of "footer errors" is currently unused
				function getUrlVars() {

					var vars = [];
					var hash;
					var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
					for (var i = 0; i < hashes.length; i += 1) {
						hash = hashes[i].split('=');
						vars.push(hash[0]);
						vars[hash[0]] = hash[1];
					}
					return vars;
				}

				var vars = getUrlVars();

				if (vars.subscriptionflag && vars.subscriptionflag === '1') {
					$scope.ShowFooterHeaderAlert = true;
					$scope.FooterErrorType = 'error';
					$scope.FooterHeaderMessage = 'Error Occured';
				}

				$scope.Date = new Date();
			}]
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Book Flight Results
	// --------------------------------------------
	//
	// * **Class:** HaBookFlightResults
	// * **Author:** Cory Shaw
	//
	// Demos, data,  and templates for looping over flight results

	'use strict';

	function FareDetail(id, dataKey, classname, MilesPricing) {
		var self = this;
		self.id = id;
		self.FlightRefKey = dataKey;
		self.SelectedClass = classname;
		self.IsMileagePricing = MilesPricing;
	}

	function SelectedFlights() {
		var self = this;
		self.FareDetails = [];
	}

	var module = angular.module('haBookFlightResultsModule', ['haFlightResultsAPI', 'duScroll', 'haUtilsModule', 'haModalService', 'haGlobalsModule', 'haFeatureFlagsModule']);

	module.filter('signedCurrency', function () {
		return function (input) {
			var out = '';
			var rounded = Math.round(input * 100) / 100;
			if (input > -1) {
				out = 'blah$' + rounded;
			} else {
				out = '-$' + Math.abs(rounded);
			}
			return out;
		};
	});

	module.directive('haBookFlightResults', [
		'$document', '$timeout', 'haGlobals', 'haConfig', 'haFlightResultsAPI', '$rootScope', '$window',
		function ($document, $timeout, haGlobals, haConfig, haFlightResultsAPI, $rootScope, $window) {

			// globals
			var serverShortDate = $window.serverShortDate;
			var searchRequest = $window.searchRequest;

			var HaBookFlightResultsController = [
				'$scope', 'haInterstitialAPI', 'haModal', 'haPassengersService',
				function ($scope, haInterstitialAPI, haModal, $pax) {

					$scope.selectedSegments = [];
					$scope.currentSegmentId = 1;
					$scope.$emit('$haBookFlightResultsReady');
					$scope.isSelected = false;
					$scope.isEditingSelected = false;
					$scope.resultsLoaded = false;
					$scope.seatClass = 'coach';
					$scope.loadingSegment = false;
					$scope.multiCity = false;
					$scope.seatClassTypes = [];
					$scope.maxDateAllowed = (typeof serverShortDate !== 'undefined') ? new Date(serverShortDate) : new Date();
					$scope.maxDateAllowed.setHours(0, 0, 0);
					$scope.maxDateAllowed.setDate($scope.maxDateAllowed.getDate() + 330);
					$scope.isNextAllowed = true;
					$scope.isPreviousAllowed = true;
					$scope.taxDetails = false;
					$scope.IsServiceErrors = false;
					$scope.ServiceErrorMessage = '';
					$scope.errordescription = '';
					$scope.isExtraComfort = false;
					$scope.IsFirstClass = false;
					$scope.IsBusinessClass = false;
					$scope.IsChangeProgress = false;
					$scope.IsNewFlightSelected = [];
					$scope.ShowMilesToggle = false;
					$scope.SignifiedMarket = 1;
					$scope.IsChangeFlightBooking = searchRequest.IsChangeFlightBooking;
					$scope.isFlightResult = true;
					$scope.showBookingWidget = false;
					$rootScope.checkHoldBookingEligibility = false;

					// Lie flat
					$scope.lieFlatAvailable = false;
					$scope.lieFlatBannerVisible = true;
					$scope.closeBanner = function (event) {
						event.stopPropagation();
						$scope.lieFlatBannerVisible = false;
					};

					$scope.$watch('currency', function (/* val */) {
						//$scope.isMileagePricing = (val === 'MILES') ? true : false;
					});

					$rootScope.selectedHoldFare = {};

					$scope.$pax = $pax;
					$pax.passengers = [];

					//For tripsummary details footer.
					$scope.TripSummary = [];

					$scope.FilterLegs = function (leg) {
						if (leg.OperatingCarrier !== 'HA' && leg.OperatedBy != null && leg.OperatedBy !== '') {
							return leg;
						}
					};

					$scope.AddPassengerList = function (count, type) {
						for (var i = 0; i < count; i++) {
							$scope.$pax.add({
								type: type,
								isUser: true
							});
						}
					};

					$scope.UpdatePassengerList = function () {
						haGlobals('searchRequest', function (searchRequest) {
							if (searchRequest.AdultCount > 0) {
								$scope.AddPassengerList(searchRequest.AdultCount, 'Adult');
							}
							if (searchRequest.ChildCount > 0) {
								$scope.AddPassengerList(searchRequest.ChildCount, 'Child');
							}
							if (searchRequest.InfantCount > 0) {
								$scope.AddPassengerList(searchRequest.InfantCount, 'Infant');
							}
						});
					};

					if (!$scope.IsChangeFlightBooking) {
						$scope.UpdatePassengerList();
					}


					$scope.FormatTrip = function (trip) {

						if ($scope.TripType === 0 || trip.IsMileagePricing) {
							if (trip.ActiveTab && trip.ActiveTab.Flights && trip.ActiveTab.Flights.length > 0) {
								angular.forEach(trip.ActiveTab.Flights, function (flight) {
									if (flight.AvailBookingFares && flight.AvailBookingFares.length > 0) {
										angular.forEach(flight.AvailBookingFares, function (fares, i) {
											if (fares.Name.toLowerCase() === 'extracomfort') {
												flight.AvailBookingFares.splice(i, 1);
											}
										});
									}
								});

							}
						}
					};

					$scope.showResultsHelp = function () {
						haFlightResultsAPI.fetchPopup('GetHelpPopup').success(function (content) {
							if ($scope.handleExceptions(content, 'haFlightResultsAPI-showResultsHelp')) {
								return;
							}
							haModal({
								id: 'results-help',
								backdrop: 'true',
								template: content
							});
						});
					};

					$scope.FormatResults = function (results) {


						if (results && results.Trips && results.Trips.length > 0) {
							angular.forEach(results.Trips, function (trip) {
								$scope.FormatTrip(trip);
							});

						} else if (results && results.ActiveTab &&
							results.ActiveTab.Flights &&
							results.ActiveTab.Flights.length > 0) {
							$scope.FormatTrip(results);

							//show/hide Miles dropdown
							var hasCodeShareFlight = false;
							$scope.ShowMilesToggle = false;

							angular.forEach(results.ActiveTab.Flights, function (flight) {
								hasCodeShareFlight = false;

								angular.forEach(flight.Legs, function (leg) {
									if (leg.IsCodeShare) {
										hasCodeShareFlight = true;
									}
								});
								if (!hasCodeShareFlight && $scope.ShowMilesToggle === false) {
									$scope.ShowMilesToggle = true;
								}
							});
						}

						//- Set properties for Seat Class Popup
						if (angular.isDefined(results) &&
							angular.isDefined(results.Trips) &&
							angular.isDefined(results.Trips.ActiveTab) &&
							angular.isDefined(results.Trips.ActiveTab.Flights) &&
							angular.isDefined(results.Trips.ActiveTab.Flights.AvailBookingFares)) {
							angular.forEach(results.Trips[0].ActiveTab.Flights[0].AvailBookingFares, function (AvailBookingFare) {
								$scope.updateClass(AvailBookingFare.Name.toLowerCase());
							});
						}
					};

					haFlightResultsAPI.fetch().success(function (results) {
						if ($scope.handleExceptions(results, 'haFlightResultsAPI-fetch')) {
							return;
						}
						$scope.resultsLoaded = true;
						$scope.lieFlatAvailable = $scope.hasLieFlat(results);

						$scope.SignifiedMarket = results.MarketID;
						if (results.Trips !== undefined && results.Trips.length > 0) {
							$scope.TripType = results.TripType;
							$scope.FormatResults(results);
							$.extend($scope, results);

							if (typeof ($scope.IsChangeFlightBooking) !== 'undefined' && $scope.IsChangeFlightBooking) {

								if ($scope.Trips.length === 1 && +$scope.Trips[0].TripId === 2) {
									$scope.Trips[0].TripId = 1;
								}
								if ($scope.Trips[0].AdultCount > 0) {
									$scope.AddPassengerList($scope.Trips[0].AdultCount, 'Adult');
								}
								if ($scope.Trips[0].ChildCount > 0) {
									$scope.AddPassengerList($scope.Trips[0].ChildCount, 'Child');
								}
								if ($scope.Trips[0].InfantCount > 0) {
									$scope.AddPassengerList($scope.Trips[0].InfantCount, 'Infant');
								}
							}
							$scope.currency = results.Currency;
							//Update Currency for TripSummary footer.
							$scope.TripSummary.currency = results.Currency;
							//$scope.IsExtraComfort = results.IsExtraComfort;
							$scope.taxes = [];
							if (results.Trips != null) {
								$scope.TripCount = results.Trips.length;
								if ($scope.TripType === 0 && $scope.PricingType === 'RoundTripFare') {
									$scope.multiCity = true;
								} else {
									$scope.multiCity = false;
								}
							}
							$scope.arrowUpdate();

							angular.forEach(results.Trips, function (trip) {
								var hasCodeShareFlight = false;
								trip.ShowPreSelected = true;
								//show/hide Miles dropdown
								angular.forEach(trip.ActiveTab.Flights, function (flight) {
									hasCodeShareFlight = false;

									angular.forEach(flight.Legs, function (leg) {
										if (leg.IsCodeShare) {
											hasCodeShareFlight = true;
										}
									});
									if (!hasCodeShareFlight && $scope.ShowMilesToggle === false) {
										$scope.ShowMilesToggle = true;
									}
								});
							});
						}
					}).error(function () {
						$scope.handleExceptions('jsError', 'haFlightResultsAPI-fetch');
						$scope.resultsLoaded = true;
					});


					var getDuration = function (key) {
						return function (obj) {

							var duration = 0;

							var hours = obj[key].match(/\d*h/g);
							var mins = obj[key].match(/\d*m/g);

							if (hours) {
								duration = parseInt(hours[0].match(/\d+/)) * 60;
							}

							if (mins) {
								duration += parseInt(mins[0].match(/\d+/));
							}

							return duration;
						};
					};

					// sorting
					$scope.sorting = { predicateKey: 'default' };

					var priceAmount = function () {
						return function (obj) {
							var displayFare = 0;
							for (var i = 0; i < obj.AvailBookingFares.length; i++) {
								if (obj.AvailBookingFares[i].FareTypes && obj.AvailBookingFares[i].FareTypes.length > 0) {
									displayFare = parseFloat(obj.AvailBookingFares[i].FareTypes[0].TotalDisplayFare);
									break;
								}
							}
							return displayFare;
						};
					};

					var getTime = function (key) {
						return function (obj) {
							return new Date(obj[key]);
						};
					};

					$scope.predicates = {
						'default': undefined,
						//lowestPrice : 'AvailBookingFares[0].FareTypes[0].TotalDisplayFare',
						lowestPrice: priceAmount(),
						earliestDepartureTime: getTime('OriginDateTime'),
						earliestArrivalTime: getTime('DestinationDateTime'),
						shortestDuration: getDuration('Duration')
					};
					$scope.reverse = false;

					$scope.ToggleTaxDetails = function () {
						$scope.taxDetails = !$scope.taxDetails;
					};

					$scope.totalTaxes = function () {
						var total = 0;
						angular.forEach($scope.selectedSegments, function (segment) {
							if (segment[segment.selectedSeatClass]) {
								total += Number(segment[segment.selectedSeatClass].TotalTaxes) * $scope.paxCount;
							}
						});

						return total.toFixed(2);
					};

					$scope.onTimePerformanceRecords = new Map();

					$scope.setArrivalPercentages = function (segment, visible) {
					    if (visible) {
					        var getFlightNumbers = [];  // The flight numbers to retrieve

					        // get the flight numbers
					        // retrieve & set Values from local Map if present
					        // retrieve & set both Map and Values from Service if not present
					        segment.Legs.forEach(function (leg) {   // Iterate the legs
					            // The front end is displaying LateArrival in the spot where the delayed percentage is displayed so use LateArrival for Delayed
					            // Added check on cancelled as a change was made to ensure these 3 were null when not filled by InternalAPI
                                // So if all 3 are null the the LD flag is on and we need to retrieve otp data otherwise show what we have
					            if (leg.OnTimeArrival == null && leg.LateArrival == null && leg.Cancelled == null)
					            {
					                // see if the details exist in the previously retrieved records
					                var details = $scope.onTimePerformanceRecords.get(leg.FlightNumber);

					                if (details == null) {   // Details are not retrieved yet add flight number to retrieval list
					                    getFlightNumbers.push(leg.FlightNumber);
					                }
					                else {
					                    leg.LateArrival = details.DelayedPercentage;
					                    leg.OnTimeArrival = details.OnTimePercentage;
					                }
					            }
					        });

					        // Get the OTP performance records
					        if (getFlightNumbers.length >= 1) {
					            // Get the on time performance records - here they are details but in the application logic they are records
					            var results = haFlightResultsAPI.fetchOnTimePerformanceDetails(getFlightNumbers.join(',')).success(function (results) {

					                if (results.ErrorMessage != null && results.ErrorMessage.length > 1) {
					                    segment.Legs.forEach(function (leg) {  // Set segment on time details
					                        if (getFlightNumbers.indexOf(leg.FlightNumber) != -1) {   // Only try to set detail for segments that were searched for
					                            leg.LateArrival = 'Error Late ';
					                            leg.OnTimeArrival = 'Error On Time ';
					                        }
					                    });

					                    return;
					                }

					                results.forEach(function (otp) {    // Save OTP results - set to . to indicate call was made if null or empty
					                    if (otp.OnTimeDetails.DelayedPercentage == null || otp.OnTimeDetails.DelayedPercentage == '')
					                        otp.OnTimeDetails.DelayedPercentage = '.';

					                    if (otp.OnTimeDetails.OnTimePercentage == null || otp.OnTimeDetails.OnTimePercentage == '')
					                        otp.OnTimeDetails.OnTimePercentage = '.';

					                    $scope.onTimePerformanceRecords.set(otp.FlightNumber, otp.OnTimeDetails);
					                });

					                segment.Legs.forEach(function (leg) {  // Set segment on time details
					                    if( getFlightNumbers.indexOf( leg.FlightNumber ) != -1 )
					                    {   // If leg wasn't in request list don't try to reset it
					                        var details = $scope.onTimePerformanceRecords.get(leg.FlightNumber);
					                        if (details != null) {   // Not every flight has OTP info so double check
					                            leg.OnTimeArrival = details.OnTimePercentage;
					                            leg.LateArrival = details.DelayedPercentage;
					                        }
					                        else {   // if not found then default to . which indicates call was made and none were found
					                            leg.OnTimeArrival = '.';
					                            leg.LateArrival = '.';
					                        }
					                    }
					                });
					            }).error(function () {
					                result.Legs.forEach(function (leg) {  // Set segment on time details
					                    if (getFlightNumbers.indexOf(leg.FlightNumber) != -1) {   // Only try to set detail for segments that were searched for
					                        leg.LateArrival = 'Error Late ';
					                        leg.OnTimeArrival = 'Error On Time ';
					                    }
					                });
					            });
					        }
					    }
					};

					$scope.grandTotal = function () {
						var total = 0;
						angular.forEach($scope.selectedSegments, function (segment) {
							if (segment[segment.selectedSeatClass]) {
								total += (Number(segment[segment.selectedSeatClass].TotalBaseFare) + Number(segment[segment.selectedSeatClass].TotalTaxes)) * $scope.paxCount;
							}
						});
						return total.toFixed(2);
					};

					$scope.$on('toggleMilesEvent', function (event, data) {
						$scope.loadingSegment = true;
						angular.element('#segment-' + data[1]).addClass('loading');

						var segmentID = data[1] * 1 - 1;
						var isMiles = false;

						if (data[0].value === 'MILES') {
							isMiles = true;
						}


						haFlightResultsAPI.toggleCurrencyMilesService(segmentID, isMiles).success(function (results) {


							if ($scope.handleExceptions(results, 'haFlightResultsAPI-toggleCurrencyMilesService')) {
								return;
							}

							$scope.loadingSegment = false;
							$scope.FormatResults(results);
							if (results.IsAllSegmentChange) {
								$scope.HideAllSegments();
								$scope.resultsLoaded = true;
								$.extend($scope, results);
								$scope.currency = results.Currency;
								$scope.backToFlightResults();
							} else {
								angular.element('#segment-' + data[1]).removeClass('loading');
								$('#segment-' + data[1]).addClass('current');
								$.extend($scope.Trips[segmentID], results.Trips[0]);
								$scope.PricingType = results.PricingType;
							}

						}).error(function () {
							$scope.handleExceptions('jsError', 'haFlightResultsAPI-toggleCurrencyMilesService');
							$scope.loadingSegment = false;
							angular.element('#segment-' + data[1]).removeClass('loading');
							$('#segment-' + data[1]).addClass('current');
						});
					});

					$scope.buildSearchQuery = function () {
						var scope = angular.element('#bookingWidget').scope();
						return scope.getFlightQueryModel();
					};

					// DateValidationPopup
					// This occurs if the 7 day tab slider is advanced past the return date or vice versa
					$scope.showDateValidationModal = function () {

						haModal(haConfig.getTemplateUrl('book/flightresults/ha-flightresults-date-validation-modal.html'), {
							backdrop: 'true',
							id: 'FutureDateValidationPopupModal',
							scope: $scope
						});
					};

					// 331DaysValidationPopup
					// This occurs if the 7 day tab slider is advanced past the 331 day range
					$scope.show331DaysPopup = function () {

						haModal(haConfig.getTemplateUrl('book/flightresults/ha-flightresults-331-days-validation-modal.html'), {
							backdrop: 'true',
							id: 'results-331Days',
							scope: $scope
						});
					};

					// LessThanCurrentDateValidationPopup
					// This occurs if the 7 day tab slider is advanced prior to the current date
					$scope.showLessThanCurrentDateModal = function () {

						haModal(haConfig.getTemplateUrl('book/flightresults/ha-flightresults-less-than-current-date-modal.html'), {
							backdrop: 'true',
							id: 'results-DateValidationLessDate',
							scope: $scope
						});
					};

					$scope.selectTab = function (tab, tripId) {
						var content;
						if (tab.SeatsAvailable > 0) {
							//logic to check for prevDate//
							if (new Date(tab.TabDate) < $scope.selectedTripDate[tripId - 1]) {
								$scope.showDateValidationModal();
								return false;
							} else if (tripId !== $scope.Trips.length && new Date(tab.TabDate) > $scope.selectedTripDate[tripId + 1]) {
								$('#ScData_PrevDateValidation_scenario').html($('.futureValidation').html());
								$scope.showDateValidationModal();
								return false;
							}
							// 331 days validation
							if (new Date(tab.TabDate) >= $scope.maxDateAllowed) {
								$scope.show331DaysPopup();
								return;
							}
							var segmentIdx = tripId - 1;
							if ($scope.Trips[segmentIdx + 1] != null) {
								if (tripId !== $scope.Trips.length && new Date(tab.TabDate) > new Date($scope.Trips[segmentIdx + 1].DepartDate)) {
									$('#ScData_PrevDateValidation_scenario').html($('.futureValidation').html());
									$scope.showDateValidationModal();
									return false;
								}
							}
							$scope.loadingSegment = true;
							angular.element('#segment-' + tripId).addClass('loading');

							// window.rootData.booking.segments[segmentIdx].departureDate = tab.TabDate;
							// in the end this should just use fetch() but since we are having to fake our requests this makes it easier to grab another dataset
							haFlightResultsAPI.fetchTab(tripId, tab.TabDate).success(function (results) {

								if ($scope.handleExceptions(results, 'haFlightResultsAPI-selectTab')) {
									return;
								}

								$scope.FormatResults(results);

								$scope.lieFlatAvailable = $scope.hasLieFlat(results);

								if (results.IsAllSegmentChange === true) {
									$scope.resultsLoaded = true;
									$.extend($scope, results);
									$scope.currency = results.Currency;
									$scope.backToFlightResults();
								} else {
									angular.element('#segment-' + tripId).removeClass('loading');
									angular.element('#segment-' + tripId).addClass('current');
									$.extend($scope.Trips[segmentIdx], results);
								}

								$scope.arrowUpdate();

								if ($scope.IsChangeFlightBooking) {
									if ($scope.Trips.length === 1 && +$scope.Trips[0].TripId === 2) {
										$scope.Trips[0].TripId = 1;
									}
								}

								if (!$scope.IsChangeFlightBooking) {
									var bookingWidgetScope = angular.element('[booking-widget]').scope();
									if (bookingWidgetScope !== undefined) {
										bookingWidgetScope.updateLegDates(2);
										bookingWidgetScope.Initialize();
									}
									var flightResultsStickProgressBarScope = angular.element('#flightResultsStickProgressBar').scope();
									flightResultsStickProgressBarScope.UpdateBar(tripId);
								}
								else {
									var flightResultsStickProgressBarScope = angular.element('#flightResultsStickProgressBarReshop').scope();
									flightResultsStickProgressBarScope.UpdateBar(tripId);
								}
							}).error(function () {
								$scope.handleExceptions('jsError', 'haFlightResultsAPI-selectTab');
							});

						}
					};


					$scope.SearchBySeven = function (tripId, direction, tabDate) {
						var content;
						var segmentIdx = tripId - 1;
						var seventhTabDate = new Date(tabDate + 'T10:00:00Z'); //- Fix for chrome known issue.
						var currentTabDate = new Date(tabDate + 'T10:00:00Z');
						var dt = new Date();
						dt.setHours(0, 0, 0, 0);
						seventhTabDate.setHours(0, 0, 0, 0);
						currentTabDate.setHours(0, 0, 0, 0);

						if (direction === 1) { //forward click
							/*
							 If we are going forwards, we want to check these items:
							 1) Is the depart date past the 331 day deadline
							 2) Is the Departure date prior to the next leg's departure date (if a subsequent leg exists)
							 */
							seventhTabDate.setDate(seventhTabDate.getDate() + 7);

							//first, check and see if the date exceeds the date of the next segment, if it exists
							if ($scope.Trips[segmentIdx + 1] && $scope.Trips[segmentIdx + 1].DepartDate &&
								tripId !== $scope.Trips.length && new Date(seventhTabDate) > Date.parse($scope.Trips[segmentIdx + 1].DepartDate)) {
								$('#ScData_PrevDateValidation_scenario').html($('.futureValidation').html());
								$scope.showDateValidationModal();
								return false;
							}
							//next, check to see if the tab date would be past 330 days
							else if (seventhTabDate > $scope.maxDateAllowed) {
								$scope.show331DaysPopup();
								return;
							}

						} else if (direction === -1) {
							/*
							 If we are going backwards, we want to check these items:
							 1) Is the depart date prior to today (no need to check the return date as it can't slide prior to departure - see 2 below)
							 2) Is the return date prior to the departure date
							 3) Is the Departure date prior to the previous leg's departure date (if a previous leg exists)
							 */
							seventhTabDate.setDate(seventhTabDate.getDate() - 7);

							//check to see if departure date is prior to today
							if (Date.parse(seventhTabDate) < Date.parse(dt)) {
								$scope.showLessThanCurrentDateModal();
								return;
							}
							//check to see if tab date would be prior to previus segment date (if prev segment exists)
							else if ($scope.Trips[segmentIdx - 1] && $scope.Trips[segmentIdx - 1].DepartDate &&
								(new Date(seventhTabDate) < Date.parse($scope.Trips[segmentIdx - 1].DepartDate))) {
								$scope.showDateValidationModal();
								return false;
							}
						}

						//dates all ok, proceed
						$scope.loadingSegment = true;
						angular.element('#segment-' + tripId).addClass('loading');


						haFlightResultsAPI.fetchTab(tripId, direction).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsAPI-SearchBySeven')) {
								return;
							}
							$scope.FormatResults(results);

							$scope.loadingSegment = false;
							$scope.lieFlatAvailable = $scope.hasLieFlat(results);

							if (results.IsAllSegmentChange) {
								$scope.resultsLoaded = true;
								$.extend($scope, results);
								$scope.currency = results.Currency;
								$scope.backToFlightResults();
							} else {
								angular.element('#segment-' + tripId).removeClass('loading');
								$('#segment-' + tripId).addClass('current');
								$.extend($scope.Trips[segmentIdx], results);
							}


							if (!$scope.IsChangeFlightBooking) {
								var flightResultsStickProgressBarScope = angular.element('#flightResultsStickProgressBar').scope();
								flightResultsStickProgressBarScope.UpdateBar(tripId);
							}
							else {
								var flightResultsStickProgressBarScope = angular.element('#flightResultsStickProgressBarReshop').scope();
								flightResultsStickProgressBarScope.UpdateBar(tripId);
							}
						}).error(function () {
							$scope.handleExceptions('jsError', 'haFlightResultsAPI-SearchBySeven');
							$scope.loadingSegment = false;
							$('#segment-' + tripId).addClass('current');
						});
					};


					$scope.arrowUpdate = function () {

						$scope.isPreviousAllowed = true;
						$scope.isNextAllowed = true;
						/*	var segmentIdx = $scope.currentSegmentId-1;
						 var today = (typeof serverShortDate !== 'undefined') ? new Date(serverShortDate) : new Date();
						 var tab1Date = $scope.Trips[$scope.currentSegmentId - 1].AlternateTabs[0].TabDate;
						 tab1Date = new Date(tab1Date + 'T10:00:00Z');  //- Fix for chrome known issue.
						 var tab7Date = $scope.Trips[$scope.currentSegmentId - 1].AlternateTabs[6].TabDate;
						 tab7Date = new Date(tab7Date + 'T10:00:00Z');  //- Fix for chrome known issue.

						 // hide prev arrow if tab1 is less than or equal to today
						 if (tab1Date <= today) {
						 $scope.isPreviousAllowed = false;
						 }

						 // hide prev arrow if this is 2nd segment or later and tab1 date is less than or equal to previous trip date
						 if ($scope.currentSegmentId > 1) {
						 if ($scope.selectedTripDate[$scope.currentSegmentId - 1] && Date.parse(tab1Date) <= Date.parse($scope.selectedTripDate[$scope.currentSegmentId - 1])) {
						 $scope.isPreviousAllowed = false;
						 }
						 }

						 // hide next arrow if tab7 date is greater than or equal to next trip date
						 if($scope.Trips[segmentIdx + 1] && tab7Date >= Date.parse($scope.Trips[segmentIdx + 1].DepartDate)){
						 $scope.isNextAllowed = false;
						 }

						 // hide next arrow if tab7 is greater than or equal to max date allowed
						 if (tab7Date >= $scope.maxDateAllowed) {
						 $scope.isNextAllowed = false;
						 }
						 */


					};

					$scope.changeCurrency = function (currency, tripId) {
						var segmentIdx = tripId - 1;
						haFlightResultsAPI.fetchCurrency(currency).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsAPI-changeCurrency')) {
								return;
							}
							$scope.Trips[segmentIdx] = results.Trips[segmentIdx];
						});
					};

					// Lie flat indicator functionality

					// Method for detecting lieflats in any segments
					// result.Trips[0].ActiveTab.Flights[i].Legs[j].LieFlatAvailable
					$scope.hasLieFlat = function (result) {
						var hasLieFlat = false;
						var activeTab = result.ActiveTab;
						// Handle various result data formats, specifically the location of `ActiveTab`
						if (!activeTab) {
							if (result.Trips && result.Trips[0] && result.Trips[0].ActiveTab) {
								// `ActiveTab` lives in the Trips[0] object
								activeTab = result.Trips[0].ActiveTab
							} else {
								// There is no tab whatsoever.
								return false;
							}
						}
						var details = activeTab.Flights;
						$scope.lieFlatBannerVisible = true;
						for (var i = 0; i < details.length; i++) {
							var legs = details[i].Legs;
							for (var j = 0; j < legs.length; j++) {
								if (legs[j].LieFlatAvailable) {
									hasLieFlat = true;
								} else {
									$scope.lieFlatBannerVisible = false;
								}
							}
						}
						return hasLieFlat;
					};

					// Does the current segment contain lie flat seats?
					$scope.segmentHasLieFlat = function(seg) {
						 return seg.Legs.some(function (elem) {return elem.LieFlatAvailable; });
					}


					$scope.showSeatMap = function (segments) {

						haModal(haConfig.getTemplateUrl('VerticalSeatmap/ha-vertical-seatmap-preview.html'), {
							backdrop: 'true',
							id: 'ha-vertical-seatmap-preview-modal',
							extendScope: {
								previewSegments: segments,
								SignifiedMarket: $scope.SignifiedMarket,
								enableTCR: $scope.enableTCR,
								disableSeatUpgrades: !!$scope.Trips[0].IsDisableSeatUpgrade
							}
						});
					};


					$scope.showSeatClassModal = function (seatclass, tripId) {
						tripId = (tripId === undefined) ? $scope.currentSegmentId : tripId;
						$scope.seatClassModal = {};
						$scope.seatClassModal.currentClass = seatclass;
						$scope.seatClassModal.Data = $scope.Trips[tripId - 1].SeatClassModalData;

						$scope.seatClassModal.selected = $scope.seatClassModal.Data.filter(function(modal) {
							return (modal.Type === seatclass);
						})[0];

						// no data found
						if (!$scope.seatClassModal.selected) {
							return false;
						}

						haModal(haConfig.getTemplateUrl('Book/FlightResults/ha-flightresults-seat-class-modal-template.html'), {
							id: 'results-seat-class',
							backdrop: 'true',
							scope: $scope,
							defaultContent: seatclass
						});

						return true;
					};

					$scope.showSeatClassModalLieFlat = function() {
						// try business first
						var success = $scope.showSeatClassModal('business');
						if (!success) {
							// if unsuccessful, try first class
							$scope.showSeatClassModal('first');
						}
					}


					$scope.showBaggageFeesModal = function (baggagecontent) {
						haFlightResultsAPI.fetchModalContent(baggagecontent)
						.success(function (data) {
							if ($scope.handleExceptions(data, 'haFlightResultsAPI-showBaggageFeesModal')) {
								return;
							}
							haModal({
								id: 'results-baggageFee',
								backdrop: 'true',
								template: data
							});
						});
					};

					$scope.showModalPopUp = function (ModalName) {
						haFlightResultsAPI.fetchModalContent(ModalName)
						.success(function (data) {
							if ($scope.handleExceptions(data, 'haFlightResultsAPI-showModalPopUp')) {
								return;
							}
							haModal({
								id: 'results-' + ModalName,
								backdrop: 'true',
								template: data
							});
						});
					};

					$scope.showFightsNoSeatsAvailableModal = function () {
						window.location.href = '/book/Error?ErrorType=SeatNotAvailable';
					};

					$scope.showFightsScheduleMismatchModal = function () {
						haFlightResultsAPI.fetchPopup('ScheduleMismatch').success(function (content) {
							if ($scope.handleExceptions(content, 'haFlightResultsAPI-showFightsScheduleMismatchModal')) {
								return;
							}
							haModal({
								id: 'schedule-missmatch',
								backdrop: 'true',
								template: content,
								modalLock: true,
								cancel: {
									label: 'Close',
									fn: function () {
										window.location.href = '/book/FlightResults';
									}
								}
							});
						});
					};

					$scope.updateClass = function (fareName) {
						if (fareName === 'first') {
							$scope.isFirstClass = true;
						} else if (fareName === 'business') {
							$scope.isBusinessClass = true;
						} else if (fareName === 'extracomfort') {
							$scope.isExtraComfort = true;
						}

					};

					$scope.updateClassTypes = function () {

						angular.forEach($scope.selectedSegments, function (segment) {
							angular.forEach(segment.AvailBookingFares, function (fare) {
								var fareName = fare.Name.toLowerCase();
								$scope.updateClass(fareName);


								// push classType into seatClassTypes if it isn't there already
								if ($scope.seatClassTypes.indexOf(fareName) < 0) {
									$scope.seatClassTypes.push(fareName);
								}
							});
						});


						//rearrage seatclasses
						if ($scope.seatClassTypes.length === 3) {
							if ($scope.seatClassTypes[2] === 'extracomfort' && $scope.seatClassTypes[1] === 'first') {
								$scope.seatClassTypes[1] = 'extracomfort';
								$scope.seatClassTypes[2] = 'first';
							}
							if ($scope.seatClassTypes[2] === 'extracomfort' && $scope.seatClassTypes[1] === 'business') {
								$scope.seatClassTypes[1] = 'extracomfort';
								$scope.seatClassTypes[2] = 'business';
							}
						}
					};

					$scope.openURL = function (url) {
						var win = window.open(url, 'blank');
						win.focus();
					};

					$scope.updateClassTypesMultiSegment = function () {
						angular.forEach($scope.selectedSegments, function (segment) {
							angular.forEach(segment.AvailBookingFares, function (fare) {
								var fareName = fare.Name.toLowerCase();

								// push classType into seatClassTypes if it isn't there already
								if ($scope.seatClassTypes.indexOf(fareName) < 0) {
									$scope.seatClassTypes.push(fareName);
								}
							});
						});

						//rearrage seatclasses
						if ($scope.seatClassTypes.length === 3) {
							if ($scope.seatClassTypes[2] === 'extracomfort' && $scope.seatClassTypes[1] === 'first') {
								$scope.seatClassTypes[1] = 'extracomfort';
								$scope.seatClassTypes[2] = 'first';
							}
							if ($scope.seatClassTypes[2] === 'extracomfort' && $scope.seatClassTypes[1] === 'business') {
								$scope.seatClassTypes[1] = 'extracomfort';
								$scope.seatClassTypes[2] = 'business';
							}
						}
					};

					$scope.getDeviantLegs = function (segment, seatClass) {
						var deviantLegs = [];

						angular.forEach(segment.Legs, function (leg) {
							var found = false;
							angular.forEach(segment.AvailBookingFares, function (cabin) {
								var displayName = cabin.Name.toUpperCase();
								seatClass = seatClass.toUpperCase();
								if (cabin.IsMixedCabin && displayName === seatClass) {
									found = true;
								}
							});

							if (found) {
								deviantLegs.push(leg);
							}
						});

						return deviantLegs;
					};

					$scope.ErrorBackToHome = function (methodName, ErrorCodeHandle, ErrorMessage, result) {
						if (methodName === 'haFlightResultsAPI-fetch') {
							// Remove HTML tags(if found)
							if ($scope.IsChangeFlightBooking) {
								var regex = /(<([^>]+)>)/ig;
								var body = ErrorCodeHandle + ': ' + ErrorMessage;
								var errorMsg = body.replace(regex, '');
								window.location.href = '/book/error?ErrorType=Custom&PathType=ChangeFlight&ErrorCode=' + errorMsg;
							}
							else {

								var regex = /(<([^>]+)>)/ig;
								var body = ErrorCodeHandle + ': ' + ErrorMessage;
								var errorMsg = body.replace(regex, '');
								window.location.href = '/book/home?FltResNull=' + errorMsg;
							}
						}
						else {
							window.location.href = result.RedirectURL;
						}
					};


					$scope.handleExceptions = function (result, methodName) {
						if (result !== 'jsError') {
							switch (result.ErrorCodeHandle) {
								case 'NOFAREGROUP':
									$scope.ErrorBackToHome(methodName, result.ErrorCodeHandle, result.ErrorMessage, result);
									break;
								case 'NO_FLIGHTRESULTS_TRIPS':
									$scope.ErrorBackToHome(methodName, result.ErrorCodeHandle, result.ErrorMessage, result);
									break;
								case '20027':
								case '20058':
								case '20059':
									$scope.ErrorBackToHome(methodName, result.ErrorCodeHandle, result.ErrorMessage, result);
									break;
								case '20024':
									// Remove HTMl tags(if found)
									var regex = /(<([^>]+)>)/ig;
									var body = result.ErrorCodeHandle + ': ' + $('#NoFlightResultsErrorMsg').text();
									var errorMsg = body.replace(regex, '');
									window.location.href = '/book/home?FltResNull=' + errorMsg;
									break;
								case '20044':
									window.location.href = '/book/error?ErrorType=Custom&ErrorCode=' + result.ErrorCodeHandle;
									break;
								case '20045':
									window.location.href = '/book/error?ErrorType=Custom&ErrorCode=' + result.ErrorCodeHandle;
									break;
								case 'SessionTimeOut':
									window.location.href = '/book/error?ErrorType=SessionTimeOut';
									break;
								default:
									if (result.ErrorCodeHandle !== undefined) {
										window.location.href = result.RedirectURL;
									}
									else {
										return false;
									}
									break;
							}
						} else {
							window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
						}
					};

					$scope.mcbRestrictionModal = function () {
						$scs.get('MCBRestrictionModal').then(function (result) {
							$scope.restrictions = result.restrictions;
							$scope.heading = result.heading;
							$scope.topcontent = result.topcontent;
							$scope.disclaimers = result.disclaimers;
							$scope.subheading = result.subheading;
							haModal(haConfig.getTemplateUrl('book/flightresults/ha-mcb-restrictions-modal.html'), {
								id: 'mcbRestrictionModal',
								backdrop: true,
								scope: $scope
							});

						});

					};

					$scope.AddFlightsToTrip = function (e, isHold) {


						e.preventDefault();

						if (!angular.isUndefined(isHold) && isHold) {
							var selectedHoldFare = $rootScope.selectedHoldFare;

							var holdReservationsPrice = {};
							holdReservationsPrice.UniquePriceID = selectedHoldFare.id;
							holdReservationsPrice.TimeLimitDays = selectedHoldFare.numberOfDays;
							holdReservationsPrice.HoldFee = selectedHoldFare.price;
							holdReservationsPrice.MSRCode = selectedHoldFare.msrCode;
							haFlightResultsAPI.updateSelectedHoldFare(holdReservationsPrice).success(function (result) {
								//console.log(result);
							}).error(function () {
								//console.log('Error in holding price');
							});
						}

						// Check for Previous date selection before adding flights //
						var selectedFlightsObj = $scope.selectedSegments;
						var returnResult = 'true';
						for (var i = 0; i < selectedFlightsObj.length - 1; i++) {
							var noOfLegs = selectedFlightsObj[i].Legs.length;
							if (selectedFlightsObj[i + 1].Legs[0].DepartureDateTime < selectedFlightsObj[i].Legs[noOfLegs - 1].ArrivalDateTime) {
								var Invalidflight1 = i + 1;
								var Invalidflight2 = i + 1 + 1;
								returnResult = 'false-' + Invalidflight1 + '/' + Invalidflight2;
								break;
							}
						}

						if (returnResult.indexOf('false') !== -1) {
							var returnResultArray = returnResult.split('-');
							var invalidSelectionsArray = returnResultArray[returnResultArray.length - 1].split('/');
							var dynamicContent = invalidSelectionsArray;
							$scope.populatePrevDateValidationPopup(dynamicContent);
							var content = $('#PrevDateValReturnToSearchPopup').html();
							haModal({
								id: 'ReturnToFlightSearchPopupModal',
								backdrop: 'true',
								template: content
							});
							return false;
						}

						var selection = new SelectedFlights();
						var index = 0;
						angular.forEach($scope.selectedSegments, function (flight) {
							selection.FareDetails.push(new FareDetail(index + 1, flight.UniquerefKey, flight.selectedSeatClass, flight.IsMileagePricing));
							index = index + 1;
						});
						selection.PricingType = $scope.PricingType;
						selection.SelectedFareGrid = $scope.SelectedFareGrid;
						selection.Passengers = $scope.pax;
						//- Call Waiting spin
						$scope.waitingSegment = true;

						//Initiate intertitial
						haInterstitialAPI.getAirAvailability()
						.success(function (data) {
							haModal({
								id: 'InterstitialAirAvailability-modal',
								backdrop: 'true',
								template: data,
								modalLock: true
							});
							$scope.interstitialDynamicResize();
							//Air Availability Check
							haFlightResultsAPI.checkFlightAvailability(selection).success(function (result) {

								if ($scope.handleExceptions(result, 'haFlightResultsAPI-checkFlightAvailability')) {
									return;
								}
								$scope.waitingSegment = false;
								if (result.status === 1) {
									$scope.showFightsNoSeatsAvailableModal();
								} else if (result.status === 2) {
									$scope.showFightsScheduleMismatchModal();
								} else if (result.status === 0) {
									$('#FlightSearchForm').submit();
								}
							}).error(function () {
								$scope.handleExceptions(data, 'haFlightResultsAPI-checkFlightAvailability');
								$scope.waitingSegment = false;
							});
						});
					};

					window.onresize = function () {
						$scope.interstitialDynamicResize();
					};

					$scope.interstitialDynamicResize = function () {
						$('.ha-modal#InterstitialAirAvailability-modal .modalContainer').css({
							'width': $(window).width(),
							'height': $(window).height(),
							'padding-top': $(window).height() * 0.20
						});
					};

					$scope.populatePrevDateValidationPopup = function (dynamicContent) {
						var newContent = $('#ScData_PrevDateValidation_Unedited').html().replace('~INVALIDFLIGHT1~', dynamicContent[0]).replace('~INVALIDFLIGHT2~', dynamicContent[1]);
						$('#ScData_PrevDateValidation_DynamicContent').html(newContent);
					};

					$scope.showSelectAllFlightsPopup = function () {
						var OriginalContent = $('#SelectOpenSegmentPopupContentNoEdit').html();
						$('#SelectOpenSegmentPopupContent').html(OriginalContent.replace('~SEGMENT~', $scope.IsNewFlightSelected.indexOf('false') + 1));
						var content = $('#SelectOpenSegmentValidationPopup').html();
						haModal({
							id: 'SelectAllSegmentsModalPopup',
							backdrop: 'true',
							template: content
						});
						return false;
					};

					$scope.getMaxPrice = function (fareTypes) {

						var fareAmount = 0.0;

						if (fareTypes !== null && fareTypes !== undefined) {
							fareAmount = parseFloat(fareTypes[0].ExchangeInfo.TotalFareDifference);
							if (fareTypes.length > 1) {

								angular.forEach(fareTypes, function (fareType) {
									if (parseFloat(fareAmount) < parseFloat(fareType.ExchangeInfo.TotalFareDifference)) {
										fareAmount = parseFloat(fareType.ExchangeInfo.TotalFareDifference);
									}
								});
							}

						}
						return fareAmount;
					};
				}];

			var HaBookFlightResultsLink = function ($scope, $el) {

				$scope.selectedTripDate = [];

				$scope.additionalCostArray = [];

				$scope.fareGridResults = {};

				$scope.exampleMethod = function () {
					return $scope;
				};

				setTimeout(function () {
					$scope.selectClass = angular.element('.select-class-wrapper');
					$scope.continueBar = angular.element('.continue-bar-wrapper');
					$scope.editingSegment = false;
					$scope.openSegments = null;
				}, 0);

				$scope.selectNewDates = function () {
					$scope.$modalCancel();
					$scope.$broadcast('open-sticky-booking-widget');
				};

				$scope.getJourneyData = function (selectedFlight, selectedClass, tripID) {

					var jdDataList = {};
					jdDataList.JDDataList = [];

					var counter = 1;
					angular.forEach($scope.selectedSegments, function (segment) {

						var JDdata = {};
						JDdata.S = segment.UniquerefKey;
						if (selectedClass !== '-1') {
							JDdata.C = segment.selectedSeatClass === undefined ? segment.selectedSeatClass.substring(0, 1).toUpperCase() : segment.selectedSeatClass.substring(0, 1);
						} else {
							JDdata.C = selectedClass;
						}

						JDdata.TId = counter++;
						JDdata.JDInd = true;
						jdDataList.JDDataList.push(JDdata);
					});

					if (selectedFlight != null) {
						var JDdata = {};
						JDdata.S = selectedFlight.UniquerefKey;
						JDdata.C = selectedClass;
						JDdata.TId = tripID;
						JDdata.JDInd = true;
						if (jdDataList.JDDataList[tripID - 1] !== undefined) {
							jdDataList.JDDataList[tripID - 1] = JDdata;
						} else {
							jdDataList.JDDataList.push(JDdata);
						}
					}
					return jdDataList;
				};

				// Method for intercepting a click on a MCB fare to show the accept restrictions modal
				// To be used in a short-circuiting click handler
				// e.g `ng-click="checkMCB(result, segment, $parent.$index, class, $index) && selectFlight(result, segment, $parent.$index, class, $index);"`
				$scope.checkMCB = function (result, trip, resultIndex, seatClass, seatClassIndex, $event) {
					// If disabled, stop doing things.
					if (!seatClass || seatClass.AvailableSeats === 0) {
						return false;
					}

				    //method to add residual pop up
					if (seatClass.FareTypes !== null && seatClass.FareTypes.length > 0 && seatClass.FareTypes[0].ExchangeInfo !== null && seatClass.FareTypes[0].ExchangeInfo.TotalFareDifference !== null && seatClass.FareTypes[0].ExchangeInfo.TotalFareDifference < 0) {
					    if ($scope.TripCount == 1 || ($scope.TripCount == 2 && trip.TripId == 2)) {
					        $timeout(function () {
					            haModal({
					                id: 'ResidualPopUpModal',
					                backdrop: 'true',
					                template: angular.element('.residualPopupModal'),
					                scope: $scope
					            });
					        });
					        return;
					    }
					}

					// 1st & 2nd segment normal pass through
					if ((seatClass.Name.toLowerCase() !== 'maincabinbasic' && trip.TripId == 1) || 
							((seatClass.Name.toLowerCase() == 'coach' || seatClass.Name.toLowerCase() == 'first') && (trip.TripId > 1) && $scope.selectedSegments[0].selectedSeatClass !=="Maincabinbasic") ||
							((seatClass.Name.toLowerCase() == 'maincabinbasic') && (trip.TripId > 1) && $scope.selectedSegments[0].selectedSeatClass =="Maincabinbasic")){
						// Continue normally
						return true;
					}

					// Previously selected MCB to MC upgrade
					if (seatClass.Name.toLowerCase() !== 'maincabinbasic' && trip.TripId > 1 && $scope.selectedSegments[0].selectedSeatClass ==="Maincabinbasic") {
						var previousSegmentFare = $scope.selectedSegments[$scope.TripCount - 2].AvailBookingFares.filter(function(cls) { return cls.Name === "Maincabinbasic"})[0].FareTypes[0].TotalDisplayFare;
						var currentSegmentFare = result.AvailBookingFares.filter(function(cls) { return cls.Name === "Coach"})[0].FareTypes[0].TotalDisplayFare;
						var upgradeFareDiff = Math.ceil(currentSegmentFare - previousSegmentFare);
						
						$scope.upgradeAmount = (upgradeFareDiff === 0 || upgradeFareDiff === -0) ? '0' : upgradeFareDiff;
						// MCB to MC Upgrade warning modal
						haModal(haConfig.getTemplateUrl('book/flightresults/ha-upgrade-warning-modal.html'), {
							id: 'mcbUpgradeWarning',
							backdrop: true,
							scope: $scope,
							size:'modal-md'
						});
						
						$scope.upgradeToMainCabin = function(selectedSegments, closeModalEvent) {
							$scope.selectFlight(result, trip, resultIndex, seatClass, seatClassIndex);
							$scope.selectedSegments = selectedSegments;
							$scope.selectedSegments.forEach(function(seg) {
								if (seg.selectedSeatClass === 'Maincabinbasic') {
									seg.selectedSeatClass = 'Coach';
									seg.Legs[0].selectedClassForDisplay = 'Main Cabin';
	
									if(seg.AvailBookingFares[0].Name === "Maincabinbasic" && seg.AvailBookingFares[0].isSelected ){
											seg.AvailBookingFares[0].isSelected = false;
											seg.AvailBookingFares[1].isSelected = true;
									}	
								}
							});
							closeModalEvent();
						}	
						return false;
					}
					
					$scope.acceptRestrictionsCallback = function() {
						// Pass through to the original selectFlightCall;
						$scope.selectFlight(result, trip, resultIndex, seatClass, seatClassIndex);
					};

					$scope.rejectRestrictionsCallback = function() {
						// Find the coach seatClass
						var found = false;
						for (var i = 0; i < result.AvailBookingFares.length; i++) {
							var c = result.AvailBookingFares[i];
							if (c.Name && c.Name.toLowerCase() === 'coach') {
								found = true;
								break;
							}
						}
						if (found) {
							// Select coach instead
							$scope.selectFlight(result, trip, resultIndex, c, i);
						}
					};
					try {
						$scope.MCBAmount = $scope.getMaxPrice(result.AvailBookingFares.filter(function(fare) { return fare.Name.toLowerCase() === 'maincabinbasic' })[0].FareTypes);
						$scope.MCAmount = $scope.getMaxPrice(result.AvailBookingFares.filter(function(fare) { return fare.Name.toLowerCase() === 'coach' })[0].FareTypes);
					} catch(e) {
						console.warn('Error getting amounts for modal.', e);
					}
					// Otherwise we have selected MCB for the first result and need to show the modal
					haModal(haConfig.getTemplateUrl('book/flightresults/ha-mcb-accept-restrictions-modal.html'), {
						id: 'mcbFlightResultsModal',
						backdrop: true,
						scope: $scope,
						size:'custom-modal-width'
					});
				};

				$scope.selectFlight = function (result, trip, idx, seatClass, seatClassIndex) {
					$scope.isReshopPath = true;
					if (seatClass && seatClass.AvailableSeats !== 0 && !result.isSelectedFlight || !seatClass) {
						if ($scope.IsChangeFlightBooking) {

							if (seatClass.FareTypes[0].SolutionType === 'N') {
								return false;
							}
						}
						$scope.Trips[trip.TripId - 1].AddToSelectedFlightCounter = true;
						var selectedSeatClass = '-1';
						if (seatClass && seatClassIndex != null) {
							angular.forEach(trip.ActiveTab.Flights, function (theresult) {
								angular.forEach(theresult.AvailBookingFares, function (theclass) {
									theclass.isSelected = false;
								});
							});

							//Flight Selection//
							result.AvailBookingFares[seatClassIndex].isSelected = true;
							$scope.IsNewFlightSelected[trip.TripId - 1] = 'true';
							//Flight Selection//

							if (seatClass.IsMixedCabin) {
								result.hasMixedClass = true;
							}

							result.selectedSeatClass = seatClass.Name;
							selectedSeatClass = seatClass.Name.toLowerCase() === 'coach' ? 'C' : seatClass.Name.toLowerCase() === 'extracomfort' ? 'E' : seatClass.Name.toLowerCase() === 'first' ? 'F' : seatClass.Name.toLowerCase() === 'business' ? 'B' : seatClass.Name.toLowerCase() === 'maincabinbasic' ? 'M' : '-1';

						} else {
							selectedSeatClass = '-1';
						}
						result.isSelected = true;
						result.IsRoundTripFare = false;
						trip.hasSelectedFlight = true;
						$scope.selectedTripDate[trip.TripId] = new Date(result.Legs[result.Legs.length - 1].ArrivalDate);
						if ($scope.Trips[trip.TripId - 1].ShowPreSelected === true) {
							$scope.Trips[trip.TripId - 1].ShowPreSelected = false;
							angular.forEach($scope.Trips[trip.TripId - 1].ActiveTab.Flights, function (flight) {
								flight.isSelectedFlight = false;
							});
						}
						$scope.flightSelectedEvent = true;

						$scope.LastStepThruQueryID = trip.TripId;
						var upgradeMainCabinElegible = (($scope.currentSegmentId > 1) && (result.selectedSeatClass!=="Maincabinbasic"));
						if ($scope.PricingType === 'RoundTripFare' && ($scope.Trips.length !== $scope.selectedFlightCounter() || $scope.TripType === 2)) {
							// if (tripId !== $scope.Trips.length) {
							$scope.loadingSegment = true;
							$('#segment-' + trip.TripId).addClass('current');
							angular.element('#segment-' + trip.TripId).addClass('loading');
							// }

							if ($scope.TripType === 2) {
								selectedSeatClass = '-1';
							}

							var jdData = $scope.getJourneyData(result, selectedSeatClass, trip.TripId);


							haFlightResultsAPI.SearchFlightsStepThrough(jdData, trip.TripId, false).success(function (results) {
								if ($scope.handleExceptions(results, 'haFlightResultsAPI-SearchFlightsStepThrough')) {
									return;
								}
								$scope.lieFlatAvailable = $scope.hasLieFlat(results);

								angular.element('#segment-' + trip.TripId).removeClass('loading');
								$scope.selectFlightStyleChanges(result, trip.TripId, idx);

								$scope.FormatResults(results);
								if (results.FareGrid && results.FareGrid.FareGridCellList &&
									results.FareGrid.FareGridCellList.length === 0) {
									results.Trips[0].TripId = trip.TripId + 1;
									$scope.Trips[trip.TripId] = results.Trips[0];
									$scope.loadingSegment = false;
								} else {
									//populate fare grid
									$scope.FareGrid = {};
									$scope.fareGridResults = {};
									angular.forEach(results.FareGrid.FareGridCellList, function (cellDetails) {
										$scope.fareGridResults[cellDetails.Key] = cellDetails.TotalFare;
										$scope.FareGrid[cellDetails.Key] = cellDetails;

										if (cellDetails.TotalFare != null && result.RoundTripFare === undefined) {
											result.RoundTripFare = cellDetails;
											result.IsRoundTripFare = true;
										}
									});
									$scope.UnavailableClasses = results.FareGrid.UnAvailableClasses;

									$scope.selectFlightStyleChanges(result, trip.TripId, idx);
								}
							}).error(function (results) {
								$scope.handleExceptions(results, 'haFlightResultsAPI-SearchFlightsStepThrough');
								$scope.loadingSegment = false;
								$('#segment-' + trip.TripId).addClass('current');

							});
						}
						else {
							$scope.selectFlightStyleChanges(result, trip.TripId, idx);
							angular.forEach($scope.selectedSegments, function (currentSeg) {
								currentSeg.isMulticityFare = false;
							});
							$scope.CalculateTaxesVar = 'Calculate';
							$scope.selectedSegments[trip.TripId - 1].isMulticityFare = true;
							$scope.calculatePerPersonPriceMulticity($scope.selectedSegments[trip.TripId - 1]);
							if ($scope.IsChangeFlightBooking) {
								$scope.calculatePerPersonPriceChangeFlight($scope.selectedSegments[trip.TripId - 1]);
							}

						}
					}
					else if (!trip.hasSelectedFlight) {
						$scope.IsNewFlightSelected[trip.TripId - 1] = 'false';
					}
				};

				$scope.calculatePerPersonPriceMulticity = function (selSeg) {
					angular.forEach(selSeg.AvailBookingFares, function (fares) {
						if (selSeg.selectedSeatClass === fares.Name) {
							if (fares.FareTypes.length > 1) {
								$scope.totalPricePerPaxDisplayMulticity = fares.FareTypes[0].TotalMultiCityPerPersonFare;
							}
							else {
								$scope.totalPricePerPaxDisplayMulticity = fares.FareTypes[0].TotalMultiCityPerPersonFare;
							}
						}
					});
				};


				$scope.calculatePerPersonPriceChangeFlight = function (selSeg) {
					angular.forEach(selSeg.AvailBookingFares, function (fares) {
						if (selSeg.selectedSeatClass === fares.Name) {
							if (fares.FareTypes.length > 0) {
								$scope.totalPricePerPaxDisplayChangeFlight = $scope.getMaxPrice(fares.FareTypes);
							}
						}
					});
				};

				$scope.selectedFlightCounter = function () {
					var SelectedFlightsCounter = 0;
					angular.forEach($scope.Trips, function (currentTrip) {
						if (currentTrip.AddToSelectedFlightCounter) {
							SelectedFlightsCounter = SelectedFlightsCounter + 1;
						}
					});
					return SelectedFlightsCounter;
				};

				$scope.getSegmentBookingType = function (tripid) {
					return $scope.Trips[tripid - 1].IsMileagePricing === true ? 'MILES' : $scope.currency;
				};

				$scope.selectFlightStyleChanges = function (result, tripId, idx) {


					$scope.editingSegment = false;
					$scope.openSegments = null;
					$scope.selectedSegments[tripId - 1] = result;
					$scope.selectedSegments[tripId - 1].IsMileagePricing = $scope.Trips[tripId - 1].IsMileagePricing;

					$scope.updateClassTypes();
					$scope.currentSegment = angular.element('#segment-' + tripId);
					$scope.$broadcast('calcTaxes');
					$scope.currentSegment.addClass('hasSelectedFlight');
					$scope.selectedFlight = $el.find('#leg-' + tripId + '-result-' + idx);
					$scope.currentSegment.find('.result').removeClass('selected');
					$scope.currentSegment.find('.select').removeClass('hidden');
					$scope.selectedFlight.find('.select').addClass('hidden');
					$scope.selectedFlight.find('.defaultSelections').addClass('hidden');
					$scope.selectedFlight.find('.change').removeClass('hidden');
					$scope.currentSegment.find('section.result .keep').addClass('hidden');
					$scope.selectedFlight.scope().detailsVisible = false;

					$document.scrollToElement(angular.element('#select-flight-anchor'), 0, 650);
					//if ($scope.MarketType === 'SEGFARE') { $scope.switchSegment(); }

					$scope.switchSegment();

					setTimeout(function () {
						$scope.loadingSegment = false;
						$scope.selectedFlight.addClass('selected');
						if ($('.hairlineGrid a.selected').get(0)) {
							$('.hairlineGrid a.selected').get(0).click();
						}
						$scope.selectedFlight.find('.defaultSelections').addClass('hidden');
						$scope.$broadcast('calcTaxes');
					}, 100);

				};


				$scope.RecalculateTaxes = function () {
					$scope.$broadcast('calcTaxes');
				};

				$scope.switchSegment = function () {
					$scope.currentSegmentId += 1;
					//$scope.numberOfSegments = $scope.TripCount;
					//$scope.currentSegment = $el.find('.segment.current');
					//$scope.currentSegment.addClass('hasSelectedFlight').removeClass('current').next().addClass('current');
					$scope.showReceipt = false;
					if ($el.find('.hasSelectedFlight').length === $scope.TripCount) {

						$scope.selectClass.addClass('active');
						// set height of seat class selectors to match dynamic height of first column
						setTimeout(function () {
							$('.row.class-selection').each(function () {
								var colSegmentHeight = $(this).find('.col-segment').height();
								$(this).find('a.col-selection').height(colSegmentHeight + 'px');
							});
						}, 10);

						$scope.continueBar.addClass('active');
						$scope.showReceipt = true;
					} else {
						$scope.arrowUpdate();
					}
				};


				$scope.HideAllSegments = function () {
					$('.segment').addClass('hidden');
					$scope.showReceipt = false;
				};

				$scope.backToFlightResultsPage = function () {
					window.location.reload();
				};

				$scope.backToFlightResults = function () {

					var segment = null;
					if ($scope.TripType === 0 && $scope.LastStepThruQueryID !== 1) {
						angular.forEach($scope.Trips, function (segment) {
							angular.forEach(segment.ActiveTab.Flights, function (flight) {
								flight.isSelected = false;
							});
							segment.AddToSelectedFlightCounter = false;
						});
						segment = $scope.Trips[0];
						$scope.selectedSegments = [];
					}


					if (segment != null && $scope.PricingType === 'RoundTripFare' && $scope.TripType === 0 && segment.TripId !== $scope.LastStepThruQueryID) {
						// if (tripId !== $scope.Trips.length) {
						$scope.loadingSegment = true;
						angular.element('#segment-' + segment.TripId).addClass('loading');
						// }
						var jdData = $scope.getJourneyData();

						$scope.LastStepThruQueryID = segment.TripId;
						haFlightResultsAPI.SearchFlightsStepThrough(jdData, segment.TripId, true).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsAPI-SearchFlightsStepThrough')) {
								return;
							}
							$scope.loadingSegment = false;
							$scope.lieFlatAvailable = $scope.hasLieFlat(results);

							angular.element('#segment-' + segment.TripId).removeClass('loading');


							$scope.FormatResults(results);
							if (results.FareGrid && results.FareGrid.FareGridCellList &&
								results.FareGrid.FareGridCellList.length === 0) {
								$.extend($scope.Trips[segment.TripId - 1], results.Trips[0]);
							}
						}).error(function (results) {
							$scope.handleExceptions(results, 'haFlightResultsAPI-SearchFlightsStepThrough');
							angular.element('#segment-' + segment.TripId).removeClass('loading');
						});
					}

					$('.segment').removeClass('current hasSelectedFlight');
					$scope.currentSegmentId = 1;
					$scope.continueBar.removeClass('active');
					$scope.selectClass.removeClass('active');
					$('.segment:first').addClass('current');
					$('.result.selected .change').addClass('hidden');
					$('.result.selected .keep').removeClass('hidden');

					$document.scrollToElement(angular.element('#select-flight-anchor'), 0, 650);
				};

				$scope.editSegment = function (segment, flight) {

					// if you modify a segment, hide hold booking block if it's shown
					$rootScope.checkHoldBookingEligibility = false;

					//$scope.Trips.forEach(function (tripDetails) {
					//    if (tripDetails.TripId >= segment.TripId)
					//        tripDetails.AddToSelectedFlightCounter = false;
					//});


					var isAllSegmentSelected = $scope.IsAllFlightsSelected();

					if ($scope.IsChangeProgress !== true && isAllSegmentSelected) {

						$scope.Trips[segment.TripId - 1].AddToSelectedFlightCounter = false;
						if (flight) {
							flight.isSelected = false;
						}

						if ($scope.PricingType === 'RoundTripFare' && $scope.TripType === 0 && segment.TripId !== $scope.LastStepThruQueryID) {
							$scope.IsChangeProgress = true;
							// if (tripId !== $scope.Trips.length) {
							$scope.loadingSegment = true;
							angular.element('#segment-' + segment.TripId).addClass('loading');
							// }
							var jdData = $scope.getJourneyData();

							$scope.LastStepThruQueryID = segment.TripId;
							haFlightResultsAPI.SearchFlightsStepThrough(jdData, segment.TripId, true).success(function (results) {
								if ($scope.handleExceptions(results, 'haFlightResultsAPI-SearchFlightsStepThrough')) {
									return;
								}
								$scope.loadingSegment = false;
								$scope.lieFlatAvailable = $scope.hasLieFlat(results);

								angular.element('#segment-' + segment.TripId).removeClass('loading');
								$scope.FormatResults(results);
								if (results.FareGrid && results.FareGrid.FareGridCellList &&
									results.FareGrid.FareGridCellList.length === 0) {
									$.extend($scope.Trips[segment.TripId - 1], results.Trips[0]);
								}
								$scope.IsChangeProgress = false;
								$scope.IsNewFlightSelected[segment.TripId - 1] = 'false';
							}).error(function () {
								angular.element('#segment-' + segment.TripId).removeClass('loading');
								$scope.IsChangeProgress = false;
								$scope.IsNewFlightSelected[segment.TripId - 1] = 'false';
							});
						}


						//Call ITA if user clicks on Change link of Trip 1
						if ($scope.IsChangeFlightBooking && +segment.TripId === 1 && $scope.Trips.length > 1) {

							$scope.loadingSegment = true;
							angular.element('#segment-' + segment.TripId).addClass('loading');


							haFlightResultsAPI.fetch().success(function (results) {
								if ($scope.handleExceptions(results, 'haFlightResultsAPI-fetch')) {
									return;
								}
								$scope.lieFlatAvailable = $scope.hasLieFlat(results);
								$scope.resultsLoaded = true;
								$scope.SignifiedMarket = results.MarketID;
								if (results.Trips !== undefined && results.Trips.length > 0) {
									$scope.TripType = results.TripType;
									$scope.FormatResults(results);
									$.extend($scope, results);

									if (typeof ($scope.IsChangeFlightBooking) !== 'undefined' && $scope.IsChangeFlightBooking) {

										if ($scope.Trips.length === 1 && +$scope.Trips[0].TripId === 2) {
											$scope.Trips[0].TripId = 1;
										}
									}
									$scope.currency = results.Currency;
									//Update Currency for TripSummary footer.
									$scope.TripSummary.currency = results.Currency;
									//$scope.IsExtraComfort = results.IsExtraComfort;
									$scope.taxes = [];
									if (results.Trips != null) {
										$scope.TripCount = results.Trips.length;
										if ($scope.TripType === 0 && $scope.PricingType === 'RoundTripFare') {
											$scope.multiCity = true;
										} else {
											$scope.multiCity = false;
										}
									}
									$scope.arrowUpdate();

									angular.forEach(results.Trips, function (trip) {
										var hasCodeShareFlight = false;
										trip.ShowPreSelected = true;
										//show/hide Miles dropdown
										angular.forEach(trip.ActiveTab.Flights, function (flight) {
											hasCodeShareFlight = false;

											angular.forEach(flight.Legs, function (leg) {
												if (leg.IsCodeShare) {
													hasCodeShareFlight = true;
												}
											});
											if (!hasCodeShareFlight && $scope.ShowMilesToggle === false) {
												$scope.ShowMilesToggle = true;
											}
										});
									});
								}
							}).error(function () {
								$scope.handleExceptions('jsError', 'haFlightResultsAPI-fetch');
								$scope.resultsLoaded = true;
							});
						}

						$scope.currentSegment = angular.element('#segment-' + segment.TripId);
						$scope.currentSegmentId = segment.TripId;
						$scope.selectedFlight = $scope.currentSegment.find('.result.selected');
						$('.segment').removeClass('current');
						$scope.currentSegment.removeClass('hasSelectedFlight');
						$scope.currentSegment.addClass('current');
						$('.segment.current .result.selected .change').addClass('hidden');
						$('.segment.current .result.selected .keep').removeClass('hidden');

						$document.scrollToElement(angular.element('#select-flight-anchor'), 0, 650);

						$scope.selectClass.removeClass('active');
						$scope.continueBar.removeClass('active');


						segment.hasSelectedFlight = false;
						if ($scope.editingSegment) {
							//$scope.currentSegment.addClass('hasSelectedFlight');
							//$scope.currentSegment.find('.result.selected .keep').addClass('hidden');
							//$scope.currentSegment.find('.result.selected .change').removeClass('hidden');
							if ($scope.openSegments != null) {
								var seg = angular.element('#segment-' + $scope.openSegments.TripId);
								seg.addClass('hasSelectedFlight');
								seg.find('.result.selected .keep').addClass('hidden');
								seg.find('.result.selected .change').removeClass('hidden');
								$scope.openSegments = segment;
							}
						} else {
							$scope.editingSegment = true;
							$scope.openSegments = segment;
						}


					} else if (!isAllSegmentSelected) {
						$scope.showSelectAllFlightsPopup();
					}


				};

				$scope.IsAllFlightsSelected = function () {

					if ($scope.IsNewFlightSelected.indexOf('false') !== -1) {
						return false;
					} else {
						return true;
					}
				};

				$scope.openSearchModal = function (flight) {

					if (!$scope.CurrentFlightScope || $scope.CurrentFlightScope === 'OFF') {
						$scope.CurrentFlightScope = 'ON';
					} else {
						$scope.CurrentFlightScope = 'OFF';
					}

					$scope.$seatViewModal.toggleClass('is-open');
					$scope.$seatViewModal.css({
						'margin-top': function () {
							return window.pageYOffset - ($(this).height() / 2);
						}
					});

					if ($scope.CurrentFlightScope === 'ON') {
						$scope.$broadcast('$UpdateSegments', flight);
					}
				};

				$scope.CalculateTaxesVar = '';
				$scope.$on('fareGridtotalPriceReady', function (event, data) {
					var fareAmount = 0.0;
					if ($scope.FareGrid[data] !== undefined || data === 'SegFareSkipCheck') {
						fareAmount = $scope.fareGridResults[data] + parseFloat(fareAmount);
						$scope.totalPricePerPaxDisplay = $scope.fareGridResults[data];
						$scope.selectedSegments[$scope.selectedSegments.length - 1].RoundTripFare = $scope.FareGrid[data];
						$scope.selectedSegments[$scope.selectedSegments.length - 1].IsRoundTripFare = true;
						$scope.SelectedFareGrid = data;
						$scope.CalculateTaxesVar = 'Calculate';
					}
				});


				$scope.seatClassCtrl = function ($scope) {

					$scope.selectSeatClass = function (fareType, classType, id) {

						if (!$('#' + classType + id).hasClass('tempDisabled') && !$('#' + classType + id).hasClass('disabled')) {
							if (fareType === 'SegmentFare') {
								$scope.segment.selectedSeatClass = classType;
								$scope.$emit('fareGridtotalPriceReady', 'SegFareSkipCheck');
								$scope.RecalculateTaxes();
								//Tell the Hold Bookings Controller to check Hold Bookings Eligibility
								$rootScope.checkHoldBookingEligibility = true;
							} else if (fareType === 'RoundTripFare') {
								var selectedFareCodeArray = [];
								if (!$scope.disabled) {
									var tempSeatClass = $scope.segment.selectedSeatClass + id;
									$scope.segment.selectedSeatClass = classType;
									$scope.RecalculateTaxes();
									var selectedFareCode = '';
									var i = 0;
									var selectedSegArray = [];
									$('.hairlineGrid a.selected').each(function (/* index */) {
										// NEP: never used.
										// var total = $('.hairlineGrid a.selected').length;
										selectedSegArray[i] = $(this).attr('id');
										selectedFareCode = selectedFareCode + $(this).attr('id');
										i++;
									});
									selectedFareCode = selectedFareCode.replace(tempSeatClass, classType + id);
									selectedSegArray[selectedSegArray.indexOf(tempSeatClass)] = classType + id;
									selectedFareCodeArray = selectedSegArray;
									// Tell the Hold Bookings Controller to check Hold Bookings Eligibility
									if (!angular.isUndefined(selectedFareCodeArray) && selectedFareCodeArray.length === 2) {
										$rootScope.checkHoldBookingEligibility = true;
									}
									$scope.$emit('fareGridtotalPriceReady', selectedFareCode);
								}
								$scope.CalculateAdditionalCost(selectedFareCodeArray, classType, id);
								$scope.RecalculateTaxes();
							} else {
								return;
							}
						}

					};

					$scope.CalculateAdditionalCost = function (selectedFareCodeArray /* , classType, idx */) {
						// NEP: never used.
						// var currentSegmentAdCostArray = [];
						$('.hairlineGrid div.class-selection a').each(function () {
							var currentCell;
							if (!$(this).hasClass('disabled') && selectedFareCodeArray.indexOf($(this).attr('id')) === -1) {

								var currentFareCodeArray = [];
								angular.forEach(selectedFareCodeArray, function (arrayObjValue) {
									currentFareCodeArray.push(arrayObjValue);
								});

								currentCell = $(this).get(0).id;
								var rowIndex = currentCell.slice(-1);
								for (var m = 0; m < currentFareCodeArray.length; m++) {
									if (currentFareCodeArray[m].indexOf(rowIndex) !== -1) {
										currentFareCodeArray[m] = currentCell;
										break;
									}
								}
								var CurrentCellFareCode = currentFareCodeArray.join();
								CurrentCellFareCode = CurrentCellFareCode.replace(/,/g, '');
								var OldBaseTotal = $scope.fareGridResults[selectedFareCodeArray.join().replace(/,/g, '')];
								if ($scope.fareGridResults[CurrentCellFareCode] != null) {
									var addCost = (parseFloat($scope.fareGridResults[CurrentCellFareCode]) - parseFloat(OldBaseTotal)).toFixed(2);
									$scope.additionalCostArray[currentCell] = addCost;
									$('#' + currentCell).removeClass('tempDisabled');
									$('#' + currentCell).find('span.adcostDisplay').show();
									$('#' + currentCell).find('span.TempNotAvailable').hide();
									$('#' + currentCell).find('span.mixedCabinRT').show();
								} else if ($scope.fareGridResults[CurrentCellFareCode] == null && selectedFareCodeArray.length !== 1) {
									$('#' + currentCell).addClass('tempDisabled');
									$('#' + currentCell).find('span.adcostDisplay').hide();
									$('#' + currentCell).find('span.TempNotAvailable').show();
									$('#' + currentCell).find('span.mixedCabinRT').hide();
								}
							} else {
								if ($(this).hasClass('disabled')) {
									$(this).find('span.TempNotAvailable').hide();
									$(this).find('span.adcostDisplay').hide();
									$('#' + currentCell).find('span.mixedCabinRT').show();
								}
							}
						});

					};


					$scope.stateClass = function (Idx) {

						$scope.InitStateGrid();
						$scope.disabled = false;

						if ($scope.segment.selectedSeatClass === $scope.classType) {
							$scope.selected = true;
							return 'selected';
						} else {
							$scope.selected = false;
						}

						var isCellDisabled = false;

						if ($scope.PricingType === 'SegmentFare') {
							if (!$scope.segment[$scope.classType] || $scope.segment[$scope.classType].AvailableSeats === 0) {
								$scope.disabled = true;
								return 'disabled';
							}
						} else if ($scope.PricingType === 'RoundTripFare') {
							angular.forEach($scope.UnavailableClasses, function (unavailableCellID) {
								if (unavailableCellID === Idx) {
									isCellDisabled = true;
								}
							});
							if (isCellDisabled) {
								$scope.disabled = true;
								return 'disabled';
							}
						}
						return undefined;
					};


					// selected : segment.selectedSeatClass === classType, disabled: !segment[classType] || segment[classType].AvailableSeats === 0

					$scope.InitStateGrid = function () {
						for (var i = 0; i < $scope.segment.AvailBookingFares.length; i++) {

							if ($scope.segment.AvailBookingFares[i].Name.toLowerCase() === $scope.classType) {

								if ($scope.PricingType === 'RoundTripFare' && $scope.UnavailableClasses) {
									if ($scope.UnavailableClasses.indexOf($scope.segment.selectedSeatClass + $scope.segmentIdx) !== -1) {
										$scope.segment.selectedSeatClass = undefined;
									}
								}

								// set first available seat class as selected
								if (!$scope.segment.selectedSeatClass && $scope.segment.AvailBookingFares[i].AvailableSeats) {
									$scope.segment.selectedSeatClass = $scope.classType;
								}

								// stash seat classes in a more usable place
								$scope.segment[$scope.classType] = $scope.segment.AvailBookingFares[i];
							}
						}

					};

					$scope.InitStateGrid();

				};

				$scope.seatClassCtrl.$inject = ['$scope'];
			};

			return {
				restrict: 'A',
				scope: true,
				link: HaBookFlightResultsLink,
				controller: HaBookFlightResultsController
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haBookFlightResultsEndOnEndModule', ['ui.router','haUplift', 'haFlightResultsEndOnEndAPI', 'duScroll', 'haUtilsModule', 'haModalService', 'haGlobalsModule', 'haFeatureFlagsModule', 'ngAnimate']);

	if (location.pathname.toLowerCase().indexOf('/book/flightresults') > -1) {
		module.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
			var resolve = {
				selectedResult: ['$stateParams', function ($stateParams) {
					return $stateParams.selectedResult;
				}],
				selectedClass: ['$stateParams', function ($stateParams) {
					return $stateParams.selectedClass;
				}]
			};
			var views = {
				segmentschosen: {
					templateProvider: ['haConfig', function (haConfig) {
						return haConfig.getTemplateUrlWithInclude('Book/FlightResults/ha-flightresults-selectedsegments.html');
					}]
				},
				flightchoices: {
					templateProvider: ['haConfig', function (haConfig) {
						return haConfig.getTemplateUrlWithInclude('Book/FlightResults/ha-flightresults-currentsegment.html');
					}],
					resolve: resolve
				},
				flightchoiceshideec: {
					templateProvider: ['haConfig', function (haConfig) {
						return haConfig.getTemplateUrlWithInclude('Book/FlightResults/ha-flightresults-currentsegment-hide-ec.html');
					}],
					resolve: resolve
				}
			};
			var ctrl = ['$scope', '$rootScope', '$state', '$window', function ($scope, $rootScope, $state, $window) {
				$scope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
					var from = parseInt(fromParams.id, 10);
					var to = parseInt(toParams.id, 10);
					var toSelectedClass = toParams.selectedClass;
					var fromSelectedClass = fromParams.selectedClass;
					// Check if MCB is available to purchase
					var isMCBAvailable = $scope.resultItem[1].FareDetails.filter(function (fare) { return fare.CabinType === 'MAINCABINBASIC' }).length > 0;
					// Check if MCB was selected on first segment
					var mcbSelected = ($scope.currentSegmentId > 1) ? $scope.resultItem.filter(function (item) { return item.selectedSeatClass === 'MAINCABINBASIC' }) : false;
					var mcbSelectedTS = ($scope.currentSegmentId > 1) ? $scope.TripSummary.Trips.filter(function (item) { return item.selectedSeatClass === 'MAINCABINBASIC' }) : false;
					var mcbChangeClick = (fromSelectedClass === 0 && to !== 1) || (fromSelectedClass === '' && to !== 1);
					if (mcbSelected.length > 0 && $scope.resultItem[$scope.currentSegmentId].FareDetails[toSelectedClass].CabinType === 'COACH' && mcbSelectedTS.length > 0 && mcbChangeClick) {
						// Is user attempting to upgrade from MCB to Main Cabin - Trigger warning Modal
						event.preventDefault()
						$scope.showUpgradeWarningModal($scope, from, toSelectedClass);
					}

					if (toSelectedClass === 0 && from === 1 && !$scope.$parent.acceptRestrictions && isMCBAvailable) {
						// If User selected Main Cabin Basic on their first segment, show them restrictions modal
						event.preventDefault()
						$scope.showRestrictionsModal($scope);
					}
				});
				$scope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
					var from = parseInt(fromParams.id, 10);
					var to = parseInt(toParams.id, 10);
					var seg;
					var selectedResult;
					var selectedClass;
					var needsReload = true;
					var custom;

					// Defense - out of bounds ids
					if ((isNaN(to) && toState.name !== 'segment.complete') || to > $scope.TripCount || to < 1) {
						location.href = location.pathname;
					}

					// Page refresh - redirect to segment 1
					if (!fromState.name && (to > 1 || !toParams.id)) {
						$state.transitionTo('segment.x', { id: 1 }, { notify: false, location: 'replace' });

						$rootScope.dtmVals = {
							pageName: window.digitalData.page.pageInfo.name + ':segment:1',
							pageURL: [location.origin, location.pathname, location.search, '#/segment/1'].join('')
						};
						return;
					}

					$rootScope.dtmVals = {
						pageName: (window.digitalData && window.digitalData.page) ? window.digitalData.page.pageInfo.name + (!!to ? ':segment:' + to : ':complete') : 'Error: window.digitalData.page is undefined for search: ' + location.search,
						pageURL: [location.origin, location.pathname, location.search, !!to ? '#/segment/' + to : '#/complete'].join('')
					};

					// Are we going backwards
					if (!isNaN(from) && !isNaN(to) && from > to || fromState.name === 'segment.complete') {
						$scope.hasLieFlat($scope.searchResults[to - 1]);
						$scope.hasECPreferred($scope.searchResults[to - 1]);
						$scope.hasMCBOferred($scope.searchResults[to - 1]);
						seg = $scope.TripSummary.Trips[to - 1];

						if (seg) {
							$scope.$emit('flightChanged', seg);
							if (seg.isDiscountNotApplied) {
								$scope.removePromo(to === 1);
							} else {
								$scope.editSegment(seg, $scope.isClassModified());
								$scope.detailsVisible = false;
							}
						}
					} else {
						// Are we done or returning from complete
						if ((toState.name === 'segment.complete') || (fromState.name === 'segment.complete')) {
							to = $scope.TripCount + 1;
						}
						// If returning we have to use the fromParams
						if (fromState.name === 'segment.complete') {
							toParams.selectedResult = fromParams.selectedResult;
							toParams.selectedClass = fromParams.selectedClass;
						} else if (isNaN(parseInt(toParams.selectedResult, 10)) || isNaN(parseInt(toParams.selectedClass, 10))) {
							// They hit forward so we have to find their choices with the isSelected Properties
							seg = $scope.searchResults[$scope.searchResults.map(function (res) {
								return res.TripId;
							}).indexOf(to - 1)];
							toParams.selectedResult = (seg) ? seg.ActiveTab.TripAndFareDetails.map(function (flt) {
								return flt.isSelectedFlight;
							}).indexOf(true) : undefined;
							needsReload = false;

							if (toState.name !== 'segment.complete') {
								$scope.hasLieFlat($scope.searchResults[to - 1]);
								$scope.hasECPreferred($scope.searchResults[to - 1]);
								$scope.hasMCBOferred($scope.searchResults[to - 1]);
							}
							// Defense - around refreshes then back/forward - page refresh then forward
							if ((typeof toParams.selectedResult !== 'undefined') && toParams.selectedResult < 0 && toState.name === 'segment.x') {
								$state.transitionTo('segment.x', { id: 1 }, { location: 'replace', notify: false });
								return;
							} else if (((typeof toParams.selectedResult !== 'undefined') && toParams.selectedResult < 0) || (toState.name === 'segment.complete' && $scope.searchResults.length < $scope.TripCount)) {
								location.replace(document.referrer);
								return;
							}

							toParams.selectedClass = (typeof toParams.selectedResult !== 'undefined') ? seg.ActiveTab.TripAndFareDetails[toParams.selectedResult].FareDetails.map(function (cls) {
								return cls.isSelected;
							}).indexOf(true) : undefined;
						}

						seg = $scope.searchResults.filter(function (res) {
							return res.TripId === to - 1;
						})[0];
						selectedResult = (seg && $scope.resultItem[to - 1]) ? seg.ActiveTab.TripAndFareDetails.filter(function (result) {
							return result.UniqueReferenceKey === $scope.resultItem[to - 1].UniqueReferenceKey;
						})[0] : undefined;
						selectedClass = (selectedResult) ? selectedResult.FareDetails[toParams.selectedClass] : undefined;

						// We found selected stuff
						if (seg && selectedResult && selectedClass) {
							seg.hasSelectedFlight = false;
							$scope.selectFlightWithPrice(selectedResult, seg, selectedClass, needsReload);
							$scope.detailsVisible = false;
						}
					}
				});
				// Use history.go for change buttons
				$scope.changeClicked = function (from, to) {
					from = parseInt(from, 10);
					var diff = (isNaN(from)) ? $scope.TripCount - to + 1 : from - to;
					$window.history.go(-1 * diff);
					$scope.hasLieFlat($scope.searchResults[to - 1]);
					$scope.hasECPreferred($scope.searchResults[to - 1]);
					$scope.hasMCBOferred($scope.searchResults[to - 1]);
				};

				$scope.resultItem = [];
				$scope.setResultItem = function (item, indx, seatClass) {
					$scope.resultItem[indx] = item;

					// track selection before proceeding
					if (seatClass) {
						var offerType = seatClass.toLowerCase();

						if (!$rootScope.isMobile && offerType === 'extracomfort' || offerType === 'preferred') {
							document.body.dispatchEvent(new CustomEvent('UpgradeSeatSelected', {
								'detail': {
									'seatType': offerType,
									'pageName': $rootScope.dtmVals.pageName,
									'pageURL': $rootScope.dtmVals.pageURL
								}
							}));
						}
						if (offerType === 'maincabinbasic') {
							document.body.dispatchEvent(new CustomEvent('MainCabinBasicSelected', {
								'detail': {
									'pageName': $rootScope.dtmVals.pageName,
									'pageURL': $rootScope.dtmVals.pageURL
								}
							}));
						}

						if (indx === 1) {
							document.body.dispatchEvent(new CustomEvent('UpgradeToMainCabin', {
								'detail': {
									'pageName': $rootScope.dtmVals.pageName,
									'seatType': offerType
								}
							}));

						}
					}
				};
			}];

			$stateProvider
				.state('segment', { abstract: true, url: '/segment', controller: ctrl })
				.state('segment.x', { url: '/:id', params: { selectedResult: '', selectedClass: '' }, views: views })
				.state('segment.complete', { url: '^/complete', params: { selectedResult: '', selectedClass: '' }, views: views });

			$urlRouterProvider.otherwise('/segment/1');
		}])
			.run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {
				$rootScope.$state = $state;
				$rootScope.$stateParams = $stateParams;
			}]);

	}

	module.directive('haBookFlightResultsEndOnEnd', [
		'$window', '$document', '$timeout', 'haGlobals', 'haConfig', 'haUplift','haFlightResultsEndOnEndAPI', '$rootScope', '$filter', 'haHttpService', '$state', 'haSitecoreStrings', '$log', 'haUtils', 'haDateUtils', 'haLaunchDarklyAPI',
		function ($window, $document, $timeout, haGlobals, haConfig, haUplift, haFlightResultsEndOnEndAPI, $rootScope, $filter, haHttpService, $state, $scs, $log, haUtils, haDateUtils, halaunchDarklyAPI) {

			// CS: For mobile safari, we have to force reload the flight results page when coming back from the pax info page due Safari's fancy "back-forward cache"
			// http://stackoverflow.com/questions/8788802/prevent-safari-loading-from-cache-when-back-button-is-clicked
			// Fixes bug #77879
			window.onpageshow = function (event) {
				if (event.persisted) {
					window.location.reload();
				}
			};

			//globals
			var serverShortDate = $window.serverShortDate;
			var enablePassengerTripSummary = $window.enablePassengerTripSummary;
			var isHoldEligible = $window.isHoldEligible;
			var sortKey = 'default';

			var haBookFlightResultsEndOnEndController = [
				'$scope', 'haInterstitialAPI', 'haModal', 'haPassengersService', 'haLaunchDarklyService',
				function ($scope, haInterstitialAPI, haModal, $pax, haLaunchDarklyService) {

					// EnableRtTooltip from Sitecore Switch
					$scope.switchRtTooltips = $scope.$switch('BookingWidget:EnableRtTooltips');
					$scope.showFareTooltip = (haUtils.localStorageGet('hideFareTooltip') !== 'true');
					$scope.lieFlatAvailable = false;
					$rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off

					var flightResultsLimitIncrement = 10,
						initialFlightResultsLimit = 10,
						interstitialPullUpDelay = 2000;

					// for resetting all the selections and data
					$scope.resetPage = function () {
						$scope.currentSegmentId = 0; // used to keep track of which segment is being focused
						$scope.searchResults = []; // used to store the search results
						$scope.editingSegment = false; // flag for if currently editing a segment
						$scope.resultsLoaded = false; // used to turn on/off loading animation and error messages
						$scope.showReceipt = false; // used to show/hide the faregrid and trip summart footer
						$scope.selectedChildFare = ''; // used to show/hide the child fare tooltip
						$scope.childFarePriceText = '';
						$scope.isNextAllowed = true; // toggle Next Arrow on and off
						$scope.isPreviousAllowed = true; // toggle previous arrow on and off
						$scope.taxDetails = false; // toggle tax details in receipt on and off
						$scope.flightSearchEditOpen = false; // toggle flightsearch edit open and closed
						$scope.priceTypeInfo = {}; // Miles drop down
						$scope.sortDropdown = []; // Sort drop down
						$scope.waitingForHeldReservation = false; // true if waiting for held reservation api call to finish
						$scope.listDetailsOpen = []; // keep track of the details section that are open
						//For tripsummary details footer
						$scope.TripSummary = {};
						$scope.TripSummary.Trips = [];
						$scope.PassengerTripSummary = {}; // new TripSummary
						$scope.UpsellGrid = {};

						$scope.IsError = false;
						$scope.ErrorMessage = '';
						$scope.IsServiceErrors = false;
						$scope.ServiceErrorMessage = '';
						$scope.errordescription = '';
						$scope.selectedPromo = null;
						$scope.hasChildPax = false;


						$rootScope.isEligible = false;
						$rootScope.selectedHoldFare = {};

						// these properties inside object to ensure scope inheritance
						$scope.model = {
							loadingSegment: true, // toggle loading animation on and off
							flightResultsLimit: initialFlightResultsLimit
						}
					};

					$scope.resetPage(); // call immediately

					$scope.$emit('$haBookFlightResultsReady');

					$scope.$pax = $pax;
					$pax.passengers = [];
					$scope.searchRequest = {}; // used to store search request

					// get searchRequest
					haGlobals(['searchRequest', 'showMilitaryTime', 'enableTCR', 'defaultDestinationImage', 'wholesaleDisableDollarMile'], function (searchRequest, showMilitaryTime, enableTCR, defaultDestinationImage, wholesaleDisableDollarMile) {
						$scope.searchRequest = searchRequest;
						$scope.TripType = searchRequest.FlightQueryTypeId;
						$scope.TripCount = searchRequest.FlightSearchSegmentList.length;
						$scope.showMilitaryTime = showMilitaryTime !== '0';
						$scope.enableTCR = enableTCR;
						$scope.multiCity = !enableTCR; // disable multi-city if ETCO is applied
						$scope.defaultDestinationImage = defaultDestinationImage;
						$scope.wholesaleDisableDollarMile = wholesaleDisableDollarMile;
					});

					// detect when last result scrolled into view to increase
					// ng-repeat limit for flight results
					$scope.$on('elementScrolledIntoView', function (event, data) {
						if (data === 'lastFlightResult') {
							$timeout(function () {
								$scope.model.flightResultsLimit += flightResultsLimitIncrement;
							});
						}
					});

					//trigger a page track on DTM for each leg in Flight Result
					$scope.$on('dtmPage', function (event, data) {
						window.digitalDataLoaded.then(function () {
							setTimeout(function () {
								document.body.dispatchEvent(new CustomEvent('FlightResults', { 'detail': data }));
							});
						});
					});

					$scope.resetFlightResultsLimit = function () {
						$scope.model.flightResultsLimit = initialFlightResultsLimit;
					}

					//method for detecting lieflats in any segments
					$scope.hasLieFlat = function (result) {
						var hasLieFlat = false;
						if (typeof result !== 'undefined' && (!result.ActiveTab || !$scope.hasFirstOrBusiness(result))) {
							$scope.lieFlatBannerVisible = false;
							return hasLieFlat;
						}
						$scope.lieFlatBannerVisible = false;
						var details = typeof result !== 'undefined' ? result.ActiveTab.TripAndFareDetails : [];
						for (var i = 0; i < details.length; i++) {
							var segs = details[i].TripSlice.Segments;
							for (var j = 0; j < segs.length; j++) {
								if (segs[j].LieFlatAvailable) {
									hasLieFlat = true;
									$scope.lieFlatBannerVisible = true;
									return hasLieFlat;
								}
							}
						}
						return hasLieFlat;
					}
					$scope.hasLieFlatFareGrid = function (result) {
						var hasLieFlat = false;
						$scope.lieFlatBannerVisible = false;
						var details = result;
						for (var i = 0; i < details.length; i++) {
							var segs = details[i].TripSlice.Segments;
							for (var j = 0; j < segs.length; j++) {
								if (segs[j].LieFlatAvailable) {
									hasLieFlat = true;
									$scope.lieFlatBannerVisible = true;
									return hasLieFlat;
								}
							}
						}
						return hasLieFlat;
					};
					$scope.hasLieFlatFromTripSlice = function (slice) {
						var segs = slice.TripSlice.Segments;
						var hasLieFlat = false;
						for (var j = 0; j < segs.length; j++) {
							if (segs[j].LieFlatAvailable) {
								hasLieFlat = true;
								$scope.lieFlatBannerVisible = true;
								return hasLieFlat;
							}
						}
						return hasLieFlat;
					};

					$scope.$on('flightChanged', function ($event, data) {
						$scope.lieFlatAvailable = $scope.hasLieFlatFromTripSlice(data);
						$scope.UpsellGrid = {};
						$scope.UpsellGrid.showUpsellGrid = false;

					});

					// Current segment has lie flat
					$scope.segmentHasLieFlat = function (seg) {
						return seg.TripSlice.Segments.every(function (elem) { return elem.LieFlatAvailable; });
					}


					// Operating Airline is HA and all aircraft assigned to trip has specified seats
					$scope.tripSeatType = function (trip, seatType) {
						return trip.TripSlice.Segments.every(function (seg) {
							if (seg.OperatingAirline != 'HA') return false;
							var info = getAircraftInfoByCode(seg.EquipmentType);
							return info ? info.FCSeatType == seatType : false;

						});
					}

					var getAircraftInfoByCode = function (code) {
						for (var i = 0, len = $scope.planesConfiguration.length; i < len; i++) {
							var item = $scope.planesConfiguration[i];
							if ($.inArray(code, item.codes) > -1) {
								return item;
							}
						}
						return false;
					};

					$scope.getAircraftMarketingInfo = function (code) {
						for (var i = 0, len = $scope.PlanesMarketingData.length; i < len; i++) {
							var item = $scope.PlanesMarketingData[i];
							if ($.inArray(code, item.EquipmentCodes) > -1) {
								return item;
							}
						}
						return false;
					};

					$scope.getAirplaneName = function (code) {
						var data = getAircraftInfoByCode(code)
						if (data.name) {
							return data.name
						}
						else {
							return '';
						}
					};

					// Any First or Business
					$scope.hasFirstOrBusiness = function (result) {
						if (!result.ActiveTab) {
							return false;
						}
						var details = result.ActiveTab.TripAndFareDetails;
						for (var i = 0; i < details.length; i++) {
							var dets = details[i].FareDetails;
							for (var j = 0; j < dets.length; j++) {
								if (dets[j].CabinType === 'FIRST' || dets[j].CabinType === 'BUSINESS') {
									return true;
								}
							}
						}
						return false;
					};

					// any EC or Preferred
					$scope.hasECPreferred = function (result) {
						var hasECPreferred = false;
						if (typeof result !== 'undefined' && !result.ActiveTab) {
							return hasECPreferred;
						}

						var details = typeof result !== 'undefined' ? result.ActiveTab.TripAndFareDetails : [];
						for (var i = 0; i < details.length; i++) {
							var dets = details[i].FareDetails;
							for (var j = 0; j < dets.length; j++) {
								var offerType = dets[j].CabinType.toLowerCase();
								if (offerType === 'extracomfort' || offerType === 'preferred') {
									hasECPreferred = true;
									document.body.dispatchEvent(new CustomEvent('UpgradeSeatOffered', {
										'detail': {
											'seatType': offerType,
											'pageName': $rootScope.dtmVals.pageName,
											'pageURL': $rootScope.dtmVals.pageURL
										}
									}));
									return hasECPreferred;
								}
							}
						}
						return hasECPreferred;
					};

					$scope.resultHasECPreferred = function(result) {
						var dets = result.FareDetails;
						for (var i = 0; i < dets.length; i++) {
							var offerType = dets[i].CabinType.toLowerCase();
							if (offerType === 'extracomfort' || offerType === 'preferred') {
								return offerType;
							}
						}
						return false;
					};

					$scope.showECPSBadge = function(result) {
						var dets = result.FareDetails;
						for (var i = 0; i < dets.length; i++) {
							var offerType = dets[i].CabinType.toLowerCase();
							if (offerType === 'extracomfort' || offerType === 'preferred') {
								return dets[i].IsAvailable;
							}
						}
						return false;
					};

					$scope.hasMCBOferred = function (result) {
						var hasMCBOferred = false;
						if (typeof result !== 'undefined' && !result.ActiveTab) {
							return hasMCBOferred;
						}

						var details = typeof result !== 'undefined' ? result.ActiveTab.TripAndFareDetails : [];
						for (var i = 0; i < details.length; i++) {
							var dets = details[i].FareDetails;
							for (var j = 0; j < dets.length; j++) {
								var offerType = dets[j].CabinType.toLowerCase();
								if (offerType === 'maincabinbasic') {
									document.body.dispatchEvent(new CustomEvent('MainCabinBasicOffered', {
										'detail': {
											'pageName': $rootScope.dtmVals.pageName,
											'pageURL': $rootScope.dtmVals.pageURL
										}
									}));
									return hasMCBOferred;
								}
							}
						}
						return hasMCBOferred;
					};


					// Price Chart Variables and Logic
					$scope.enablepricechart = $scope.$switch('LowFare:enablepricechart');
					$scope.enablepricecalendar = $scope.$switch('LowFare:enablepricecalendar');
					// Check if the duration is valid for Low Fare chart
					function validateLowFareDuration() {
						if ($scope.TripType === 2 && $scope.searchRequest.FlightSearchSegmentList[0].DepartureDate && $scope.searchRequest.FlightSearchSegmentList[1].DepartureDate) {
							var departDateRaw = $scope.searchRequest.FlightSearchSegmentList[0].DepartureDate;
							var departDate = new Date(parseInt(departDateRaw.match(/\d+/)[0], 10));
							var returnDateRaw = $scope.searchRequest.FlightSearchSegmentList[1].DepartureDate;
							var returnDate = new Date(parseInt(returnDateRaw.match(/\d+/)[0], 10));
							$scope.isValidLowFareDuration = (haDateUtils.numDaysDifference(departDate, returnDate) <= 60);
						} else {
							$scope.isValidLowFareDuration = true;
						}
					}
					validateLowFareDuration();

					function getLeg(i, depart) {
						var origin = {
							code: $scope.searchResults[0].OriginCityCode,
							display: $scope.searchResults[0].OriginCityName
						}
						var destination = {
							code: $scope.searchResults[0].DestinationCityCode,
							display: $scope.searchResults[0].DestinationCityName
						}
						// for the return leg on roundtrips, flip the origin/dest
						return {
							origin: (i === 0 && origin) || destination,
							destination: (i === 0 && destination) || origin,
							departDate: depart
						};
					}
					function getLegs() {
						var departDateRaw = $scope.searchRequest.FlightSearchSegmentList[0].DepartureDate;
						var departDate = parseInt(departDateRaw.match(/\d+/)[0], 10);
						departDate = new Date(moment(departDate).utc().format("MM/DD/YYYY"));
						var legs = [getLeg(0, departDate)];
						if ($scope.TripType === 2) {
							var returnDateRaw = $scope.searchRequest.FlightSearchSegmentList[1].DepartureDate;
							var returnDate = parseInt(returnDateRaw.match(/\d+/)[0], 10);
							returnDate = new Date(moment(returnDate).utc().format("MM/DD/YYYY"));
							legs.push(getLeg(1, returnDate));
						}

						return legs;
					}
					// Open price chart modal
					$scope.openPriceChart = function (display) {
						display = display || 'calendar';
						haModal(haConfig.getTemplateUrl('/Book/FlightSearch/ha-flexible-price-modal.html'), {
							id: 'FlexiblePriceView',
							backdrop: 'true',
							extendScope: {
								legs: getLegs(),
								adults: $scope.searchRequest.AdultCount,
								children: $scope.searchRequest.ChildCount,
								display: display
							}
						});
					};

					// Open Mixed cabin modal
					$scope.openMobileMixedCabin = function (cabinDetails) {
						haModal(haConfig.getTemplateUrl('Book/FlightResults/ha-mixed-cabin-modal.html'), {
							backdrop: 'true',
							id: 'MixedCabinModal',
							extendScope: {
								legsWithCabinDetails: cabinDetails,
								getCabinDisplayName: $scope.getCabinDisplayName
							}
						});
					};

					// cross check the result matches the expected search request
					$scope.crossCheckSearch = function (results, checkMiles) {
						var isValid = true;

						// check the origin and destination returned in the result matches the search request
						if ($scope.searchRequest.FlightSearchSegmentList.length >= results.TripId) {
							var checkSegment = $scope.searchRequest.FlightSearchSegmentList[results.TripId - 1];
							if (checkSegment.OriginCityCode !== results.OriginCityCode || checkSegment.DestinationCityCode !== results.DestinationCityCode) {
								isValid = false;
							}
						} else {
							isValid = false;
						}

						// check the booking type in the result matches the search request
						if (isValid && checkMiles) {
							switch (results.BookingType) {
								case 0:
									if ($scope.searchRequest.FlightSearchSegmentList.filter(function (segment) {
										return segment.IsMiles;
									}).length > 0) {
										isValid = false;
									}
									break;
								case 1:
									if ($scope.searchRequest.FlightQueryTypeId === 0 || $scope.searchRequest.FlightSearchSegmentList.filter(function (segment) {
										return !segment.IsMiles;
									}).length > 0) {
										isValid = false;
									}
									break;
								case 2:
									if ($scope.searchRequest.FlightQueryTypeId !== 2 || $scope.searchRequest.FlightSearchSegmentList.length !== 2 || $scope.searchRequest.FlightSearchSegmentList[0].IsMiles || !$scope.searchRequest.FlightSearchSegmentList[1].IsMiles) {
										isValid = false;
									}
									break;
								case 3:

									if ($scope.searchRequest.FlightQueryTypeId !== 2 || $scope.searchRequest.FlightSearchSegmentList.length !== 2 || !$scope.searchRequest.FlightSearchSegmentList[0].IsMiles || $scope.searchRequest.FlightSearchSegmentList[1].IsMiles) {
										isValid = false;
									}
									break;
								default:
									isValid = false;
							}
						}

						if (!isValid) {
							window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
						}

						return isValid;
					};

					$scope.hideFareTooltip = function () {
						$scope.showFareTooltip = false;
						haUtils.localStorageSet('hideFareTooltip', true);
					};

					$scope.showFareModal = function () {
						haModal(haConfig.getTemplateUrl('fare-help-modal.html'), {
							id: 'fare-help-modal',
							backdrop: 'true',
							scope: $scope
						});
					};

					$scope.closeBanner = function (event) {
						event.stopPropagation();
						$scope.lieFlatBannerVisible = false;
					};

					//social proof initialization
					$scope.socialProofDisplay = function (departDate, origin, destination,flightResultCount) {
						var socialProofShow = function () {
							$scs.get('SocialProofFlightViewSettings').then(function (result) {
								angular.element('#social-proof-container').css({
									'animation-duration': result.duration,
									'animation-delay': result.delay,
									'animation-speed': result.animationspeed
								});
								angular.element('#social-proof-container').addClass('visible');
							});
						};

						var socialProofHide = function () {
							angular.element('#social-proof-container').removeClass('visible');
						};

						$scope.closeSp = function () {
							socialProofHide();
						};

						//social proof get data request
						haFlightResultsEndOnEndAPI.getSocialProofData(origin, destination, departDate, flightResultCount).success(function (results) {
							if (results.ErrorMessage != null && results.ErrorMessage.length > 1) {
								$scope.spData = {
									visitorNo: 0,
									displayEligible: false
								};
							}
							else {
								$scope.spData = {
									visitorNo: results.FlightSearchCount,
									displayEligible: results.DisplaySocialProofSlider
								};

								//social proof dynamic value replace
		
								$scs.get('SocialProofFlightViewSettings').then(function (result) {
   								    $scope.visitor = '<b>' + $scope.spData.visitorNo + '</b>';
									$scope.spData.displayText = result.socialprooftext.replace('[Number value]', $scope.visitor).replace('[Destination]', $scope.searchResults[$scope.currentSegmentId - 1].DestinationCityName.split(',')[0]);
									$scope.spData.displayText = $scope.spData.displayText + '.';
								})

								//social proof visibility functionality
								$timeout(socialProofShow, 3000);
								if ($rootScope.isMobile) {
									$timeout(socialProofHide, 7000);
								}
								else {
									$timeout(socialProofHide, 13000);
								}
							}
						});
					};


					$scope.initialSearch = function (results) {

						var $interstitial = $('#FlightResultsInterstitial');
						var animateEndFunc = function () {
							// remove inline overflow:hidden; that was present during interstitial
							angular.element('body').removeAttr('style');
							$interstitial.addClass('ng-hide');
						};

						$interstitial.addClass('done');
						window.scrollTo(0, 0);
						$scope.socialProofDisplay(results.DepartureDate, results.OriginCityCode, results.DestinationCityCode, results.ActiveTab.TripAndFareDetails.length);

						$timeout(function () {
							$interstitial.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', animateEndFunc).addClass('animated').addClass('bounceOutUp');
							if ($(document.documentElement).hasClass('no-csstransforms3d')) {
								animateEndFunc();
							}
							$scope.model.loadingSegment = false;
							// fix for firefox not removing loading class
							$timeout(function () {
								angular.element('#segment-1').removeClass('loading');
							}, 0);
						}, interstitialPullUpDelay);
						$scope.resultsLoaded = true;

						//Fire custom event to clear MCB eVars 176 and 177
						document.body.dispatchEvent(new CustomEvent('UpgradeToMainCabin', {
							'detail': {
								'pageName': $rootScope.dtmVals.pageName,
								'clearData': "true"
							}
						}));

						$scope.lieFlatAvailable = $scope.hasLieFlat(results);
						$scope.hasECPreferred(results);
						$scope.hasMCBOferred(results);

						// Check if child fare feature switch is enabled
						$scope.enableChildFare = $scope.$switch('FlightResult:enablechildfarelabels');

						// Check if there are child pax
						angular.forEach($scope.$pax.passengers, function (p) {
							if (p.type == 'Child') {
								$scope.hasChildPax = true;
							}
						});

						if (results.ActiveTab !== undefined && results.AlternateTabs !== undefined && results.TripId !== undefined) {
							$scope.currentSegmentId = results.TripId;
							$scope.currency = results.CurrencyCode;
							$scope.searchResults[results.TripId - 1] = results; // store the flight search result
							$scope.resetFlightResultsLimit();
							$scope.searchRequest.IsRefundable = results.IsRefundableFare; // update IsRefundableFare flag in the searchRequest
							$scope.Message = results.Message; // update alert message
							$scope.SignifiedMarket = results.MarketID; // update market id

							// update miles option dropdown
							// change to 0 in case if it's higher than 3, it means it's Reshop, etc.
							if (results.BookingType !== undefined && results.BookingType <= 3) {
								$scope.priceTypeInfo.priceType = results.BookingType;
							} else {
								$scope.priceTypeInfo.priceType = 0;
							}

							// set the priceTypeSelection, but cap at 2 since there are only 3 radios
							if ($scope.priceTypeInfo.priceType === 3) {
								$scope.priceTypeInfo.priceTypeSelection = 2;
							} else {
								$scope.priceTypeInfo.priceTypeSelection = $scope.priceTypeInfo.priceType;
							}
							$scope.priceTypeInfo.priceTypeMilesDollarsSelection = '';

							// set the priceTypeMilesDollarsSelection if Miles/Dollars or Dollars/Miles
							if ($scope.priceTypeInfo.priceType > 1) {
								$scope.priceTypeInfo.priceTypeMilesDollarsSelection = $scope.priceTypeInfo.priceType;
							}

							$scope.arrowUpdate();
							$scope.updatePromo(results.Discounts); // update promo list in sticky booking widget and title for tooltip
							$scope.updateSortDropdown(); // update sort dropdown
						}

						if ($rootScope.isMobile) {
							$scope.getLowestFares(results);
						}
					};

					//loading planeconfiguration
					$scope.planesConfiguration = [];
					haFlightResultsEndOnEndAPI.fetchPlaneConfiguration().success(function (results) {
						$scope.planesConfiguration = results;
					}).error(function () {
						$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-planeConfiguration');
					});

					//loading plane fetchPlaneMarketingData
					$scope.PlanesMarketingData = [];

					// initial flight search
					haFlightResultsEndOnEndAPI.fetch().success(function (results) {
						if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-fetch')) {
							return;
						}

						$scope.initialSearch(results);

						$scope.$emit('dtmPage', {
							pageName: $rootScope.dtmVals.pageName,
							pageURL: $rootScope.dtmVals.pageURL,
							lowestFare: $scope.searchResults[0].AlternateTabs[3].DisplayAmount || '0'
						});

						//Load data into uplift
						$(document).ready(function () {
							setTimeout(function () {
								haUplift.loadUplift(results, $scope.TripType );
								upliftPageLoaded = true;
							}, $rootScope.UpTimeout);
						});
						

					})
					.error(function () {
						$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-fetch');
						$scope.resultsLoaded = true;
					});				

					var upliftPageLoaded = false;

					//add monthly offer for  addidtional rows of flights 
					$scope.$watch('model.flightResultsLimit', function (nv) {
						console.log('result limit', nv);
						$timeout(function () {
							
							if (upliftPageLoaded) {
								haUplift.refreshUplift();
							}
						});
					});
					//show monthly offer when trip summary is updated
					$scope.$watch('PassengerTripSummary', function (nv) {
						$timeout(function () {
							
							if (upliftPageLoaded) {
								haUplift.refreshUplift();
							}
						}, $rootScope.UpTimeout);
					});
					//show monthly offer for sub-total per segment page
					window.onhashchange = function () {
						$timeout(function () {
							
							if (upliftPageLoaded) {
								haUplift.refreshUplift();
							}
						});
					}

					$scope.$watch('priceTypeInfo.priceTypeSelection', function (nv, ov) {
						nv = parseInt(nv);
						if (isNaN(nv) || nv === parseInt(ov)) {
							return;
						}
						if (nv <= 2) {
							$scope.priceTypeInfo.priceType = nv;
						}
					});

					$scope.$watch('priceTypeInfo.priceTypeMilesDollarsSelection', function (nv, ov) {
						nv = parseInt(nv);
						if (isNaN(nv) || nv === parseInt(ov)) {
							return;
						}
						$scope.priceTypeInfo.priceType = nv;
					});
					// Miles dropdown
					$scope.$watch('priceTypeInfo.priceType', function (nv, ov) {
						if (ov !== undefined && ov !== nv && nv !== undefined) {
							// Reset selection when option is changed
							$scope.selectClass.removeClass('active'); // hide up sell grid
							$scope.continueBarActive = false; // hide continue button
							$scope.resetPage(); // reset data

							// focus on loading spinner
							$timeout(function () {
								angular.element('.ha-loading-spinner').focus();
							}, 0);

							// update searchRequest
							switch (nv) {
								case 0:
									angular.forEach($scope.searchRequest.FlightSearchSegmentList, function (segment) {
										segment.IsMiles = false;
									});
									break;
								case 1:
									angular.forEach($scope.searchRequest.FlightSearchSegmentList, function (segment) {
										segment.IsMiles = true;
									});
									break;
								case 2:
									if ($scope.searchRequest.FlightSearchSegmentList.length === 2) {
										$scope.searchRequest.FlightSearchSegmentList[0].IsMiles = false;
										$scope.searchRequest.FlightSearchSegmentList[1].IsMiles = true;
									}
									break;
								case 3:
									if ($scope.searchRequest.FlightSearchSegmentList.length === 2) {
										$scope.searchRequest.FlightSearchSegmentList[0].IsMiles = true;
										$scope.searchRequest.FlightSearchSegmentList[1].IsMiles = false;
									}
									break;
							}

							$state.go('segment.x', { id: 1 }, { notify: false }); // back to initial state

							haFlightResultsEndOnEndAPI.changeMilesOption(nv).success(function (results) {
								if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-changeMilesOption')) {
									return;
								}
								// cross check the result and search request
								if ($scope.crossCheckSearch(results, false)) {
									$scope.initialSearch(results);
								}
							})
								.error(function () {
									$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-changeMilesOption');
								});

						}
					});

					$scope.$watch('model.loadingSegment', function (nv) {
						// height and listner setup after each loading finish
						if (!nv && $scope.currentSegmentId <= $scope.TripCount) {
							$timeout(function () {
								// for displaying Discount Tooltip
								$('.discount-applied').bind('mouseover', function (event) {
									// append only if it's not already appended
									if ($(event.target).children('#discountTooltip').length === 0) {
										$(event.target).parent().append(angular.element('#discountTooltip'));
									}
									angular.element('#discountTooltip').removeClass('ng-hide');

									// attach event listner to hide on mouse leave
									$(event.target).mouseleave(function () {
										$('#discountTooltip').addClass('ng-hide');
										$('body').append(angular.element('#discountTooltip'));
									});
								});

								//show monhtly offer after loading segment
								console.log('refreshing from loading');
								$timeout(function () {
									if (upliftPageLoaded) {
										haUplift.refreshUplift();
									}
								}, 5000);								
							}, 10);
						}
					});

					// Helper method to update pax count in pax service
					$scope.AddPassengerList = function (count, type) {
						for (var i = 0; i < count; i++) {
							$scope.$pax.add({
								type: type,
								isUser: true
							});
						}
					};

					// Update the pax count in pax service
					$scope.UpdatePassengerList = function () {
						if ($scope.searchRequest.AdultCount > 0) {
							$scope.AddPassengerList($scope.searchRequest.AdultCount, 'Adult');
						}
						if ($scope.searchRequest.ChildCount > 0) {
							$scope.AddPassengerList($scope.searchRequest.ChildCount, 'Child');
						}
						if ($scope.searchRequest.InfantCount > 0) {
							$scope.AddPassengerList($scope.searchRequest.InfantCount, 'Infant');
						}
					};

					$scope.UpdatePassengerList();

					$scope.$on('applyPromo', function ($event, offerId) {
						$scope.selectPromo(offerId);
					});

					$scope.$on('removePromo', function () {
						$scope.removePromo();
					});


					$scope.getCurrencySymbol = function () {
						return (0).toLocaleString(
							'en-US',
							{
								style: 'currency',
								currency: $scope.currency,
								minimumFractionDigits: 0,
								maximumFractionDigits: 0
							}
						).replace(/\d/g, '').trim()
					}

					// Select promo
					$scope.selectPromo = function (offerId) {
						$scope.selectClass.removeClass('active'); // hide up sell grid
						$scope.continueBarActive = false; // hide continue button
						$scope.resetPage(); // reset data

						$state.go('segment.x', { id: 1 }, { notify: false }); // back to initial state

						haFlightResultsEndOnEndAPI.selectPromo(offerId).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-selectPromo')) {
								return;
							}
							$scope.initialSearch(results);

						}).error(function () {
							$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-selectPromo');
						});
					};

					$scope.updatePromo = function (promos) {
						$scope.$broadcast('updatePromoList', promos); // update promo list in sticky booking widget
						angular.forEach(promos, function (promo) {
							$scope.selectedPromoTitle = undefined;
							if (promo.Active) {
								$scope.selectedPromo = promo;
							}
						});
					};

					// Remove promo
					$scope.removePromo = function (skipGo) {
						$scope.selectClass.removeClass('active'); // hide up sell grid
						$scope.continueBarActive = false; // hide continue button
						$scope.resetPage(); // reset data

						if (!skipGo) {
							$state.go('segment.x', { id: 1 }, { notify: false }); // back to initial state
						}

						haFlightResultsEndOnEndAPI.removePromo().success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-removePromo')) {
								return;
							}
							$scope.initialSearch(results);

						}).error(function () {
							$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-removePromo');
						});
					};

					// clicked on a date tab
					$scope.selectTab = function (tab, tripId) {
						var m = tab.TabDate.split(/[^0-9]/);
						var newTabDate = new Date(m[0], m[1] - 1, m[2], m[3], m[4], m[5]);

						if (!tab.IsSoldOut && !tab.IsNoFlights) {

							// check if the date is allowed
							if (!$scope.handleDateCheckAndError(newTabDate, tripId)) {
								return false;
							}

							$scope.model.loadingSegment = true;
							angular.element('#segment-' + tripId).addClass('loading');

							// focus on loading spinner
							$timeout(function () {
								angular.element('.ha-loading-spinner').focus();
							}, 0);

							// Get new search result for the selected date
							haFlightResultsEndOnEndAPI.fetchTab($scope.getJourneyData(), tripId, tab.TabDate).success(function (results) {
								$scope.model.loadingSegment = false;
								if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-selectTab')) {
									return;
								}

								$scope.lieFlatAvailable = $scope.hasLieFlat(results);
								$scope.hasECPreferred(results);
								$scope.hasMCBOferred(results);

								// cross check the result and search request
								if ($scope.crossCheckSearch(results, true)) {
									// Slice off everything after this one
									$scope.searchResults = $scope.searchResults.slice(0, results.TripId);
									$scope.resetFlightResultsLimit();
									// replace the search result for the selected trip
									angular.element('#segment-' + tripId).removeClass('loading');
									angular.element('#segment-' + tripId).addClass('current');
									$.extend($scope.searchResults[tripId - 1], results);
									$scope.Message = results.Message; // update alert message
									$scope.updatePromo(results.Discounts); // update promo list in sticky booking widget and title for tooltip
									$scope.socialProofDisplay(results.DepartureDate, results.OriginCityCode, results.DestinationCityCode, results.ActiveTab.TripAndFareDetails.length);


									$scope.arrowUpdate();

									// update the dates in the booking widget if it is visible
									var bookingWidgetScope = angular.element('[booking-widget]').scope();
									if (bookingWidgetScope !== undefined) {
										bookingWidgetScope.updateLegDates(2);
										bookingWidgetScope.Initialize();
									}

									// update the dates in the sticky progress bar
									if (!$scope.IsChangeFlightBooking) {
										var flightResultsStickProgressBarScope = angular.element('#flightResultsStickProgressBar').scope();
										flightResultsStickProgressBarScope.UpdateBar(tripId);
									}
									else {
										var flightResultsStickProgressBarReshopScope = angular.element('#flightResultsStickProgressBarReshop').scope();
										flightResultsStickProgressBarReshopScope.UpdateBar(tripId);
									}

									// focus on selected tab
									$timeout(function () {
										$('#leg-' + tripId + '-tab-3').focus();
									}, 0);

									$scope.$emit('dtmPage', {
										pageName: $rootScope.dtmVals.pageName,
										pageURL: $rootScope.dtmVals.pageURL,
										lowestFare: $scope.searchResults[0].AlternateTabs[3].DisplayAmount || '0'
									});
								}
								if ($rootScope.isMobile) {
									$scope.getLowestFares(results);
								}

							}).error(function () {
								$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-selectTab');
							});

						}
					};

					//if (document.referrer.indexOf('deals-and-offers') != -1) {
					//	$scope.TripType = 2;
					//}

					// Open/Close flight search edit
					$scope.openFlightSearchEdit = function () {
						$scope.isFlightSearchEditModal = true;

						haModal(haConfig.getTemplateUrl('ha-book-flight-search-edit-modal.html'), {
							id: 'fare-help-modal',
							backdrop: 'false',
							scope: $scope,
							modalLock: true
						});
					};

					// clicked on arrows by the date tabs
					$scope.SearchBySeven = function (tripId, direction, tabDate) {
						var m = tabDate.split(/[^0-9]/);
						var newTabDate = new Date(m[0], m[1] - 1, m[2], m[3], m[4], m[5]);

						if (direction === 1) { // forward arrow click
							newTabDate.setDate(newTabDate.getDate() + 7);

						} else if (direction === -1) { // back arrow click
							newTabDate.setDate(newTabDate.getDate() - 7);
						}

						// check if the date is allowed
						if (!$scope.handleDateCheckAndError(newTabDate, tripId)) {
							return false;
						}

						// show loading animation
						$scope.model.loadingSegment = true;
						angular.element('#segment-' + tripId).addClass('loading');

						// focus on loading spinner
						$timeout(function () {
							angular.element('.ha-loading-spinner').focus();
						}, 0);

						haFlightResultsEndOnEndAPI.fetchTab($scope.getJourneyData(), tripId, direction).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-SearchBySeven')) {
								return;
							}

							// cross check the result and search request
							if ($scope.crossCheckSearch(results, true)) {

								$scope.model.loadingSegment = false;

								// Slice off everything after this one
								$scope.searchResults = $scope.searchResults.slice(0, results.TripId);
								$scope.resetFlightResultsLimit();
								// replace the search result for the selected trip
								angular.element('#segment-' + tripId).removeClass('loading');
								$('#segment-' + tripId).addClass('current');
								$.extend($scope.searchResults[tripId - 1], results);
								$scope.Message = results.Message; // update alert message
								$scope.updatePromo(results.Discounts); // update promo list in sticky booking widget and title for tooltip
								$scope.socialProofDisplay(results.DepartureDate, results.OriginCityCode, results.DestinationCityCode, results.ActiveTab.TripAndFareDetails.length);

								// update the dates in the sticky progress bar
								if (!$scope.IsChangeFlightBooking) {
									var flightResultsStickProgressBarScope = angular.element('#flightResultsStickProgressBar').scope();
									flightResultsStickProgressBarScope.UpdateBar(tripId);
								}
								else {
									var flightResultsStickProgressBarReshopScope = angular.element('#flightResultsStickProgressBarReshop').scope();
									flightResultsStickProgressBarReshopScope.UpdateBar(tripId);
								}

								// focus on selected tab
								$timeout(function () {
									$('#leg-' + tripId + '-tab-3').focus();
								}, 0);

								$scope.$emit('dtmPage', {
									pageName: $rootScope.dtmVals.pageName,
									pageURL: $rootScope.dtmVals.pageURL,
									lowestFare: $scope.searchResults[0].AlternateTabs[3].DisplayAmount || '0'
								});
							}

						}).error(function () {
							$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-SearchBySeven');
							$scope.model.loadingSegment = false;
							$('#segment-' + tripId).addClass('current');
						});
					};

					// clicked on Continue button
					$scope.AddFlightsToTrip = function (e, isHold) {
						if (e !== undefined) {
							e.preventDefault();
						}

						if (!angular.isUndefined(isHold) && isHold) {
							var selectedHoldFare = $rootScope.selectedHoldFare;

							var holdReservationsPrice = {};
							holdReservationsPrice.UniquePriceID = selectedHoldFare.id;
							holdReservationsPrice.TimeLimitDays = selectedHoldFare.numberOfDays;
							holdReservationsPrice.HoldFee = selectedHoldFare.price;
							holdReservationsPrice.MSRCode = selectedHoldFare.msrCode;
							haFlightResultsEndOnEndAPI.updateSelectedHoldFare(holdReservationsPrice).success(function (result) {
							}).error(function () {
								console.error('Error in holding price');
							});
						}

						// Check for Previous date selection before adding flights //
						var selectedFlightsObj = $scope.TripSummary.Trips;

						// Check to see if MCB is upgraded to Main Cabin, fire a custom DTM event
						if (selectedFlightsObj.length > 0 && selectedFlightsObj[0].selectedSeatClassInitial === "MAINCABINBASIC" && (selectedFlightsObj[0].selectedSeatClass === "COACH" || selectedFlightsObj[0].selectedSeatClass === "EXTRACOMFORT" || selectedFlightsObj[0].selectedSeatClass === "PREFERRED")) {
							document.body.dispatchEvent(new CustomEvent('UpgradeToMainCabin', {
								'detail': {
									'pageName': $rootScope.dtmVals.pageName,
									'pageURL': $rootScope.dtmVals.pageURL
								}
							}));
						}

						var returnResult = 'true';
						for (var i = 0; i < selectedFlightsObj.length - 1; i++) {
							var noOfLegs = selectedFlightsObj[i].TripSlice.Segments.length;
							if (selectedFlightsObj[i + 1].TripSlice.Segments[0].DepartureDateTime < selectedFlightsObj[i].TripSlice.Segments[noOfLegs - 1].ArrivalDateTime) {
								var Invalidflight1 = i + 1;
								var Invalidflight2 = i + 1 + 1;
								returnResult = 'false-' + Invalidflight1 + '/' + Invalidflight2;
								break;
							}
						}

						// parse result and show error popup
						if (returnResult.indexOf('false') !== -1) {
							var returnResultArray = returnResult.split('-');
							var invalidSelectionsArray = returnResultArray[returnResultArray.length - 1].split('/');
							var dynamicContent = invalidSelectionsArray;
							$scope.populatePrevDateValidationPopup(dynamicContent);
							var content = $('#PrevDateValReturnToSearchPopup').html();
							haModal({
								id: 'ReturnToFlightSearchPopupModal',
								backdrop: 'true',
								template: content
							});
							return false;
						}

						var selection = {};
						var source = '';
						selection.FareDetails = [];
						angular.forEach($scope.TripSummary.Trips, function (flight) {
							var fareDetail = {};
							fareDetail.id = flight.TripId;
							fareDetail.FlightRefKey = flight.UniqueReferenceKey;
							fareDetail.IsMileagePricing = false;
							fareDetail.SelectedClass = flight.selectedSeatClass;
							selection.FareDetails.push(fareDetail);

							if (source !== '') {
								source += '+';
							}
							source += flight.selectedSeatClass.toUpperCase();
						});
						selection.SelectedFareGrid = source;

						//- Call Waiting spin
						$scope.waitingSegment = true;

						//Initiate intertitial
						haInterstitialAPI.getAirAvailability().success(function (data) {
							haModal({
								id: 'InterstitialAirAvailability-modal',
								backdrop: 'true',
								template: data,
								modalLock: true
							});
							$scope.interstitialDynamicResize();

							//Air Availability Check
							haFlightResultsEndOnEndAPI.checkFlightAvailability(selection).success(function (result) {

								if ($scope.handleExceptions(result, 'haFlightResultsEndOnEndAPI-checkFlightAvailability')) {
									return;
								}

								$scope.waitingSegment = false;

								if (result.status === 1) {
									$scope.showFightsNoSeatsAvailableModal();
								} else if (result.status === 2) {
									$scope.showFightsScheduleMismatchModal();
								} else if (result.status === 4) {
									$scope.showFightsDataAccessDownModal();
								} else if (result.status === 0) {
								    //$('#FlightSearchForm').submit();
								    haFlightResultsEndOnEndAPI.getRedirectionUrl().success(function (result) {
								        var data = result;
								        if (typeof data === "string" && data !== "") {
								            var redirectUrl = data;
								            window.location.href = redirectUrl;
								        }
								        else {
								            $("#FlightSearchForm").submit();
								        }
								    });
								}
							}).error(function () {
								$scope.handleExceptions(data, 'haFlightResultsEndOnEndAPI-checkFlightAvailability');
								$scope.waitingSegment = false;
							});
						});
					};

					// DateValidationPopup
					// This occurs if the 7 day tab slider is advanced past the return date or vice versa
					$scope.showDateValidationModal = function () {

						haModal(haConfig.getTemplateUrl('book/flightresults/ha-flightresults-date-validation-modal.html'), {
							backdrop: 'true',
							id: 'FutureDateValidationPopupModal',
							scope: $scope
						});
					};

					// 331DaysValidationPopup
					// This occurs if the 7 day tab slider is advanced past the 331 day range
					$scope.show331DaysValidationModal = function () {

						haModal(haConfig.getTemplateUrl('book/flightresults/ha-flightresults-331-days-validation-modal.html'), {
							backdrop: 'true',
							id: 'results-331Days',
							scope: $scope
						});
					};

					// LessThanCurrentDateValidationPopup
					// This occurs if the 7 day tab slider is advanced prior to the current date
					$scope.showLessThanCurrentDateModal = function () {

						haModal(haConfig.getTemplateUrl('book/flightresults/ha-flightresults-less-than-current-date-modal.html'), {
							backdrop: 'true',
							id: 'results-DateValidationLessDate',
							scope: $scope
						});
					};

					// Helper method to check if the new date is allowed
					$scope.handleDateCheckAndError = function (newDate, tripId) {
						var content;

						if ($scope.today === undefined) {
							// get today
							if (typeof serverShortDate !== 'undefined') { // user server date if provided
								var m = serverShortDate.split(/[^0-9]/);
								$scope.today = new Date(m[2], m[0] - 1, m[1], 0, 0, 0);
							} else { // if not use local date
								$scope.today = new Date();
								$scope.today.setHours(0, 0, 0, 0);
							}
						}

						if ($scope.maxDateAllowed === undefined) {
							// get max date allowed in the 331 days range
							$scope.maxDateAllowed = new Date($scope.today.getTime());
							$scope.maxDateAllowed.setDate($scope.maxDateAllowed.getDate() + 330);
						}


						// Error if selected date is prior to current date
						if (Date.parse(newDate) < Date.parse($scope.today)) {
							$scope.showLessThanCurrentDateModal();
							return false;
						}

						var testDate;

						// Error if selected date is before the selected date of the previous trip
						// check against search result date if available, if not, check against search request date
						if ($scope.searchResults[tripId - 2]) {
							testDate = new Date($scope.searchResults[tripId - 2].DepartureDate);
						} else if ($scope.searchRequest.FlightSearchSegmentList[tripId - 2]) {
							testDate = new Date(Number($scope.searchRequest.FlightSearchSegmentList[tripId - 2].DepartureDate.replace(/[^0-9]/g, '')));
						} else {
							testDate = false;
						}

						if (testDate && Date.parse(newDate) < testDate) {

							$scope.showDateValidationModal();
							return false;
						}

						// Error if selected date is after the selected date of the next trip
						// check against search result date if available, if not, check against search request date
						if ($scope.searchResults[tripId]) {
							testDate = new Date($scope.searchResults[tripId].DepartureDate);
						} else if ($scope.searchRequest.FlightSearchSegmentList[tripId]) {
							testDate = new Date(Number($scope.searchRequest.FlightSearchSegmentList[tripId].DepartureDate.replace(/[^0-9]/g, '')));
						} else {
							testDate = false;
						}
						if (testDate && Date.parse(newDate) > testDate) {

							$('#ScData_PrevDateValidation_scenario').html($('.futureValidation').html());
							$scope.showDateValidationModal();
							return false;
						}

						// Error if exceeds 331 day maximum allowed
						if (Date.parse(newDate) > Date.parse($scope.maxDateAllowed)) {

							$scope.show331DaysValidationModal();
							return false;
						}

						return true;
					};

					// Toggle the previous and next arrow on and off
					$scope.arrowUpdate = function () {

						$scope.isPreviousAllowed = true;
						$scope.isNextAllowed = true;
					};

					$scope.changeCurrency = function (currency, tripId) {
						var segmentIdx = tripId - 1;
						haFlightResultsEndOnEndAPI.fetchCurrency(currency).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-changeCurrency')) {
								return;
							}
							$scope.Trips[segmentIdx] = results.Trips[segmentIdx];
						});
					};

					// method for sort type lowestPrice
					var priceAmount = function () {
						return function (obj) {
							var displayFare = 0;
							for (var i = 0; i < obj.FareDetails.length; i++) {
								if (obj.FareDetails[i].DisplayAmount) {
									displayFare = parseFloat(obj.FareDetails[i].DisplayAmount);
									break;
								}
							}
							return displayFare;
						};
					};

					// method for sort type lowestMiles
					var milesAmount = function () {
						return function (obj) {
							var displayMiles = 0;
							for (var i = 0; i < obj.FareDetails.length; i++) {
								if (obj.FareDetails[i].DisplayMiles) {
									displayMiles = parseFloat(obj.FareDetails[i].DisplayMiles);
									break;
								}
							}
							return displayMiles;
						};
					};

					// method for sort type earliestDepartureTime
					var getDepartureTime = function () {
						return function (obj) {
							return new Date(obj.TripSlice.Segments[0].DepartureDateTime);
						};
					};

					// method for sort type earliestArrivalTime
					var getArrivalTime = function () {
						return function (obj) {
							return new Date(obj.TripSlice.Segments[obj.TripSlice.Segments.length - 1].ArrivalDateTime);
						};
					};

					// method for sort type shortestDuration
					var getDuration = function (key) {
						return function (obj) {

							var duration = 0;
							//Grabbing the time in days hours and minutes to calculate druation of trip
							var days = obj.TripSlice[key].match(/\d*d/g);
							var hours = obj.TripSlice[key].match(/\d*h/g);
							var mins = obj.TripSlice[key].match(/\d*m/g);

							//If time exists add the duration in minutes
							if (days) {
								duration += parseInt(days[0].match(/\d+/)) * 1440;
							}
							if (hours) {
								duration += parseInt(hours[0].match(/\d+/)) * 60;
							}

							if (mins) {
								duration += parseInt(mins[0].match(/\d+/));
							}

							return duration;
						};
					};

					var sortOptions = [];
					// method for finding lowest fares (in dollars/miles) for each result (for mobile), and determining if there are any discounts applied
					$scope.getLowestFares = function (results) {
						if (results.ActiveTab != null) {
							angular.forEach(results.ActiveTab.TripAndFareDetails, function (result, i) {
								var faresdollars = [];
								var faresmiles = [];
								var hasdiscount = false;

								angular.forEach(result.FareDetails, function (cabin) {
									if (cabin.IsAvailable == true) {
										faresdollars.push(cabin.DisplayAmount);
										faresmiles.push(cabin.DisplayMiles);
									}

									if (cabin.IsDiscount == true) {
										hasdiscount = true;
									}
								});
								faresdollars.sort(function (a, b) { return a - b });
								faresmiles.sort(function (a, b) { return a - b });
								$scope.searchResults[results.TripId - 1].ActiveTab.TripAndFareDetails[i].lowestDollars = faresdollars[0];
								$scope.searchResults[results.TripId - 1].ActiveTab.TripAndFareDetails[i].lowestMiles = faresmiles[0];

								$scope.searchResults[results.TripId - 1].ActiveTab.TripAndFareDetails[i].hasDiscount = hasdiscount;
							});
						}
					};

					// Method to sort for least amount of stops and shortest dur
					var fewestStops = function (stops, duration) {
						return function (obj) {
							//adding a large block of time so that FewestStops is sorted correctly
							var count = obj.TripSlice[stops] * 10000;

							//Grabbing the time in days hours and minutes to calculate druation of trip
							var days = obj.TripSlice[duration].match(/\d*d/g)
							var hours = obj.TripSlice[duration].match(/\d*h/g);
							var mins = obj.TripSlice[duration].match(/\d*m/g);

							//If time exists add the duration in minutes
							if (obj.TripSlice.IsCodeShare) {
								count += 200;
							}

							if (days && obj.TripSlice[stops] > 0) {
								count += parseInt(days[0].match(/\d+/)) * 1440;
							}

							if (hours && obj.TripSlice[stops] > 0) {
								count += parseInt(hours[0].match(/\d+/)) * 60;
							}
							if (mins && obj.TripSlice[stops] > 0) {
								count += parseInt(mins[0].match(/\d+/));
							}

							if (obj.TripSlice[stops] === 0) {
								count = moment(Date(obj.TripSlice.Segments[0].DepartureDateTime)).seconds()
							}

							return count;
						};
					};

					//Method to return default sorting based on Feature Flag
					var defaultSort = function () {
						halaunchDarklyAPI.getFeatureFlag(HA.FLAGS.MetalNeutralShoppingFeatureKey, 'boolean', true, false).then(
							function (response) {
								if (response.data === "ENABLED") // If Metal Neutral Shopping is Enabled, do not do custom sort
								{
									sortKey = 'default';
									return null;
								} else {
									$scope.sorting = { predicateKey: sortKey };
								}
							});

					};




					// Sort options & Dollars/Miles
					$scope.updateSortDropdown = function () {
						var priceType = $scope.priceTypeInfo.priceType;

						$scope.sortOptions = sortOptions.filter(function (o, i) {
							if (i === 1) {
								return priceType !== 1;
							}
							if (i === 2) {
								return priceType !== 0;
							}
							return true;
						});

						$scope.sorting = { predicateKey: sortKey };
					};

					$scs.get('FareSearch').then(function () {
						sortOptions = [
							{ key: $scs('FareSearch.defulttext'), val: 'default' },
							{ key: $scs('FareSearch.lowestpricetext'), val: 'lowestPrice' },//show if priceType !== 1
							{ key: $scs('FareSearch.lowestmilestext'), val: 'lowestMiles' },//show if priceType !== 0
							{ key: $scs('FareSearch.earliestdeparturetimetext'), val: 'earliestDepartureTime' },
							{ key: $scs('FareSearch.earliestarrivaltimetext'), val: 'earliestArrivalTime' },
							{ key: $scs('FareSearch.shortestdurationtext'), val: 'shortestDuration' }
						];
						$scope.updateSortDropdown();

						$scope.milesDollarsDropdown = [
							{ key: $scs('FareSearch.selectonetext'), val: '' },
							{ key: $scs('FareSearch.dollarmilestext'), val: 2 },
							{ key: $scs('FareSearch.milesdollartext'), val: 3 }
						];

						$scope.operatedByText = $scs('FareSearch.operatedbytext');
					});

					// definitions of sort dropdown items
					$scope.predicates = {
						'default': defaultSort(),
						fewestStops: fewestStops('StopsCount', 'Duration'),
						lowestPrice: priceAmount(),
						lowestMiles: milesAmount(),
						earliestDepartureTime: getDepartureTime(),
						earliestArrivalTime: getArrivalTime(),
						shortestDuration: getDuration('Duration')
					};

					// setting for sort
					$scope.reverse = false;


					// toggle tax detail list
					$scope.ToggleTaxDetails = function () {
						$scope.taxDetails = !$scope.taxDetails;
					};

					$scope.showSeatClassModal = function (event, seatclass) {
						event.stopPropagation();

						if (seatclass === 'maincabinbasic') {
							return $scope.mcbRestrictionModal();
						}

						$scope.seatClassModal = {};
						$scope.seatClassModal.currentClass = seatclass;
						if ((seatclass === 'business' | seatclass === 'first') && $scope.lieFlatBannerVisible) {
							seatclass = 'lieflat';
						}

						if ($scope.searchResults[$scope.currentSegmentId - 1]) {
							// use the seatClassModal data from the current segment as it can vary in case of multicity
							$scope.seatClassModal.Data = $scope.searchResults[$scope.currentSegmentId - 1].SeatClassModalData;
						} else {
							// currentSegmentId will be out of bound for up sell grid,
							// use index 0 as only one way and round trip will show the up sell grid
							// and the modal data will not vary by segment
							$scope.seatClassModal.Data = $scope.searchResults[0].SeatClassModalData;
						}

						$scope.seatClassModal.selected = $scope.seatClassModal.Data.filter(function (modal) {
							return (modal.Type === seatclass);
						})[0];

						// no data found
						if (!$scope.seatClassModal.selected) {
							return false;
						}

						haModal(haConfig.getTemplateUrl('Book/FlightResults/ha-flightresults-seat-class-modal-template.html'), {
							id: 'results-seat-class',
							backdrop: 'true',
							scope: $scope
						});

						return true;
					};

					$scope.showSeatClassModalLieFlat = function () {
						// try business first
						var success = $scope.showSeatClassModal('business');
						if (!success) {
							$scope.showSeatClassModal('first');
						}
					}

					$scope.showBaggageFeesModal = function (baggagecontent) {
						haFlightResultsEndOnEndAPI.fetchModalContent(baggagecontent)
							.success(function (data) {
								if ($scope.handleExceptions(data, 'haFlightResultsEndOnEndAPI-showBaggageFeesModal')) {
									return;
								}
								haModal({
									id: 'results-baggageFee',
									backdrop: 'true',
									template: data
								});
							});
					};

					$scope.showModalPopUp = function (ModalName) {
						haFlightResultsEndOnEndAPI.fetchModalContent(ModalName)
							.success(function (data) {
								if ($scope.handleExceptions(data, 'haFlightResultsEndOnEndAPI-showModalPopUp')) {
									return;
								}
								haModal({
									id: 'results-' + ModalName,
									backdrop: 'true',
									template: data
								});
							});
					};

					$scope.showFightsNoSeatsAvailableModal = function () {
						window.location.href = '/book/Error?ErrorType=SeatNotAvailable';
					};

					$scope.showFightsDataAccessDownModal = function () {
						window.location.href = '/book/Error?ErrorType=DataAccessDown';
					};

					$scope.showFightsScheduleMismatchModal = function () {
						haFlightResultsEndOnEndAPI.fetchPopup('ScheduleMismatch').success(function (content) {
							if ($scope.handleExceptions(content, 'haFlightResultsEndOnEndAPI-showFightsScheduleMismatchModal')) {
								return;
							}
							haModal({
								id: 'schedule-missmatch',
								backdrop: 'true',
								template: content,
								modalLock: true,
								cancel: {
									label: 'Close',
									fn: function () {
										window.location.href = '/book/FlightResults';
									}
								}
							});
						});
					};

					var seatmapDebounceFlag = false;
					$scope.showSeatMap = function (segments) {
						// VSP SWITCH
						if (seatmapDebounceFlag) {
							return;
						}
						seatmapDebounceFlag = true;
						haLaunchDarklyService.GetFlagVariantAsBoolean('VariableSeatPricingOnSeatmapEnabled', 'boolean', true).then(function(res) {
							seatmapDebounceFlag = false;
							if (res) {
								haModal(haConfig.getTemplateUrl('VariableSeatmap/ha-variable-seatmap-preview.html'), {
									backdrop: 'true',
									id: 'ha-variable-seatmap-preview-modal',
									extendScope: {
										previewSegments: segments,
										SignifiedMarket: $scope.SignifiedMarket,
										enableTCR: $scope.enableTCR,
										disableSeatUpgrades: $scope.searchResults[0].IsDisableSeatUpgrade
									}
								});
							} else {
								haModal(haConfig.getTemplateUrl('VerticalSeatmap/ha-vertical-seatmap-preview.html'), {
									backdrop: 'true',
									id: 'ha-vertical-seatmap-preview-modal',
									extendScope: {
										previewSegments: segments,
										SignifiedMarket: $scope.SignifiedMarket,
										enableTCR: $scope.enableTCR,
										disableSeatUpgrades: $scope.searchResults[0].IsDisableSeatUpgrade
									}
								});
							}
						});

					};
					$scope.showSeatMapwithFirstClassAvailableCheck = function (segments, isFirstClassAvailable) {
						// VSP SWITCH						
						if (seatmapDebounceFlag) {
							return;
						}
						seatmapDebounceFlag = true;
						haLaunchDarklyService.GetFlagVariantAsBoolean('VariableSeatPricingOnSeatmapEnabled', 'boolean', true).then(function (res) {
							seatmapDebounceFlag = false;
							if (res) {
								haModal(haConfig.getTemplateUrl('VariableSeatmap/ha-variable-seatmap-preview.html'), {
									backdrop: 'true',
									id: 'ha-variable-seatmap-preview-modal',
									extendScope: {
										previewSegments: segments,
										SignifiedMarket: $scope.SignifiedMarket,
										enableTCR: $scope.enableTCR,
										disableSeatUpgrades: $scope.searchResults[0].IsDisableSeatUpgrade,
										disableFirstClass: !isFirstClassAvailable
									}
								});
							} else {
								haModal(haConfig.getTemplateUrl('VerticalSeatmap/ha-vertical-seatmap-preview.html'), {
									backdrop: 'true',
									id: 'ha-vertical-seatmap-preview-modal',
									extendScope: {
										previewSegments: segments,
										SignifiedMarket: $scope.SignifiedMarket,
										enableTCR: $scope.enableTCR,
										disableSeatUpgrades: $scope.searchResults[0].IsDisableSeatUpgrade
									}
								});
							}
						});

					};

					$scope.ErrorBackToHome = function (methodName, ErrorCodeHandle, ErrorMessage) {
						var regex;
						var body;
						var errorMsg;
						if (methodName === 'haFlightResultsEndOnEndAPI-fetch' || methodName === 'haFlightResultsEndOnEndAPI-changeMilesOption') {
							// Remove HTML tags(if found)
							if ($scope.IsChangeFlightBooking) {
								regex = /(<([^>]+)>)/ig;
								body = ErrorCodeHandle + ': ' + ErrorMessage;
								errorMsg = body.replace(regex, '');
								window.location.href = '/book/error?ErrorType=Custom&PathType=ChangeFlight&ErrorCode=' + errorMsg;
							}
							else {
								regex = /(<([^>]+)>)/ig;
								body = ErrorCodeHandle + ': ' + ErrorMessage;
								errorMsg = body.replace(regex, '');
								window.location.href = '/book/home?FltResNull=' + errorMsg;
							}
						}
						else {
							regex = /(<([^>]+)>)/ig;
							body = ErrorCodeHandle + ': ' + ErrorMessage;
							errorMsg = body.replace(regex, '');
							window.location.href = '/book/home?FltResNull=' + errorMsg;
						}
					};

					$scope.handleExceptions = function (result, methodName) {
						if (result !== 'jsError') {
							switch (result.ErrorCodeHandle) {
								case 'BFS0':
									$scope.ErrorBackToHome(methodName, result.ErrorCodeHandle, result.ErrorMessage, result.RedirectURL);
									break;
								case 'BFRT0':
									$scope.ErrorBackToHome(methodName, result.ErrorCodeHandle, result.ErrorMessage, result.RedirectURL);
									break;
								case '20027':
								case '20058':
								case '20059':
									var errorMessage = result.ErrorMessage;
									if (result.ErrorMessage !== null) { errorMessage = result.ErrorMessage.split(':')[0];}
									$scope.ErrorBackToHome(methodName, result.ErrorCodeHandle, errorMessage, result.RedirectURL);
									break;
								case '20055':
									$scs.get('FareSearch').then(function () {
										$scope.ErrorBackToHome(methodName, '20055', $scs('FareSearch.dollarsnoavailability'));
									});
									break;
								case '20066':
									// Transforming 20055 to 20066 now with a sitecore based message
									$scs.get('FareSearch').then(function () {
										$scope.ErrorBackToHome(methodName, '20066', $scs('FareSearch.dollarsmilesnoavailability'));
									});
									break;
								case '20024':
									// Remove HTMl tags(if found)
									var regex = /(<([^>]+)>)/ig;
									$scs.get('FareSearch').then(function () {
										var body = result.ErrorCodeHandle + ': ' + $scs('FareSearch.noflightresultserrormsg');
										var errorMsg = body.replace(regex, '');
										window.location.href = '/book/home?FltResNull=' + errorMsg;
									});
									break;
								case '20044':
									window.location.href = '/book/error?ErrorType=Custom&ErrorCode=' + result.ErrorCodeHandle;
									break;
								case '20045':
									window.location.href = '/book/error?ErrorType=Custom&ErrorCode=' + result.ErrorCodeHandle;
									break;
								case 'SessionTimeOut':
									window.location.href = '/book/error?ErrorType=SessionTimeOut';
									break;
								default:
									if (result.ErrorCodeHandle !== undefined) {
										window.location.href = result.RedirectURL;
									}
									else {
										if (result.ErrorMessage) {
											window.location.href = '/book/error?ErrorType=Custom&ErrorCode=' + result.ErrorMessage;
										} else {
											return false;
										}
									}
									break;
							}
						} else {
							window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
						}
					};

					var debounceInterstitialDynamicResize = haUtils.debounce(function () {
						$scope.interstitialDynamicResize();
					}, 100, false);

					window.onresize = function () {
						debounceInterstitialDynamicResize();
					};

					$scope.interstitialDynamicResize = function () {
						if (!$rootScope.isMobile) {
							$('.ha-modal#InterstitialAirAvailability-modal .modalContainer').css({
								'width': $(window).width(),
								'height': $(window).height(),
								'padding-top': $(window).height() * 0.20
							});
						}

					};


					$scope.populatePrevDateValidationPopup = function (dynamicContent) {
						var newContent = $('#ScData_PrevDateValidation_Unedited').html().replace('~INVALIDFLIGHT1~', dynamicContent[0]).replace('~INVALIDFLIGHT2~', dynamicContent[1]);
						$('#ScData_PrevDateValidation_DynamicContent').html(newContent);
					};

					$scope.showSelectAllFlightsPopup = function () {
						var OriginalContent = $('#SelectOpenSegmentPopupContentNoEdit').html();
						$('#SelectOpenSegmentPopupContent').html(OriginalContent.replace('~SEGMENT~', $scope.openSegments.TripId));
						var content = $('#SelectOpenSegmentValidationPopup').html();
						haModal({
							id: 'SelectAllSegmentsModalPopup',
							backdrop: 'true',
							template: content
						});
						return false;
					};

					// method to check if the flight faretype contains mixed cabin
					$scope.containsMixedClass = function (fareTypes, selectedSeatClass) {
						var isMixedCabin = false;
						angular.forEach(fareTypes, function (fareType) {
							if (fareType.CabinType.toLowerCase() === selectedSeatClass.toLowerCase()) {
								angular.forEach(fareType.BookingClassDetails, function (classDetail) {
									if (classDetail.IsMixedCabin) {
										isMixedCabin = true;
									}
								});
							}
						});
						return isMixedCabin;
					};

					// for displaying mixed cabin details
					$scope.legsWithCabinDetails = function (segments, fareDetails, selectedSeatClass) {
						var mappedSegmentList = [];

						angular.forEach(segments, function (segment) {
							mappedSegmentList.push(segment);
						});

						angular.forEach(fareDetails, function (fareDetail) {
							if (fareDetail.CabinType.toLowerCase() === selectedSeatClass.toLowerCase()) {
								angular.forEach(fareDetail.BookingClassDetails, function (classDetail) {
									angular.forEach(classDetail.ClassOfServices, function (classOfService, idx) {
										mappedSegmentList[idx].PricedClass = classOfService.PricedClass;
									});
								});
							}
						});

						return mappedSegmentList;
					};

					// for getting list of airline codes to show
					$scope.getFlightList = function (trip) {
						// check to see if it already went through the logic once for this trip
						if (trip.flightList === undefined) {
							var airlineCodeList = [];
							var anyHA = false;
							var html = '';
							var operatedByList = '';

							// only add Non-HA airlines first, and only add unique airline
							angular.forEach(trip.Segments, function (leg) {
								if (leg.OperatingAirline != null && leg.OperatingAirline !== '' && airlineCodeList.indexOf(leg.OperatingAirline) === -1) {
									if (leg.OperatingAirline === 'HA') {
										anyHA = true;
									} else {
										airlineCodeList.push(leg.OperatingAirline);
									}
								}
							});

							// varruct the html
							html += '<span class="flightnos">';
							angular.forEach(trip.Segments, function (leg, legIndex) {
								html += '<span class="flightno">' + leg.MarketingAirline + ' ' + leg.FlightNumber;
								if (leg.OperatingAirline != null && leg.OperatingAirline !== '' && airlineCodeList.indexOf(leg.OperatingAirline) > -1) {
									html += '<sup aria-hidden="true">' + (airlineCodeList.indexOf(leg.OperatingAirline) + 1) + '</sup>';
									html += '<span class="sr-only">' + $scope.operatedByText + ' ' + $scope.getCodeShareAirlineName(leg.OperatingAirline) + '</span>';
								}
								if (legIndex < trip.Segments.length - 1) {
									html += ',&nbsp;';
								}
								html += '</span>';
							});
							html += '</span>';

							if (airlineCodeList.length > 0) {
								html += '</br><span aria-hidden="true" class="operatedby">' + $scope.operatedByText;
								angular.forEach(airlineCodeList, function (code, codeIndex) {
									if (anyHA && codeIndex === 0) {
										operatedByList += $scope.getCodeShareAirlineName('HA') + ', ';
										html += ' ' + $scope.getCodeShareAirlineName('HA') + ',';
										trip.showHA = true; // use to decide if Operated by Hawaiian should show in the details section
									}
									if (codeIndex > 0) {
										operatedByList += ', ';
										html += ',';
									}
									operatedByList += $scope.getCodeShareAirlineName(code);
									html += ' <sup>' + (codeIndex + 1) + '</sup>' + $scope.getCodeShareAirlineName(code);
								});
								html += '</span>';
							}

							// save the result in the trip
							trip.operatedByList = operatedByList;
							trip.flightList = html;
						}

						return trip.flightList;
					};
					// Utility function for mobile lowest price display
					// Looks at a result's fares to see if it contains
					// the lowest price.
					$scope.hasLowestPrice = function (result) {
						if (!result || !result.FareDetails) {
							return;
						}
						for (var i = 0; i < result.FareDetails.length; i++) {
							if (result.FareDetails[i].IsLowestFare) {
								return true;
							}
						}
						return false;
					}

					$scope.upgradeToMainCabin = function (resultItem, closeModalEvent) {
						$scope.mcbToMain = true;
						var modalName = window.digitalData.page.pageInfo.name + ":" + "mcbUpgradeWarning";
						$scope.resultItem = resultItem;
						$scope.resultItem.forEach(function (seg) {
							if (seg.selectedSeatClass === 'MAINCABINBASIC') {
								seg.selectedSeatClass = 'COACH';
							}
						});

						resultItem.forEach(function (result, index, array) {
							var trip = $scope.searchResults[index - 1];
							if (trip.ActiveTab.TripAndFareDetails.filter(function (trip) { return (trip.isSelectedFlight && trip.selectedSeatClassInitial === "MAINCABINBASIC") }).length) {
								var selectedClass = result.FareDetails.filter(function (fare) { return fare.CabinType === 'COACH' })[0];

								if ($scope.searchResults[index - 1].hasSelectedFlight !== undefined && $scope.searchResults[index - 1].hasSelectedFlight) {
									delete $scope.searchResults[index - 1].hasSelectedFlight;
								}

								$scope.selectFlightWithPrice(result, trip, selectedClass, false);
							}
						});

						document.body.dispatchEvent(new CustomEvent('UpgradeToMainCabin', {
							'detail': {
								'pageName': modalName,
								'pageURL': $rootScope.dtmVals.pageURL
							}
						}));

						closeModalEvent();
					}
					$scope.acceptRestrictions = false;
				}];


			// ############## haBookFlightResultsEndOnEndLink ##############
			var haBookFlightResultsEndOnEndLink = function ($scope) {

				$timeout(function () {
					$scope.selectClass = angular.element('.select-class-wrapper');
					$scope.continueBarActive = false;
					$scope.editingSegment = false;
					$scope.openSegments = null;
				}, 0);

				// get top position of main content area so we know where to scroll the user to when selecting flights
				$timeout(function () {
					$scope.contentScrollTop = angular.element('.main-content').offset().top;
				}, 1000);

				// click on Select New Dates on the date error modal      //TODO - change once new booking form in place#############################################################################################
				$scope.selectNewDates = function () {
					$scope.$modalCancel();
					$scope.openFlightSearchEdit();
				};

				// get journey data
				$scope.getJourneyData = function () {

					var JDDataList = [];

					if ($scope.TripSummary && $scope.TripSummary.JDDataList) {

						angular.forEach($scope.TripSummary.JDDataList, function (jData) {
							if (jData) {
								JDDataList.push(jData);
							}
						});
					}
					return JDDataList;
				};

				// update journey data for a particular trip
				$scope.updateJourneyData = function (tripId) {
					//  initialize if it's undefined
					if ($scope.TripSummary.JDDataList === undefined) {
						$scope.TripSummary.JDDataList = [];
					}

					var trip = $scope.TripSummary.Trips[tripId - 1];

					if (trip) {
						var JDdata = {};
						JDdata.JDInd = true;
						JDdata.TId = trip.TripSlice.TripID;
						JDdata.S = trip.UniqueReferenceKey;
						angular.forEach(trip.FareDetails, function (fareDetail) {
							if (fareDetail.CabinType.toLowerCase() === trip.selectedSeatClass.toLowerCase()) {
								JDdata.C = fareDetail.BookingClassDetails[0];
								JDdata.Source = trip.selectedSeatClass;
								JDdata.HasUpgrade = trip.hasUpgrade;
								if (trip.selectedSeatClass.toLowerCase() === 'extracomfort') {
									JDdata.isEC = true;
								} else {
									JDdata.isEC = false;
								}
								if (trip.selectedSeatClass.toLowerCase() === 'preferred') {
									JDdata.isPS = true;
								} else {
									JDdata.isPS = false;
								}
							}
						});
						$scope.TripSummary.JDDataList[tripId - 1] = JDdata;
					}
				};

				// reset journey data for a particular trip
				$scope.resetJourneyData = function (tripId) {
					if ($scope.TripSummary && $scope.TripSummary.JDDataList && $scope.TripSummary.JDDataList[tripId - 1]) {
						$scope.TripSummary.JDDataList[tripId - 1] = null;
					}
				};

				// return true if there is any upgrade avaialble for the selected flight
				$scope.checkHasUpgrade = function (result, cabinType) {
					for (var i = 0; i < result.FareDetails.length; i++) {
						if (cabinType.toLowerCase() === result.FareDetails[i].CabinType.toLowerCase()) {
							if ((result.FareDetails[i + 1] && result.FareDetails[i + 1].BookingCount > 0) ||
								(result.FareDetails[i + 2] && result.FareDetails[i + 2].BookingCount > 0)) {
								return true;
							}
						}
					}
					return false;
				};

				// return true if any segment has hasUpgrade flag of true
				$scope.isAnyUpgrade = function () {
					var isUpgrade = false;
					angular.forEach($scope.TripSummary.Trips, function (trip) {
						if (trip.hasUpgrade) {
							isUpgrade = true;
						}
					});
					return isUpgrade;
				};

				// Iterate through the segments and see if we need to show the discount not applied flag.
				$scope.updateIsDiscountNotApplied = function () {
					if (!$scope.selectedPromo) {
						return;
					}
					var hideDiscount = false;
					angular.forEach($scope.TripSummary.Trips, function (trip) {
						var selectedClass = trip.FareDetails.filter(function (detail) {
							return detail.CabinType.toLowerCase() === trip.selectedSeatClass.toLowerCase();
						})[0];
						if (selectedClass && !selectedClass.IsDiscount) {
							hideDiscount = true;
						}
					});
					angular.forEach($scope.TripSummary.Trips, function (trip) {
						trip.isDiscountNotApplied = hideDiscount;
					});
				}

				// Selected a flight
				$scope.selectFlightWithPrice = function (result, trip, selectedClass, needsReload) {

					if (trip.hasSelectedFlight) {
						return;
					}
					trip.hasSelectedFlight = true; // true when collapsed, false when expanded
					$scope.editingSegment = false; // flag to prevent editing a trip while editing another trip

					// loop through flights and classes to turn off any selections
					angular.forEach($scope.searchResults[trip.TripId - 1].ActiveTab.TripAndFareDetails, function (flight) {
						flight.isSelectedFlight = false;
						angular.forEach(flight.FareDetails, function (fareDetail) {
							fareDetail.isSelected = false;
						});
					});

					result.isSelectedFlight = true; // set the flight to selected
					selectedClass.isSelected = true; // set the class to selected to turn on the radio button
					if ($scope.mcbToMain) {
						// checking if fare details have been updated
						if (result.TripSlice.TripID !== 1 && result.needsUpdate !== false) {
							result.needsUpdate = (result.FareDetails.filter(function (fare) { return fare.CabinType === "MAINCABINBASIC" }).length > 0) // setting this segment for fare detail update if upgrading from MCB to Main Cabin
						}
					}

					$scope.lastStepThruQueryID = trip.TripId;

					window.scrollTo(0, 0);
					// var scrollToPageTop = $('.page-1').offset().top - 40;
					// $('body, html').animate({ scrollTop: scrollToPageTop }, 'slow');
					// store the selected trip in the TripSummary
					$scope.TripSummary.Trips[trip.TripId - 1] = {};
					result.selectedSeatClass = selectedClass.CabinType; // save the selected class
					result.selectedSeatClassInitial = selectedClass.CabinType; // save copy to keep track of initial selection
					result.hasUpgrade = $scope.checkHasUpgrade(result, selectedClass.CabinType); // update flag for if update is available or not
					$.extend(true, $scope.TripSummary.Trips[trip.TripId - 1], result); // use extend to save as copy instead of reference, true for deep copy
					$scope.updateJourneyData(trip.TripId); // update journey data

					//if promo is selected but selected class has no discount, display Discount Not Applied message in all selected trips
					if ($scope.selectedPromo && !selectedClass.IsDiscount) {
						angular.forEach($scope.TripSummary.Trips, function (trip) {
							trip.isDiscountNotApplied = true;
						});
					}

					// Don't get results if we already have them - forward event
					if (typeof needsReload !== 'undefined' && !needsReload && $scope.searchResults[trip.TripId]) {
						$scope.currentSegmentId = trip.TripId + 1;
						return;
					}

					// show loading animation
					$scope.model.loadingSegment = true;

					// focus on loading spinner
					$timeout(function () {
						angular.element('.ha-loading-spinner').focus();
					}, 0);


					var jrnyData = $scope.getJourneyData();

					if (($scope.TripType === 1 && $scope.isAnyUpgrade() && $scope.priceTypeInfo.priceType === 0 && !$scope.searchRequest.IsRefundable) || ($scope.TripType === 2 && $scope.getJourneyData().length === 1) ||
						($scope.TripType === 2 && $scope.getJourneyData().length === 2 && $scope.isAnyUpgrade() && $scope.priceTypeInfo.priceType === 0 && !$scope.searchRequest.IsRefundable) ||
						($scope.TripType === 0 && $scope.getJourneyData().length < $scope.TripCount)) {
						// One Way and there is upgade and not a miles search and upsell grid is enabled
						// OR Round Trip and not last trip
						// OR Round Trip and last trip and there is upgrade and not a miles search and upsell grid is enabled
						// OR MultiCity and not last trip
						haFlightResultsEndOnEndAPI.SearchFlightsStepThrough($scope.getJourneyData(), trip.TripId, false).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-SearchFlightsStepThrough')) {
								return;
							}
							$scope.model.loadingSegment = false;
							if ($scope.$state.current.name !== 'segment.complete') {
								$scope.socialProofDisplay(results.DepartureDate, results.OriginCityCode, results.DestinationCityCode, results.ActiveTab.TripAndFareDetails.length);
							}
							$scope.lieFlatAvailable = $scope.hasLieFlat(results);
							$scope.hasECPreferred(results);
							$scope.hasMCBOferred(results);
							$scope.updateFareDetails = false;
							$scope.$emit('dtmPage', {
								pageName: $rootScope.dtmVals.pageName,
								pageURL: $rootScope.dtmVals.pageURL,
								lowestFare: results.AlternateTabs ? results.AlternateTabs[3].DisplayAmount || '0' : '0'
							});

							if (results.IsSuccess && results.TripId && results.ActiveTab && results.ActiveTab.TripAndFareDetails) {
								// cross check the result and search request
								if ($scope.crossCheckSearch(results, true)) {
									// Flight list response
									$scope.searchResults[results.TripId - 1] = results;
									$scope.resetFlightResultsLimit();
									$scope.Message = results.Message; // update alert message
									$scope.currentSegmentId = results.TripId; // focus on next flight
									$scope.updatePromo(results.Discounts); // update promo list in sticky booking widget and title for tooltipup
									$scope.arrowUpdate();
									$state.go('segment.x', { id: jrnyData.length + 1 });
								}
							} else if (results.IsSuccess && results.AvailFareGridResponseList) {
								// Fare grid response
								$scope.UpsellGrid = {};
								$scope.currentSegmentId = $scope.TripCount + 1;
								$state.go('segment.complete');

								if (results.AvailFareGridResponseList.length > 0 && results.UpSellGridDetails && results.UpSellGridDetails.AvailableCabins.length > 0) {
									// update FareGrid data

									$scope.lieFlatAvailable = $scope.hasLieFlatFareGrid($scope.TripSummary.Trips);

									////** Does this need a bigger List? **********************************
									// review offpreferredSeatCell.AvailGridTrips.AvailGridFareDetail.AvailGridFareTypes[0].AvailGridTaxes
									// $scope.FareGrid.FareGridDetails = results.AvailFareGridResponseList;
									// $scope.FareGrid.AvailableCabins = results.UpSellGridDetails.AvailableCabins;
									// $scope.FareGrid.UpSellGridTrips = results.UpSellGridDetails.UpSellGridTrips;

									$scope.UpsellGrid.UpSellGridDetails = results.UpSellGridDetails;
									$scope.UpsellGrid.AvailFareGridResponseList = results.AvailFareGridResponseList;

									// update TripSummary.FareDetails
									$scope.updateTripSummaryFareDetail();

									//Trigger Event when upsalegrid is offered
									var offerType = '';
									if ($.inArray('EXTRACOMFORT', results.UpSellGridDetails.AvailableCabins) > -1) {
										offerType = 'EXTRACOMFORT';
									}
									if ($.inArray('PREFERRED', results.UpSellGridDetails.AvailableCabins) > -1) {
										offerType = 'PREFERREDSEAT';
									}
									if (offerType) {
										document.body.dispatchEvent(new CustomEvent('UpgradeSeatOffered', {
											'detail': {
												'seatType': offerType,
												'pageName': $rootScope.dtmVals.pageName,
												'pageURL': $rootScope.dtmVals.pageURL
											}
										}));
									}

								} else {
									// fare grid response was empty, continue with initial selection
									$scope.generateTripSummaryFareDetail(selectedClass);
								}

								if (enablePassengerTripSummary && results.PassengerTripSummary) {
									// new TripSummary
									$scope.PassengerTripSummary = results.PassengerTripSummary;
								}

								$timeout(function () {
									if (!$scope.UpsellGrid.showUpsellGrid && $scope.UpsellGrid.UpSellGridDetails && $scope.UpsellGrid.UpSellGridDetails.UpSellGridTrips && $scope.UpsellGrid.UpSellGridDetails.UpSellGridTrips.length > 0) {
										$scope.UpsellGrid.showUpsellGrid = true;
									}
									$scope.continueBarActive = true; // show continue button
									$scope.showReceipt = true; // show trip summary

									//check if MCB is selected
									$scope.isMainCabinBasicSelected = $scope.TripSummary.Trips.filter(function(item){ return item.selectedSeatClass === 'MAINCABINBASIC'}).length > 0;

									// Tell the Hold Bookings Controller to check Hold Bookings Eligibility
									if ($scope.TripSummary.FareDetails && (!$scope.selectedPromo || isHoldEligible)) {
										$rootScope.checkHoldBookingEligibility = true;
									}
								}, 0);

							} else {
								if (results.ErrorCodeHandle === '20027' || results.ErrorCodeHandle === '20058' || results.ErrorCodeHandle === '20059') {
									var regex = /(<([^>]+)>)/ig;
									var body = results.ErrorCodeHandle + ': ' + results.ErrorMessage;
									var errorMsg = body.replace(regex, '');
									window.location.href = '/book/home?FltResNull=' + errorMsg;
								}
								else {
									$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-SearchFlightsStepThrough');
								}
							}
							if ($rootScope.isMobile) {
								$scope.getLowestFares(results);
							}
							// reload uplift after selecting a flight
							$(document).ready(function () {
								setTimeout(function () {
								haUplift.loadUplift(results, $scope.TripType);
								}, $rootScope.UpTimeout);
							});
						}).error(function (results) {
							$scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-SearchFlightsStepThrough');
							$scope.model.loadingSegment = false;
						});

					} else { // MultiCity and all flights are selected or there is no upgrade// flight list style update
						$scope.currentSegmentId = $scope.TripCount + 1;

						// for miles or ecert booking, go straight to pax page
						if ($scope.priceTypeInfo.priceType > 0 || ($scope.selectedPromo && $scope.selectedPromo.Type !== 2 && !isHoldEligible) || $scope.enableTCR || $scope.searchRequest.IsRefundable) {
							$timeout(function () {
								$scope.model.loadingSegment = false;
								$scope.AddFlightsToTrip(); // continue to availablity check
							}, 700);
						} else { // else check for held reservation first
							// Tell the Hold Bookings Controller to check Hold Bookings Eligibility
							var holdReservationScope = angular.element('#holdReservation').scope();
							holdReservationScope.checkEligibility(true, function (isEligible) {

								if (isEligible) {
									// if eligible, show TripSummary
									if (enablePassengerTripSummary) {
										// new TripSummary
										haFlightResultsEndOnEndAPI.GetTripSummary($scope.getJourneyData()).success(function (results) {
											$scope.model.loadingSegment = false;
											if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-GetTripSummary')) {
												return;
											}
											if (results.AvailGridTrips) {
												$scope.PassengerTripSummary = results;
											} else {
												$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-GetTripSummary');
											}

											$scope.continueBarActive = true; // show continue button
											$scope.showReceipt = true; // show trip summary

										}).error(function (results) {
											$scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-GetTripSummary');
										});

									} else {
										// old TripSummary
										$scope.model.loadingSegment = false;
										$scope.generateTripSummaryFareDetail(selectedClass);

										$scope.continueBarActive = true; // show continue button
										$scope.showReceipt = true; // show trip summary
									}
								} else {
									// if not eligible, redirect to pax page
									$timeout(function () {
										$scope.model.loadingSegment = false;
										$scope.AddFlightsToTrip(); // continue to availablity check
									}, 700);
								}

							});
						}
					}
				};

				// Helper method to generate TripSummary faredetail for MultiCity
				$scope.generateTripSummaryFareDetail = function (selectedClass) {

					$scope.TripSummary.FareDetails = {};
					$scope.TripSummary.FareDetails.Source = '';
					$scope.TripSummary.FareDetails.AvailGridTrips = {};
					$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridTripList = [];

					// map the trips
					angular.forEach($scope.TripSummary.Trips, function (trip, tripIndex) {
						// generate source text
						if (tripIndex > 0) {
							$scope.TripSummary.FareDetails.Source += '+';
						}
						$scope.TripSummary.FareDetails.Source += trip.selectedSeatClass.toUpperCase();

						// map segments
						$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridTripList.push({
							Segments: []
						});
						angular.forEach(trip.TripSlice.Segments, function (segment, segmentIndex) {
							$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridTripList[tripIndex].Segments.push({
								FlightNumber: segment.FlightNumber,
								DepartureDateTime: segment.DepartureDateTime,
								ArrivalDateTime: segment.ArrivalDateTime,
								Duration: segment.Duration,
								DayDifference: segment.DayDifference,
								OriginCityName: segment.OriginCityName,
								DestinationCityName: segment.DestinationCityName,
								MarketingAirline: segment.MarketingAirline,
								OperatingAirline: segment.OperatingAirline,
								ISOverNightStayRequired: segment.IsOverNightStay,
								LayOverTime: segment.LayOverTime,
								ExtraComfortFee: segment.ExtraComfortFee,
								StopInformation: segment.StopInformation,
								AvailGridClassOfService: {}
							});

							// map class of service.
							angular.forEach(trip.FareDetails, function (fareDetail) {
								if (trip.selectedSeatClass.toLowerCase() === fareDetail.CabinType.toLowerCase()) {
									$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridTripList[tripIndex].Segments[segmentIndex].AvailGridClassOfService.PricedCabin =
										fareDetail.BookingClassDetails[0].ClassOfServices[segmentIndex].PricedClass;
									$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridTripList[tripIndex].Segments[segmentIndex].AvailGridClassOfService.BookingClass =
										fareDetail.BookingClassDetails[0].ClassOfServices[segmentIndex].BookingClass;
								}
							});
						});
					});

					// map the fare detail
					$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridFareDetail = {};
					$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridFareDetail.DisplayAmount = selectedClass.DisplayAmount;
					$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridFareDetail.AvailGridFareTypes = [];
					angular.forEach(selectedClass.FareTypes, function (fareType) {
						$scope.TripSummary.FareDetails.AvailGridTrips.AvailGridFareDetail.AvailGridFareTypes.push({
							Type: fareType.Type,
							TotalAmount: fareType.TotalAmount,
							TotalBaseFare: fareType.TotalBaseFare,
							BaseFareAmountWithOutDiscount: fareType.BaseFareWithOutDiscount,
							FuelSurcharge: fareType.FuelSurcharge,
							TotalTax: fareType.TotalTax,
							PaxType: fareType.PaxType,
							AvailGridTaxes: fareType.Taxes,
							IsCompanion: fareType.IsCompanion
						});
					});

					$scope.$broadcast('calcTaxes'); // update receipt
				};


				// method to update the TripSummary FareDetail based on latest class selections
				$scope.updateTripSummaryFareDetail = function () {
					// build the seat class source name string
					var fareTypeStr = '';
					angular.forEach($scope.TripSummary.Trips, function (trip, TripIndex) {
						if (TripIndex > 0) {
							fareTypeStr += '+';
						}
						fareTypeStr += trip.selectedSeatClass.toUpperCase(); // currently selected seat class for other segments
					});

					// update the TripSummary FareDetail with the new price
					angular.forEach($scope.UpsellGrid.AvailFareGridResponseList, function (fare) {
						if (fare.Source === fareTypeStr) {
							$scope.TripSummary.FareDetails = fare;
						}
					});

					$scope.$broadcast('calcTaxes'); // update receipt
				};

				// method to update the PassengerTripSummary based on latest class selections
				$scope.updatePassengerTripSummaryFareDetail = function () {
					// build the seat class source name string
					var fareTypeStr = '';
					var selectedFareDetail;
					angular.forEach($scope.TripSummary.Trips, function (trip, TripIndex) {
						if (TripIndex > 0) {
							fareTypeStr += '+';
						}
						fareTypeStr += trip.selectedSeatClass.toUpperCase(); // currently selected seat class for other segments
					});

					// update the TripSummary FareDetail with the new price
					angular.forEach($scope.UpsellGrid.AvailFareGridResponseList, function (fare) {
						if (fare.Source === fareTypeStr) {
							selectedFareDetail = fare;
						}
					});

					if (selectedFareDetail) {
						// make a copy so update is done all at once at the end
						var newSummary = angular.copy($scope.PassengerTripSummary);

						// map AvailGridTrips
						newSummary.AvailGridTrips = selectedFareDetail.AvailGridTrips.AvailGridTripList;

						// map FareDetails
						newSummary.FareDetails = [];
						angular.forEach(selectedFareDetail.AvailGridTrips.AvailGridFareDetail.AvailGridFareTypes, function (fareType) {
							var fareTypeCode = 0;

							switch (fareType.Type) {
								case 'WEB':
									fareTypeCode = 0;
									break;
								case 'WBC':
									fareTypeCode = 1;
									break;
								case 'INF':
									fareTypeCode = 2;
									break;
							}

							var hasDiscount = selectedFareDetail.AvailGridTrips.AvailGridFareDetail.IsDiscount;

							var newFareType = {
								FareType: fareTypeCode,
								IsCompanion: fareType.IsCompanion,
								HasDiscount: hasDiscount,
								DiscountedAmount: fareType.DiscountAmount,
								TotalAmount: fareType.TotalAmount,
								BaseFareAmount: fareType.TotalBaseFare,
								BaseFareAmountWithOutDiscount: fareType.BaseFareWithOutDiscount,
								FuelSurcharge: fareType.FuelSurcharge,
								BaseFareMiles: 0,
								TaxAmount: fareType.TotalTax,
								Taxes: []
							};

							angular.forEach(fareType.AvailGridTaxes, function (tax) {
								var found = false;
								angular.forEach(newFareType.Taxes, function (existingTax) {
									if (existingTax.Code === tax.TaxCode) {
										found = true;
										existingTax.Amount += tax.TaxAmount;
									}
								});
								if (!found) {
									newFareType.Taxes.push({
										Code: tax.TaxCode,
										Description: tax.Description,
										Amount: tax.TaxAmount
									});
								}
							});

							newSummary.FareDetails.push(newFareType);
						});

						// map Passenger.Seats
						angular.forEach(newSummary.Passengers, function (passenger) {
							passenger.Seats = [];
							angular.forEach(newSummary.AvailGridTrips, function (trip) {
								angular.forEach(trip.Segments, function (segment) {
									var type = 0;
									var upSellGridSeatAmount = 0;
									if (segment.AvailGridClassOfService.PricedCabin.toUpperCase() === 'EXTRACOMFORT') {
										type = 1;
										upSellGridSeatAmount = segment.ExtraComfortFee;
									}
									if (segment.AvailGridClassOfService.PricedCabin.toUpperCase() === 'PREFERRED') {
										type = 2;
										upSellGridSeatAmount = segment.PreferredSeatFee;
									}
									passenger.Seats.push({
										Type: type,
										SeatLocation: null,
										SegmentID: segment.SegmentID,
										SeatAmount: upSellGridSeatAmount,
										TripID: trip.TripID
									});
								});
							});
						});

						$scope.PassengerTripSummary = newSummary;
					}
				};

				// method to return total price per person
				$scope.totalPricePerPaxDisplay = function () {
					return (enablePassengerTripSummary) ? parseFloat($scope.PassengerTripSummary.FareDetails[0].TotalAmount) : parseFloat($scope.TripSummary.FareDetails.AvailGridTrips.AvailGridFareDetail.DisplayAmount);
				};

				$scope.totalPricePerAdultDisplay = function () {
					var adultFare = 0;
					angular.forEach($scope.PassengerTripSummary.Passengers, function (pax) {
						if (pax.PaxType == 0 && pax.TotalAmount > 0 && pax.fare) {
							adultFare = pax.fare.TotalAmount;
						}
					});
					return parseFloat(adultFare);
				}

				$scope.totalPricePerChildDisplay = function () {
					var childFare = 0;
					angular.forEach($scope.PassengerTripSummary.Passengers, function (pax) {
						if (pax.PaxType == 1 && pax.fare) {
							childFare = pax.fare.TotalAmount;
						}
					});
					return parseFloat(childFare);
				}

				// Click on back to flight results button
				$scope.backToFlightResultsPage = function () {
					window.location.reload();
				};

				// Click on Change button on selected trip
				$scope.editSegment = function (segment, performSearch) {
					// ignore if in the middle of loading a segment
					if ($scope.model.loadingSegment) {
						return false;
					}

					$scope.model.loadingSegment = true;

					// if you modify a segment, hide hold booking block if it's shown
					$rootScope.checkHoldBookingEligibility = false;
					$scope.showReceipt = false; // hide trip summary
					$scope.selectClass.removeClass('active'); // hide up sell grid
					$scope.continueBarActive = false; // hide continue button

					$scope.currentSegmentId = segment.TripSlice.TripID;

					// Reset the journey data for all later segments
					angular.forEach($scope.searchResults, function (trip) {
						if (trip.TripId >= segment.TripSlice.TripID) {
							trip.hasSelectedFlight = false;
							$scope.resetJourneyData(trip.TripId); // reset journey data for that trip
						}
					});

					if ($scope.mcbToMain && (segment.needsUpdate === undefined || segment.needsUpdate) && segment.TripSlice.TripID > 1) {
						// Upgrading from MCB to Main Cabin and trip and segment id's are equal and segment needs to be updated and trip is not first segment fetch new fair details

						// focus on loading spinner
						$timeout(function () {
							angular.element('.ha-loading-spinner').focus();
						}, 0);

						$scope.lastStepThruQueryID = segment.TripSlice.TripID;
						haFlightResultsEndOnEndAPI.SearchFlightsStepThrough($scope.getJourneyData(), segment.TripSlice.TripID, true).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-SearchFlightsStepThrough')) {
								return;
							}
							$scope.model.loadingSegment = false;

							// replace the search result for the trip to be edited with the new list
							$.extend($scope.searchResults[results.TripId - 1], results);
							var newSegment = $scope.searchResults[results.TripId - 1].ActiveTab.TripAndFareDetails.filter(function (seg) {
								return seg.UniqueReferenceKey === segment.UniqueReferenceKey
							})[0];

							newSegment.isSelectedFlight = true;
							newSegment.needsUpdate = false;
							newSegment.mcbToMain = true;
							newSegment.FareDetails.filter(function (selectedClass) { return selectedClass.CabinType === 'COACH' })[0].isSelected = true;
							newSegment.selectedSeatClass = newSegment.FareDetails.filter(function (selectedClass) { return selectedClass.CabinType === 'COACH' })[0].CabinType;
							$scope.Message = results.Message; // update alert message

							// lowest fare assignment
							if ($rootScope.isMobile) {
								$scope.getLowestFares($scope.searchResults[results.TripId - 1]);
							}

						}).error(function () {
							$scope.model.loadingSegment = false;
						});
					}

					if (performSearch) {
						$scope.model.loadingSegment = true;
						$scope.lastStepThruQueryID = segment.TripSlice.TripID;

						haFlightResultsEndOnEndAPI.SearchFlightsStepThrough($scope.getJourneyData(), segment.TripSlice.TripID, true).success(function (results) {
							if ($scope.handleExceptions(results, 'haFlightResultsEndOnEndAPI-SearchFlightsStepThrough')) {
								return;
							}
							$scope.model.loadingSegment = false;


							// Slice off everything after this one
							$scope.searchResults = $scope.searchResults.slice(0, results.TripId);

							// replace the search result for the trip to be edited with the new list
							$.extend($scope.searchResults[results.TripId - 1], results);
							$scope.openSegments = $scope.searchResults[segment.TripSlice.TripID - 1]; // used to identify expanded segment
							$scope.Message = results.Message; // update alert message

							// Reset the selectedSeatClassInitial for all other segments
							for (var i = 0; i < results.TripId; i++) {
								$scope.TripSummary.Trips[i].selectedSeatClassInitial = $scope.TripSummary.Trips[i].selectedSeatClass;
							}
						}).error(function () {
							$scope.model.loadingSegment = false;
						});
					} else {
						$scope.openSegments = $scope.searchResults[segment.TripSlice.TripID - 1]; // used to identify expanded segment
						window.scrollTo(0, 0);
						if (($scope.mcbToMain === undefined) ||
							($scope.mcbToMain && segment.needsUpdate === false) ||
							($scope.mcbToMain && segment.needsUpdate === undefined && segment.TripSlice.TripID === 1)) {
							// not Upgrading from MCB to Main
							// Upgrading and fair details have been updated
							// Upgrading and its first segment
							$timeout(function () {
								$scope.model.loadingSegment = false;
							}, 0);
						}
					}
				};

				$scope.openSearchModal = function (flight) {

					if (!$scope.CurrentFlightScope || $scope.CurrentFlightScope === 'OFF') {
						$scope.CurrentFlightScope = 'ON';
					} else {
						$scope.CurrentFlightScope = 'OFF';
					}

					$scope.$seatViewModal.toggleClass('is-open');
					$scope.$seatViewModal.css({
						'margin-top': function () {
							return window.pageYOffset - ($(this).height() / 2);
						}
					});

					if ($scope.CurrentFlightScope === 'ON') {
						$scope.$broadcast('$UpdateSegments', flight);
					}
				};

				// For converting cabin type to cabin display name
				haGlobals('cabinDict', function (cabinDict) {
					$scope.cabinDict = [];
					angular.forEach(cabinDict, function (cabinType) {
						if (cabinType.Key !== undefined && cabinType.Value !== undefined) {
							$scope.cabinDict[cabinType.Key.toUpperCase()] = cabinType.Value;
						}
					});
				});

				$scope.getCabinDisplayName = function (cabinType) {
					cabinType = cabinType.toUpperCase();
					if ($scope.searchRequest.IsRefundable) {
						cabinType = 'REFUNDABLE' + cabinType;
					}
					if (cabinType in $scope.cabinDict) {
						return $scope.cabinDict[cabinType];
					} else {
						return cabinType;
					}
				};

				// For formatting DayDifference
				$scope.getFormattedDayDifference = function (dateDifference) {
					if ($scope.dateDifferenceDict === undefined) {
						haGlobals('dateDifferenceDict', function (dateDifferenceDict) {
							$scope.dateDifferenceDict = [];
							angular.forEach(dateDifferenceDict, function (value, key) {
								$scope.dateDifferenceDict[key] = value;
							});
						});
					}

					var dateDiffStr = '';
					var dateIndicator = '';
					dateIndicator = dateDifference;

					if (dateDifference < 0) {
						if (dateIndicator !== '' && dateIndicator in $scope.dateDifferenceDict) {
							dateDiffStr = dateDifference;
							return $scope.dateDifferenceDict[dateDiffStr];
						}
						dateDiffStr = '-' + dateDifference;

					} else if (dateDifference > 0) {
						if (dateIndicator !== '' && dateIndicator in $scope.dateDifferenceDict) {
							dateDiffStr = dateDifference;
							return $scope.dateDifferenceDict[dateDiffStr];
						}
						dateDiffStr = '+' + dateDifference;
					}

					if (dateDiffStr !== '' && dateDiffStr in $scope.dateDifferenceDict) {
						return $scope.dateDifferenceDict[dateDiffStr];
					}
					return dateDifference;
				};

				// For formatting airline codes
				$scope.getCodeShareAirlineName = function (airlineCode) {
					if ($scope.codeShareAirlineDict === undefined) {
						haGlobals('codeShareAirlineDict', function (codeShareAirlineDict) {
							$scope.codeShareAirlineDict = [];
							angular.forEach(codeShareAirlineDict, function (value, key) {
								$scope.codeShareAirlineDict[key] = value;
							});
						});
					}

					if (airlineCode in $scope.codeShareAirlineDict) {
						return $scope.codeShareAirlineDict[airlineCode];
					}
					return airlineCode;
				};

				// For formatting full date display
				$scope.getFormattedFullDate = function (dateStr, locale) {
					var m = dateStr.split(/[^0-9]/);
					var date = new Date(m[0], m[1] - 1, m[2], m[3], m[4], m[5]);
					if (locale === 'en') {
						return $filter('date')(date, 'fullDate');
					} else {
						return $filter('date')(date, 'longDate') + ' ' + $filter('date')(date, 'EEEE');
					}
				};

				// For formatting time display
				$scope.getFormattedTime = function (datetimeStr, isMilitaryTime) {
					var m = datetimeStr.split(/[^0-9]/);
					var date = new Date(m[0], m[1] - 1, m[2], m[3], m[4], m[5]);
					if (isMilitaryTime) {
						return $filter('date')(date, 'HH:mm');
					} else {
						var ampm = '';
						if ($filter('date')(date, 'H') < 12) {
							ampm = 'am';
						} else {
							ampm = 'pm';
						}
						return $filter('date')(date, 'h:mm') + ampm;
					}
				};

				// Add a Map to store the on time performance records using the flight number as the key
				$scope.onTimePerformanceRecords = new Map();

				$scope.clickDetails = function (result) {
					//Loading Plane configuration content
					if ($scope.PlanesMarketingData.length == 0) {
						haFlightResultsEndOnEndAPI.fetchPlaneMarketingData().success(function (results) {
							$scope.PlanesMarketingData = results;
							result.TripSlice.Segments.forEach(function (seg) {  // Set segment on time details
								{
									var mData = $scope.getAircraftMarketingInfo(seg.EquipmentType)
									if (mData) {
										seg.MarketingInfo = { Icon: mData.Icon, Message: mData.Message }
									}
									else {
										seg.MarketingInfo = { Icon: '', Message: '' }
									}

								}
							});

						}).error(function () {
							$scope.handleExceptions('jsError', 'haFlightResultsEndOnEndAPI-PlaneMarketingData');
						})
					} else {
						result.TripSlice.Segments.forEach(function (seg) {  // Set segment on time details
							{
								if (seg.MarketingInfo == null) {
									var mData = $scope.getAircraftMarketingInfo(seg.EquipmentType)
									if (mData) {
										seg.MarketingInfo = { Icon: mData.Icon, Message: mData.Message }
									}
									else {
										seg.MarketingInfo = { Icon: '', Message: '' }
									}
								}

							}
						});
					};

					if ($rootScope.isMobile) {
						haModal(haConfig.getTemplateUrl('book/flightresults/ha-flightresults-flightdetails-modal.html'), {
							id: 'flightDetailsModal',
							backdrop: true,
							scope: $scope,
							extendScope: { result: result }
						});
					}
					else if (result.detailsVisible) {
						var getFlightNumbers = [];  // The flight numbers to retrieve

						// Iterate the segments pull the flight numbers and set the details if previously pulled otherwise add flight number to retrieval list
						result.TripSlice.Segments.forEach(function (seg) {

							if (seg.OnTimeDetail == null) {   // Only retrieve details if they aren't already set
								var record = $scope.onTimePerformanceRecords.get(seg.FlightNumber);    // see if the details exist in the previously retrieved records

								if (record == null) {   // Details are not retrieved yet add flight number to retrieval list
									getFlightNumbers.push(seg.FlightNumber);
								}
								else {
									seg.OnTimeDetail = record;
								}
							}
						});

						// Get the OTP performance records if there are flight numbers to get
						if (getFlightNumbers.length >= 1) {
							// Get the on time performance records - here they are details but in the application logic they are records
							var results = haFlightResultsEndOnEndAPI.fetchOnTimePerformanceDetails(getFlightNumbers.join(',')).success(function (results) {
								if (results.ErrorMessage != null && results.ErrorMessage.length > 1) {
									result.TripSlice.Segments.forEach(function (seg) {  // Set segment on time details
										if (getFlightNumbers.indexOf(seg.FlightNumber) != -1) {   // Only try to set detail for segments that were searched for
											seg.OnTimeDetail = { DelayedPercentage: 'Error Delayed ', OnTimePercentage: 'Error On Time ' };
										}
									});

									return;
								}

								results.forEach(function (otp) {    // Save OTP results
									if (otp.OnTimeDetails.DelayedPercentage == null || otp.OnTimeDetails.DelayedPercentage == '')
										otp.OnTimeDetails.DelayedPercentage = '.';

									if (otp.OnTimeDetails.OnTimePercentage == null || otp.OnTimeDetails.OnTimePercentage == '')
										otp.OnTimeDetails.OnTimePercentage = '.';

									$scope.onTimePerformanceRecords.set(otp.FlightNumber, otp.OnTimeDetails);
								});

								result.TripSlice.Segments.forEach(function (seg) {  // Set segment on time details
									if (getFlightNumbers.indexOf(seg.FlightNumber) != -1) {   // Only try to set detail for segments that were searched for
										var detail = $scope.onTimePerformanceRecords.get(seg.FlightNumber);

										if (detail != null) {
											seg.OnTimeDetail = detail;
										}
										else {  // default the percentages to . so that the spinner can go away and display N/A
											seg.OnTimeDetail = { DelayedPercentage: '.', OnTimePercentage: '.' };
										}
									}
								});
							}).error(function () {
								result.TripSlice.Segments.forEach(function (seg) {  // Set segment on time details
									if (getFlightNumbers.indexOf(seg.FlightNumber) != -1) {   // Only try to set detail for segments that were searched for
										seg.OnTimeDetail = { DelayedPercentage: 'Error Delayed ', OnTimePercentage: 'Error On Time ' };
									}
								});
							});
						}
					}
				};

				$scope.togglePrices = function (result) {
					result.pricesVisible = !result.pricesVisible;
				};

				$scope.compareExperiencesModal = function () {
					$scs.get('compareexperience').then(function (result) {
						$scope.serviceTypeByBrand = result.servicestype[0].subservicetype[0].servicetypebybrand.map(function (brandType) {
							return { brandType: brandType.brandtype[0] }
						});
						$scope.servicesType = result.servicestype;
						haModal(haConfig.getTemplateUrl('book/flightresults/ha-compare-experience-modal.html'), {
							id: 'compareExperienceModal',
							backdrop: true,
							scope: $scope
						});

					});
				};

				$scope.showUpgradeWarningModal = function (scope, selectedResult, selectedClass) {
					var currentSegmentItem = scope.resultItem[$scope.currentSegmentId];
					var previousSegmentItem = scope.resultItem[$scope.currentSegmentId - 1]
					scope.upgradeAmount = currentSegmentItem.FareDetails.filter(function (cls) { return cls.CabinType === "COACH" })[0].DisplayAmount - previousSegmentItem.FareDetails.filter(function (cls) { return cls.CabinType === "MAINCABINBASIC" })[0].DisplayAmount
					scope.upgradeAmount = Math.ceil(scope.upgradeAmount);
					scope.upgradeAmount = (scope.upgradeAmount === 0 || scope.upgradeAmount === -0) ? '0' : scope.upgradeAmount;
					haModal(haConfig.getTemplateUrl('book/flightresults/ha-upgrade-warning-modal.html'), {
						id: 'mcbUpgradeWarning',
						backdrop: true,
						scope: scope,
						extendScope: {
							selectedResult: selectedResult,
							selectedClass: selectedClass
						},
						size: 'modal-md'
					});
				}

				$scope.showRestrictionsModal = function (scope) {
					var currentSegmentItem = scope.resultItem[$scope.currentSegmentId];
					var flightResultModalName = "mcbFlightResultsModal";
					var upgradeModalPageName = window.digitalData.page.pageInfo.name + ":" + flightResultModalName;
					// Price difference
					scope.amount = currentSegmentItem.FareDetails.filter(function (cls) { return cls.CabinType === "COACH" })[0].DisplayAmount - currentSegmentItem.FareDetails.filter(function (cls) { return cls.CabinType === "MAINCABINBASIC" })[0].DisplayAmount;
					scope.amount = Math.ceil(scope.amount);

					// Individual fare amounts
					scope.MCAmount = currentSegmentItem.FareDetails.filter(function (cls) { return cls.CabinType === "COACH" })[0].DisplayAmount;
					scope.MCBAmount = currentSegmentItem.FareDetails.filter(function (cls) { return cls.CabinType === "MAINCABINBASIC" })[0].DisplayAmount;

					scope.currentSegmentItem = currentSegmentItem;
					scope.acceptRestrictionsCallback = function () {
						scope.setAcceptRestrictions(true);
						scope.setResultItem(scope.currentSegmentItem, scope.currentSegmentId);
						$state.go((scope.currentSegmentId < scope.TripCount) ? 'segment.x' : 'segment.complete', { id: scope.currentSegmentId + 1, selectedResult: 0, selectedClass: 0 });
					};
					scope.rejectRestrictionsCallback = function () {
						scope.setResultItem(scope.currentSegmentItem, scope.currentSegmentId);
						document.body.dispatchEvent(new CustomEvent('UpgradeToMainCabin', {
							'detail': {
								'pageName': upgradeModalPageName,
								'pageURL': $rootScope.dtmVals.pageURL
							}
						}));


						$state.go((scope.currentSegmentId < scope.TripCount) ? 'segment.x' : 'segment.complete', { id: scope.currentSegmentId + 1, selectedResult: 1, selectedClass: 1 });
					}
					haModal(haConfig.getTemplateUrl('book/flightresults/ha-mcb-accept-restrictions-modal.html'), {
						id: flightResultModalName,
						backdrop: true,
						scope: scope,
						size: 'custom-modal-width'
					});
				}

				$scope.setAcceptRestrictions = function (val) {
					$scope.acceptRestrictions = val;
				}

				$scope.mcbRestrictionModal = function () {
					$scs.get('MCBRestrictionModal').then(function (result) {
						$scope.restrictions = result.restrictions;
						$scope.heading = result.heading;
						$scope.topcontent = result.topcontent;
						$scope.disclaimers = result.disclaimers;
						$scope.subheading = result.subheading;
						haModal(haConfig.getTemplateUrl('book/flightresults/ha-mcb-restrictions-modal.html'), {
							id: 'mcbRestrictionModal',
							backdrop: true,
							scope: $scope
						});

					});

				};

				$scope.toggleChildFareTooltip = function (c) {
					if ($scope.selectedChildFare == '' || $scope.selectedChildFare != c.$$hashKey) {
						$scope.selectedChildFare = c.$$hashKey;
						angular.forEach(c.FareTypes, function (fare) {
							if (fare.Type == 'WBC') {
								var tripText = '';
								if ($scope.TripType == 1) {
									tripText = $scs('FareSearch.onewaytext');
								} else if ($scope.TripType == 2) {
									tripText = $scs('FareSearch.roundtriptext');
								}
								var price = $filter('localCurrency')(fare.TotalAmount, $scope.currency);
								$scope.childFarePriceText = $scs('FareSearch.childfarepopuptext', [price, tripText]);
							}
						});
					} else {
						$scope.selectedChildFare = '';
					}
				}

				$scope.closeChildFareOverlay = function () {
					$scope.selectedChildFare = '';
				}

				$scope.isClassModified = function () {
					for (var i = 0; i < $scope.TripSummary.Trips.length; i++) {
						if ($scope.TripSummary.Trips[i].selectedSeatClass !== $scope.TripSummary.Trips[i].selectedSeatClassInitial) {
							return true;
						}
					}
				};

				$scope.selectSeatClass = function (classType, tripIndex) {

					if ($scope.TripSummary.Trips[tripIndex].selectedSeatClass.toLowerCase() === classType.toLowerCase()) {
						// revert the seat class to what was initially selected
						$scope.TripSummary.Trips[tripIndex].selectedSeatClass = $scope.TripSummary.Trips[tripIndex].selectedSeatClassInitial;
					} else {
						// update class selection
						$scope.TripSummary.Trips[tripIndex].selectedSeatClass = classType;
					}
					if (enablePassengerTripSummary) {
						$scope.updatePassengerTripSummaryFareDetail(); // update PassengerTripSummary FareDetail
					} else {
						$scope.updateTripSummaryFareDetail(); // update TripSummary FareDetail
					}

					// Update discount not applied flags
					$scope.updateIsDiscountNotApplied();

					// Update journey data
					$scope.updateJourneyData(tripIndex + 1);

					// Tell the Hold Bookings Controller to check Hold Bookings Eligibility
					if ($scope.TripSummary.FareDetails && (!$scope.selectedPromo || isHoldEligible)) {
						$rootScope.checkHoldBookingEligibility = true;
					}
				};
			};

			return {
				restrict: 'A',
				scope: true,
				link: haBookFlightResultsEndOnEndLink,
				controller: haBookFlightResultsEndOnEndController
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Explore Map
	// --------------------------------------------
	//
	// * **Class:** ExploreMap
	// * **Author:** Cory Shaw
	//
	// This is the interactive map that appears in the explore section

	'use strict';

	var module = angular.module('exploreMapModule', []);

	module.directive('exploreMap', ['haConfig', function (haConfig) {

		var ExploreMapController = function () {

		};

		ExploreMapController.$inject = ['$scope'];

		var ExploreMapLink = function ($scope) {
			$scope.$emit('$exploreMapReady');
		};

		ExploreMapLink.$inject = ['$scope'];

		return {
			restrict: 'A',
			templateUrl: haConfig.getTemplateUrl('explore-map-base-template'),
			scope: true,
			link: ExploreMapLink,
			controller: ExploreMapController
		};
	}]);

})(angular);;
(function (angular) {

	// Explore Hero
	// --------------------------------------------
	//
	// * **Class:** ExploreHero
	// * **Author:** Cory Shaw
	//
	// This is the hero full bleed image that appears at the top of many pages in the explore area

	'use strict';

	var module = angular.module('exploreHeroModule', []);

	module.directive('exploreHero', ['haConfig', function (haConfig) {

		var ExploreHeroController = function ($scope) {
			$scope.$emit('$methodsBound');
		};

		ExploreHeroController.$inject = ['$scope'];

		var ExploreHeroLink = function ($scope, $el, $attrs) {
			$scope.PhotoGallery = [];
			// NEP: FIXME: Over-assigment with an undefined or global.
			// $scope.PhotoGallery = PhotoGalleryVM;

			$scope.widgetTitle = $attrs.widgettitle;
			$scope.widgetDescription = $attrs.widgetdescription;
			$scope.widgetLinkLabel = $attrs.widgetlinklabel;
		};

		return {
			restrict: 'A',
			scope: true,
			link: ExploreHeroLink,
			templateUrl: haConfig.getTemplateUrl('explore-hero-base-template.html'),
			controller: ExploreHeroController
		};
	}]);

})(angular);
;
(function (angular) {
	'use strict';
	angular.module('haGlobalHeaderModule', ['haGlobalHeaderAPI'])
		.directive('haGlobalHeader', ['$timeout', 'haConfig', 'haModal', '$http', '$rootScope', function ($timeout, haConfig, haModal, $http, $rootScope) {
		return {
			restrict: 'A',
			scope: true,
			link: function (scope, elem) {

				var tray = elem.find('[ha-primary-nav-tray]');
				var navTrayHeightPx = '185px';

				function toggleNavigationTray(open, section) {
					if (open) {
						tray.css({ 'bottom': 0, 'visibility': 'visible' });
						$('.navtray-content-inner.active').removeClass('active');
						var active = $('.navtray-content-inner--' + section);
						$('.nav-overflow').css('max-height', navTrayHeightPx);
						scope.primaryNavtrayIsOpen = true;
						scope.currentSection = section;
						
						$timeout(function () {
							active.addClass('active');
							$('.nav-pane').css('opacity', '1');
						}, 120);

					} else {
						tray.css({ 'bottom': 0, 'transition': 'bottom .2s ease-in' });
						scope.primaryNavtrayIsOpen = false;
						scope.currentSection = null;
						$('.nav-overflow').css('max-height', '0');
						$('.nav-pane').css('opacity', '0');

						$timeout(function () {
							tray.css({ 'visibility': 'hidden' });
						}, 200);
					}
				}

				function toggleRegionMenu(open) {
					scope.regionSelectMenuIsOpen = open;

					if (!open) {
						$('.region.link').focus();
					}
				}

				function transitionTrayContent(section) {

					var current = $('.nav-pane.' + scope.currentSection);
					var to = $('.nav-pane.' + section);

					current.css('opacity', '1');
					scope.currentSection = section;

					$timeout(function () {
						to.css('opacity', '1');
					}, 90);
				}

				scope.skipToContent = function () {
					$('[role="main"] :tabbable:first').focus();
				};

				scope.toggleNavigation = function (section) {
					if (scope.primaryNavtrayIsOpen) {
						if (scope.currentSection === section) {
							toggleNavigationTray(false);
						} else {
							transitionTrayContent(section);
						}
					} else {
						toggleNavigationTray(true, section);
					}
					// Handle hiding Expert Booking tile on frontend side since tiles are cached per user in Sitecore
					// so it didn't work well when using Sitecore rule to show/hide the tile
					if (section === 'book') {
						if (!$rootScope.user.isExpertBookingOptIn) {
							$timeout(function() {
								angular.element('[ha-global-header]').find('a[href^="/Book/ExpertBooking"]').addClass('ng-hide');
							}, 0);
						}
					}
				};

				scope.toggleRegion = function () {
					if (scope.regionSelectMenuIsOpen) {
						toggleRegionMenu(false);
					} else {
						toggleRegionMenu(true);
					}
				};

				if (!scope.$root.isMobile) {
					$('html')
						.on('click',
							function(e) {

								var navTray = $.grep($(e.target).parents(),
									(function(n) {
										return $(n).hasClass('nav-overflow');
									}))
									.length >
									0;

								if (scope.primaryNavtrayIsOpen && !navTray && !$(e.target).hasClass('ha-nav')) {
									$timeout(function() {
										toggleNavigationTray(false);
									});
								}

								if (scope.regionSelectMenuIsOpen && !$(e.target).closest('.parent').hasClass('region')) {
									$timeout(function() {
										toggleRegionMenu(false);
									});
								}
							});

					$('body')
						.delegate('a:not(.ha-nav.nav-li-inner, .nav-pane a)',
							'focus',
							function() {
								if (scope.primaryNavtrayIsOpen) {
									$timeout(function() {
										toggleNavigationTray(false);
									});
								}
							})
						.delegate('a:not(.region)',
							'focus',
							function() {
								if (scope.regionSelectMenuIsOpen) {
									$timeout(function() {
										toggleRegionMenu(false);
									});
								}
							});

					$('body')
						.on('keyup',
							function(e) { // -- escape key
								if (e.keyCode === 27) {
									e.preventDefault();
									if (scope.regionSelectMenuIsOpen) {
										$timeout(function() {
											toggleRegionMenu(false);
										});
									}
									if (scope.primaryNavtrayIsOpen) {

										var pane = $(document.activeElement).parent().attr('class').split(' ')[1];

										if (['book', 'manage', 'airline', 'island'].indexOf(pane) > -1) {
											$('.ha-nav.' + pane).focus();
										}
										$timeout(function() {
											toggleNavigationTray(false);
										});
									}
								}
							});

					$('#skipToContent')
						.focus(function() {
							$(this).removeClass('sr-only');
						})
						.blur(function() {
							$(this).addClass('sr-only');
						});

					$('.nav-pane').removeClass('init');
				} else {

					scope.$watch('mobileMenus',	function(newVal) {
						if (newVal.menuOpen || newVal.myAcctOpen) {
							$('.ha-global-footer, section[role="main"]').hide();
						} else {
							$('.ha-global-footer, section[role="main"]').show();
						}
					}, true);

					// elem.on('remove', function() {
					// 	$('.ha-global-footer, section[role="main"]').show();
					// });
				}
			},
			controller: ['$scope', '$rootScope', 'haUser', 'haFavorites', 'haGlobals', 'haGlobalHeaderAPI', function ($scope, $rootScope, haUser, haFavorites, haGlobals, haGlobalHeaderAPI) {

				if ($rootScope.user) {

					$.extend($rootScope.user, {
						accountType: sessionStorage.getItem('accType'),
						haMiles: sessionStorage.getItem('haMiles')
					});
				}

				$scope.mobileMenus = {
					myAcctOpen: false,
					menuOpen: false
				};
				var redirectFn = function () {
					if ($rootScope.isMobile) {
						window.location.href = $scope.link;
					}
					else {
						window.open(
							$scope.link,
							'_blank'
						);
					}
				}
				$rootScope.barclaysPopup = function (link, sccontent) {
					$scope.link = link;
					if ($rootScope.isMobile) {
						Promise.all([
							$scs.get(sccontent + '.Content'),
							$scs.get(sccontent + '.Title')
						])
							.then(function (strings) {
								haModal(haConfig.getTemplateUrl('ha-modal.html'), {
									backdrop: 'true',
									title: strings[1], // don't show the header (we don't want the H1)
									success: {
										label: "Okay", fn: redirectFn
									},
									cancel: {}, // hide the cancel button
									size: 'modal-md',
									defaultContent: strings[0],
									scope: $scope,
									mobileVerticalCenter: true
								});
							});
					}
					else {
						redirectFn();
					}
				}
				function readCookie(name) {
					name += '=';
					for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--) {
						if (!ca[i].indexOf(name)) {
							return ca[i].replace(name, '');
						}
					}
				};

				function getBccIPSCookie() {
					var checkCookie = readCookie('IPS');
					if (checkCookie !== undefined) {
						return checkCookie;
					}
					return null;
				};

				// Define corporate and individual account types on rootscope
				$rootScope.corpAccTypes = ['C', 'R', 'W', 'A'];
				$rootScope.individualAccTypes = ['B', 'D', 'E', 'H', 'I', 'O', 'P', 'S', 'T', 'V', 'Z'];

				$scope.logout = function () {
					haUser.logout();
					sessionStorage.clear();
				};

				var enableUSWebSite;
				var enableAUWebSite;
				var enableNZWebSite;
				var sitecoreContextId;

				haUser.updateUser();
				haFavorites.updateFavorites();

				haGlobals('enableUSSite', function (enableUSSite) {
					enableUSWebSite = enableUSSite === "True";
				});
				haGlobals('enableAUSite', function (enableAUSite) {
					enableAUWebSite = enableAUSite === "True";
				});
				haGlobals('enableNZSite', function (enableNZSite) {
					enableNZWebSite = enableNZSite === "True";
				});
				haGlobals('sitecoreContextId', function (sitecoreCtxtId) {
					sitecoreContextId = sitecoreCtxtId;
				});
				haGlobals(['isLoggedIn', 'acctType', 'acctNo', 'isExpertBookingOptIn','haMilesEliteStatus'], function (isLoggedIn, acctType, acctNo, isExpertBookingOptIn, haMilesEliteStatus) {
					$rootScope.isLoggedIn = isLoggedIn;
					$rootScope.bccIpsOffer = getBccIPSCookie() === "True";
					if ($rootScope.user) {
						$.extend($rootScope.user, { accountType: acctType, haMiles: acctNo, isExpertBookingOptIn: isExpertBookingOptIn,  haMilesEliteStatus: haMilesEliteStatus });
					}
				});

				$scope.navSelectCountry = function (country) {
					haGlobalHeaderAPI.selectCountry(country, sitecoreContextId).success(function (result) {
						if (result !== null && result !== '') {
							window.location.href = result.RedirectUrl;
						}
					}, function (errorMessage) {
						window.alert(errorMessage);
					});					
				};
			}]
		};
	}]);
})(angular);
;
(function (angular) {
	'use strict';
	angular.module('haHeaderSearchModule', [])
	.directive('haHeaderSearch', ['haModal', '$http', function (haModal, $http) {
		return {
			restrict: 'A',
			scope: true,
			link: function (scope) {

				function toggleSearchModal(open) {

					if (open) {
						$('#searchModalTemplate .search-textarea').removeAttr('id');

						haModal({
							id: 'SearchModel',
							backdrop: false,
							scope: scope,
							size: 'modal-size',
							template: $('#searchModalTemplate'),
							cancel: {
								fn: function () {
									scope.searchModalIsOpen = false;
								}
							}
						});

						$('#searchModalTemplate .search-textarea').attr('id', 'help-search');
						$('#searchModalTemplate').attr('role', 'dialog');

						// ha-modal-service.js:107 focuses input

					} else {
						scope.$modalCancel();
						$('.nav-utility-li--search > a').focus();
					}

					scope.searchModalIsOpen = open;
				}

				scope.search = function () {
					if (scope.searchText) {
						$('.search-submit-btn').removeClass('disabled');
						$('.search-submit-btn > .sr-only').css('display', 'none');
					} else {
						$('.search-submit-btn').addClass('disabled');
						$('.search-submit-btn > .sr-only').css('display', 'block');
					}
				};

				scope.submitSearch = function () {

					if (!scope.searchText) {
						return false; // display error mayhaps?
					}

					$http({
						url: '/search-results',
						method: 'POST',
						headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
						data: $.param({ 'searchText': scope.searchText })
					}).then(function () {
						window.location.href = '/search-results';
					});
				};

				scope.toggleSearch = function () {
					if (!scope.searchModalIsOpen) {
						toggleSearchModal(true);
					} else {
						toggleSearchModal(false);
					}
				};
			}
		};
	}]);
})(angular);
;
(function (angular) {

    // Ha Share Miles
    // --------------------------------------------
    // * **Author:** Cory Shaw
    //
    // Controls the Share Miles pages
    'use strict';

    var module = angular.module('haShareMilesModule', ['haHttpService']);

    module.directive('haShareMiles', ['haGlobals', 'haHttpService', function (haGlobals, haHttpService) {

        return {
            restrict: 'A',
            scope: true,
            controller: ['$scope', 'haModal', function ($scope, haModal) {

                $scope.ShareMiles = {
                    AccountNumber: '',
                    LastName: '',
                    MilesToShare: '',
                    CurrentMilesBalance: 0,
                    HasSharedTravelers: true
                };
                $scope.Error = '';
                $scope.FilteredTravelersList = [];

                var travelersEndPoint = '/myaccount/travelerslist/gettravelerlist';

                haGlobals('shareMilesReceipientJson', function (shareMilesReceipientJson) {
                    $scope.ShareMiles.AccountNumber = shareMilesReceipientJson.AccountNumber;
                    $scope.ShareMiles.LastName = shareMilesReceipientJson.LastName;
                    $scope.ShareMiles.CurrentMilesBalance = shareMilesReceipientJson.CurrentMilesBalance;
                    if (shareMilesReceipientJson.MilesToShare > 0) {
                        $scope.ShareMiles.MilesToShare = shareMilesReceipientJson.MilesToShare;
                    }

                    $scope.ShareMiles.HasSharedTravelers = shareMilesReceipientJson.HasSharedTravelers;
                });

                $scope.getHelpContent = function () {
                    haModal('', {
                        id: 'share-miles-help',
                        backdrop: 'true',
                        template: angular.element('.getHelpContent')
                    });
                };

                haGlobals('errorMessage', function (errorMessage) {
                    if (errorMessage !== '') {
                        $scope.Error = errorMessage;
                    }
                });

                if ($scope.ShareMiles.HasSharedTravelers) {
                    haHttpService.GET(travelersEndPoint).then(
						function (response) {
						    if (response.data.TravelersList) {
						        $scope.FilteredTravelersList = response.data.TravelersList;
						    }
						},
						function (err) {
						    console.log(err);
						    $scope.ShareMiles.HasSharedTravelers = false;
						}
					);
                }

                $scope.travelerChosen = function (traveler) {
                    $scope.$modalCancel();
                    $scope.ShareMiles.LastName = traveler.LastName;
                    $scope.ShareMiles.AccountNumber = traveler.HMAccountNo || '';
                };

                $scope.openSharedTravelers = function () {
                    haModal({
                        id: 'sharedTravelersModal',
                        backdrop: 'true',
                        scope: $scope,
                        template: angular.element('#sharedTravelers')
                    });
                };
            }]
        };
    }]);


    module.directive('milesLimit', ['haGlobals', function (haGlobals) {
        return {
            require: 'ngModel',
            link: function (scope, elm, attr, ngModel) {
                var currentMilesBalance = null;
                var isValidCardMember = false;
                haGlobals('shareMilesReceipientJson', function (shareMilesReceipientJson) {
                    currentMilesBalance = parseInt(shareMilesReceipientJson.CurrentMilesBalance);
                });

                ngModel.$parsers.push(function (inputValue) {
                    try {
                        if (inputValue == null) {
                            return '';
                        }
                        var inputMiles = parseInt(inputValue);
                        if (inputMiles === 0) {
                            ngModel.$setValidity('InvalidMiles', false);
                        }
                        else {
                            if (inputMiles > currentMilesBalance) {
                                ngModel.$setValidity('MilesExceeded', false);
                            }
                            else {
                                ngModel.$setValidity('MilesExceeded', true);
                            }
                            ngModel.$setValidity('InvalidMiles', true);
                        }
                    }
                    catch (ex) {
                        ngModel.$setValidity('InvalidMiles', true);
                        ngModel.$setValidity('MilesExceeded', true);
                    }
                    return inputValue;
                });
            }
        };
    }]);

    module.directive('haShareMilesPayment', ['haGlobals', function (haGlobals) {

        var HaShareMilesController = function ($scope, $rootScope, haModal, $timeout) {
            $scope.submitting = false;

            haGlobals('transferMilesRecipientJson', function (transferMilesRecipientJson) {
                if (transferMilesRecipientJson != null) {
                    //$.extend($scope, transferMilesRecipientJson);
                    $scope.IsHACardHolder = transferMilesRecipientJson;
                }
            });

            $scope.getHelpContent = function () {
                haModal('', {
                    id: 'share-miles-help',
                    backdrop: 'true',
                    template: angular.element('.getHelpContent')
                });
            };

            $scope.showPrivacyPolicy = function () {
                haModal('', {
                    id: 'privacy-policy',
                    backdrop: 'true',
                    template: angular.element('.showPrivacyPolicy')
                });
            };

            $scope.showTerms = function () {
                haModal('', {
                    id: 'terms-and-conditions',
                    backdrop: 'true',
                    template: angular.element('.showTerms')
                });
            };

            $scope.onSubmitForm = function () {
                // ... all other internal processing on submit
                /* global userdata */
                /* jshint -W106 */
                userdata.load_data('user_data');
                /* jshint +W106 */

                $scope.submitting = true;
                return true;
            };

            // for PCI Payment Authorization
            $scope.haPaymentTypes = {
                paymentMethod: 'creditDebit'
            };

            $scope.CDESubmit = function () {
                $rootScope.$broadcast('CDESubmit');
            };

            $scope.$on('CDEPaymentSubmitted', function () {
                $scope.submitting = true;
            });

            $scope.$on('CDEPaymentSubmitError', function () {
                $scope.submitting = false;
                $timeout(function () {
                    $('html, body').animate({ scrollTop: $('#formIFrame').offset().top - 50 }, 'fast');
                }, 100);
            });
            // end PCI
        };

        HaShareMilesController.$inject = ['$scope', '$rootScope', 'haModal', '$timeout'];

        var HaShareMilesLink = function () {

        };

        return {
            restrict: 'A',
            scope: true,
            link: HaShareMilesLink,
            controller: HaShareMilesController
        };
    }]);

    module.directive('haShareMilesConfirmation', function () {
        var HaShareMilesConfirmationController = function ($scope, haModal, $filter) {

            /* global confirmationVM */
            $scope.CurrentMilesBalance = confirmationVM.CurrentMilesBalance;
            var newBalanceText = 'Balance' + ': ' + $filter('number')($scope.CurrentMilesBalance);
            angular.element('#account-nav-list li:first-child').find('span.popover-link-secondary').text(newBalanceText);

            $scope.showFAQ = function () {
                haModal('', {
                    id: 'share-miles-faq',
                    backdrop: 'true',
                    template: angular.element('.showFAQ')
                });
            };

            $scope.getHelpContent = function () {
                haModal('', {
                    id: 'share-miles-help',
                    backdrop: 'true',
                    template: angular.element('.getHelpContent')
                });
            };

            $scope.showTerms = function () {
                haModal('', {
                    id: 'terms-and-conditions',
                    backdrop: 'true',
                    template: angular.element('.showTerms')
                });
            };

            $scope.$on('haFormValidationSuccess', function () {
                return false;
            });

        };

        HaShareMilesConfirmationController.$inject = ['$scope', 'haModal', '$filter'];

        return {
            restrict: 'A',
            scope: true,
            controller: HaShareMilesConfirmationController
        };
    });

    module.filter('localPurchaseMilesCurrency', ['$compile', function () {
        var formats = {
            USD: '<span class="currency-symbol">$</span>{{ amount }}',
            AUD: '<span class="currency-symbol">$</span>{{ amount }} <span class="currency-type">AUD</span>',
            NZD: '<span class="currency-symbol">$</span>{{ amount }} <span class="currency-type">NZD</span>',
            CNY: '<span class="currency-symbol">¥</span>{{ amount }}',
            KRW: '<span class="currency-symbol">₩</span>{{ amount }}',
            JPY: '<span class="currency-symbol">¥</span>{{ amount }}',
            TWD: '<span class="currency-type">NT</span><span class="currency-symbol">$</span>{{ amount }}',
            MILES: '{{ amount }} <span class="currency-type">mi</span>'
        };

        var replaceNumberWithCommas = function (yourNumber) {
            //Seperates the components of the number
            var n = yourNumber.toString().split('.');
            //Comma-fies the first part
            n[0] = n[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
            //Combines the two sections
            return n.join('.');
        };

        var signCurrency = function (rawAmount, formatted) {
            if (rawAmount > -1) {
                return formatted + ' ' + angular.element('#MoreText').val();
            } else {
                return formatted.replace(/-([1-9]+)/g, function ($1, $2) {
                    return $2;
                }) + ' ' + angular.element('#LessText').val();
            }
        };

        return function (amount, code, noDecimal, signed, disableRoundOff) {
            var rounded;
            var commaFormatted = 0;
            if (code === 'CNY' || code === 'KRW' || code === 'JPY' || code === 'TWD') {
                disableRoundOff = false;
                noDecimal = true;
            }
            if (!disableRoundOff) {
                if (noDecimal) {
                    rounded = Math.round(amount);
                } else {
                    rounded = (Math.round(amount * 100) / 100).toFixed(2);
                }
            }
            else {
                rounded = amount;
            }

            if (rounded) {
                commaFormatted = replaceNumberWithCommas(rounded);
            }

            // get rid of decimal places if MILES
            if (code === 'MILES') {
                commaFormatted = commaFormatted.split('.')[0];
            }

            if (formats[code]) {
                var denominationFormatted = formats[code].replace(/\{\{.*\}\}/, commaFormatted);
                if (signed) {
                    return signCurrency(amount, denominationFormatted);
                } else {
                    //console.log(denominationFormatted);
                    return denominationFormatted;
                }
            }
        };
    }]);

})(angular);
;
(function (angular) {
	// Ha Cms Body Copy With Sidebar
	// --------------------------------------------
	//
	// * **Class:** HaCmsBodyCopyWithSidebar
	// * **Author:** Chad Kumabe

	'use strict';

	var mod = angular.module('haCmsBodyCopyWithSidebarModule', []);

	mod.directive('haCmsBodyCopyWithSidebar', function () {

		var HaCmsBodyCopyWithSidebarController = function ($scope) {
			$scope.$emit('$haCmsBodyCopyWithSidebarReady');
		};

		HaCmsBodyCopyWithSidebarController.$inject = ['$scope'];

		var HaCmsBodyCopyWithSidebarLink = function ($scope, $el) {
			// polyfill needed for Ie8
			if ($('html').hasClass('lte-ie8')) {
				$el.find(':last-child').addClass('last-child');
			}
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCmsBodyCopyWithSidebarLink,
			controller: HaCmsBodyCopyWithSidebarController
		};
	});

})(angular);
;
(function (ng) {

	// Ha Hotels Add On
	// --------------------------------------------
	//
	// * **Class:** HaHotelsAddOn
	// * **Author:** Nathan Probst
	//
	// This module implements the Orbitz Hotels Add On

	'use strict';

	var module;
	try {
		module = ng.module('haHotelPackagesModule');
	} catch (e) {
		module = ng.module('haHotelPackagesModule', ['haUtilsModule', 'haGlobalsModule', 'haFeatureFlagsModule', 'haCurrencyModule', 'haAncillariesModule', 'haModalService', 'haEqualHeightModule', 'haPassengersService', 'haRoundingFiltersModule', 'haSitecoreModule']);
	}


	module.directive('haHotelAddOn', ['haConfig', function (haConfig) {
		return {
			restrict: 'A',
			scope: true,
			transclude: true,
			templateUrl: haConfig.getTemplateUrl('ha-hotels-add-on-template.html'),
			controller: 'haHotelAddOnCtrl as ctrl'
		};
	}]);


	module.controller('haHotelAddOnCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'$location',
		'$window',
		'haUtils',
		'haGlobals',
		'haFeatureFlags',
		'haHotelCommonService',

		function ($log, $scope, $rootScope, $location, $window, haUtils, haGlobals, flags, hotelSvc) {

			haGlobals('HA', function (HA) {
				angular.extend($scope, HA.messages);
				$scope.morePerNightTxt = $scope.morePerNightTxt || 'morePerNightTxt';
			});

			var ctrl = {
				// Variables
				isEligible: false,
				hasPackages: false,
				airportCode: '',
				hotels: [],
				packages: [],
				nights: null,

				// Flags
				limitedAvailabilityLevel: flags.get('HotelLimitedAvailabilityLevel', 10),
				savingsThreshold: flags.get('HotelShowSavingsThreshold', 20), // default min $10

				// Methods
				hotelId: function () {
					return $location.search().hotelId;
				},
				showDetail: function (hotel) {
					$window.location.href = this.detailUrl(hotel);
				},
				detailUrl: function (hotel) {
					return ctrl.listUrl() + '#?hotelId=' + hotel.id;
				},
				listUrl: function () {
					return '/Book/Itinerary/GetVacationPackageList';
				},
				promoHasTerms: function (promo) {
					return promo.promoCode === 'VA' && (promo.customerFulfillmentRequirement || promo.termsAndConditions);
				},
				UpdateHotelMark: function()
				{
				    window.performance.clearMarks("mark_end_Hotel");
					window.performance.mark("mark_end_Hotel");
					if (window.performance.getEntriesByName("mark_end_Hotel") && window.performance.getEntriesByName("mark_end_Hotel")[0]) {
						var hotelMeasure = window.performance.getEntriesByName("mark_end_Hotel")[0].startTime;
						if (window.BOOMR && BOOMR.version) {
							window.BOOMR.sendTimer("Hotel_Itin_Time", hotelMeasure);
							window.BOOMR.sendMetric("UI_Search_Load", 1);
						}
				    }
				}
			};


			var init = function (data) {
				if (data == null) {
					return;
				}
				ctrl.isEligible = data.IsOrbitzPkgEligible;
				if (data.OrbitzPackage != null) {
					ctrl.hasPackages = data.OrbitzPackage.HasRetrievedOrbitzPackage;
					ctrl.airportCode = data.OrbitzPackage.DestinationAirport;
					ctrl.hotels = hotelSvc.mapOrbitzPackages(data.OrbitzPackage);
				}
				ctrl.loading = false;
			};

			// var _error = function (err) {
			//     ctrl.loading = false;
			//     ctrl.error = err;
			// };

			// ancillariesSvc.getAncillaries().then(_init, _error);
			$scope.$watch('ancillariesData', init);
			$scope._init = init; //jscs:ignore disallowDanglingUnderscores

			return ctrl;
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var el;

	function setText(id, text) {
		if (el[id]) {
			el[id].textContent = text;
		}
	}

	var module = angular.module('haPerformanceStatsModule', []);

	module.directive('haPerformanceStats', [
		'$log',
		'$window',
		'$document',
		'$rootScope',
		'haConfig',

		function ($log, $window, $document, $rootScope, haConfig) {
			// globals
			var performance = $window.performance;

			var ngStart = (performance != null) ? performance.now() : 0;
			var stats = $window.perfStats;

			stats.timeToAngular = (ngStart - stats.headStart);

			var countScopesWatchers = function () {
				// This logic is borrowed from $digest(). Keep it in sync!
				var next;
				var current;
				var target = $rootScope;
				var scopes = 0;
				var watchers = 0;

				current = target;
				do {
					scopes += 1;

					if (current.$$watchers) {
						watchers += current.$$watchers.length;
					}

					// Insanity Warning: scope depth-first traversal
					// yes, this code is a bit crazy, but it works and we have tests to prove it!
					// this piece should be kept in sync with the traversal in $broadcast
					if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
						while (current !== target && !(next = current.$$nextSibling)) {
							current = current.$parent;
						}
					}
				} while ((current = next));

				return [scopes, watchers];
			};

			return {
				restrict: 'A',
				replace: true,
				templateUrl: haConfig.getTemplateUrl('ha-performance-stats.html'),
				link: function (/* scope, elem, attrs */) {
					// Cache DOM elements
					el = {
						'#scopes': $document[0].querySelector('#scopes'),
						'#watchers': $document[0].querySelector('#watchers'),
						'#dirty-checks': $document[0].querySelector('#dirty-checks'),
						'#digest-cycles': $document[0].querySelector('#digest-cycles'),
						'#digest-ms': $document[0].querySelector('#digest-ms'),
						'#digest-fps': $document[0].querySelector('#digest-fps'),
						'#avg-digest-ms': $document[0].querySelector('#avg-digest-ms'),
						'#avg-digest-fps': $document[0].querySelector('#avg-digest-fps'),
						'#max-digest-ms': $document[0].querySelector('#max-digest-ms'),
						'#max-digest-fps': $document[0].querySelector('#max-digest-fps'),

						'#head-load': $document[0].querySelector('#head-load'),
						'#body-load': $document[0].querySelector('#body-load'),
						'#footer-load': $document[0].querySelector('#footer-load'),
						'#vendor-load': $document[0].querySelector('#vendor-load'),
						'#app-load': $document[0].querySelector('#app-load'),
						'#metrics-load': $document[0].querySelector('#metrics-load'),
						'#time-to-eop': $document[0].querySelector('#time-to-eop'),
						'#time-to-ng': $document[0].querySelector('#time-to-ng')
					};

					setText('#head-load', stats.headLoad.toFixed(1));
					setText('#body-load', stats.bodyLoad.toFixed(1));
					setText('#footer-load', stats.footerLoad.toFixed(1));
					setText('#vendor-load', stats.vendorScriptLoad.toFixed(1));
					setText('#app-load', stats.appLoad.toFixed(1));
					setText('#metrics-load', stats.metricsLoad.toFixed(1));
					setText('#time-to-eop', stats.TTLB.toFixed(1));
					setText('#time-to-ng', stats.timeToAngular.toFixed(1));

					// If the browser doesn't support Web Performance API
					// (I'm looking at you, Safari), don't even try.
					if (performance != null) {
						var digestCycles = 0;
						var digestStart = 0;
						var sumDigestMs = 0;
						var maxDigestMs = 0;
						var dirtyChecks = 0;

						// $digest loop uses a reverse while.
						// Pushing onto the end of $$watchers array makes this run first...
						$rootScope.$$watchers.push({
							eq: false,
							last: null,
							fn: function () {
							},
							exp: function () {
							},
							get: function () {
								dirtyChecks++;

								// Only update digestStart if not set. This allows for multiple
								// iterations inside the "dirty loop."
								//
								// NOTE: This technique for timing the $digest cycles
								//       DOES NOT capture time spent processing the asyncQueue!
								if (digestStart === 0) {
									// $log.debug('$rootScope.$watch: digestStart');
									digestStart = performance.now();
									digestCycles++;
								}

								// Schedules a one-shot callback after digest loop is clean
								$rootScope.$$postDigest(function () {
									if (digestStart !== 0) {
										var digestEnd = performance.now();
										var digestMs = (digestEnd - digestStart);
										setText('#digest-ms', digestMs.toFixed(1));
										setText('#digest-fps', (1000 / digestMs).toFixed(0));

										maxDigestMs = Math.max(digestMs, maxDigestMs);
										setText('#max-digest-ms', maxDigestMs.toFixed(1));
										setText('#max-digest-fps', (1000 / maxDigestMs).toFixed(0));

										sumDigestMs += digestMs;
										if (digestCycles > 0) {
											var avgDigestMs = sumDigestMs / digestCycles;
											setText('#avg-digest-ms', avgDigestMs.toFixed(1));
											setText('#avg-digest-fps', (1000 / avgDigestMs).toFixed(0));
										}

										setText('#dirty-checks', dirtyChecks);
										setText('#digest-cycles', digestCycles);

										var count = countScopesWatchers();
										var scopes = count[0];
										var watchers = count[1];

										setText('#scopes', scopes);
										setText('#watchers', watchers);

										var log = 'NG-PERF: Digest Cycle #' + digestCycles + ': ' + digestMs.toFixed(1) + ' ms, ' +
											'Scopes: ' + scopes + ', Watchers: ' + watchers +
											' [Overhead: ' + (performance.now() - digestEnd).toPrecision(3) + ' ms]';
										$log.debug(log);
										if ($window.console.timeStamp) {
											$window.console.timeStamp(log);
										}

										// Register an async function to run first.
										//
										// NOTE: This technique for timing the $digest cycles
										//       DOES capture time spent processing the asyncQueue!
										// $rootScope.$$asyncQueue.unshift({
										// 	scope: $rootScope,
										// 	expression: function (scope) {
										// 		// $log.debug('$rootScope.$evalAsync: digestStart');
										// 		digestStart = performance.now();
										// 		digestCycles++;
										// 	}
										// });

										// Clear digestStart for next "dirty loop."
										digestStart = 0;
									}
								});

								return null;
							}
						});
					}
				}
			};
		}
	]);

})(angular);

;
(function (angular) {

	'use strict';

	/**
	* @ngdoc directive
	* @name directive:haBookingForm
	* @description
	* # haBookingFrom
	*/

	var module = angular.module('haBookingFormModule', ['haFeatureFlagsModule', 'duScroll']);

	//	theme - sets theme a, b, c.  Default is a.
	//	promos - sets promos on/off. default is true/on.
	//  promoCodeValue - sets code inside of promo input.
	//	travelCredit - sets travelCredit on/off. Default is true/on.
	//	btnSearchFlights - hides/shows search flights button.  Default is true/on.
	//	btnSearchFlightsAndHotels - hides/shows search flights and hotels button. Default is true/on.
	//	recentSearchesSwitch - hides/shows recent searches.  Default is on.
	//	advancedSearchLink - hides/shows advanced search link.  Default is on for all themes except a (which is advanced search).
	//	flexibilePriceViewLink - hides/shows Monthly View Of Fares link.  Default is on.
	//	flightScheduleLink - hides/shows Flight Schedule link.  Default is on
	//  roundTripFlight - sets whether flight is round trip or one-way. Default is true (round trip).
	//	oneWay - allows one-way trips.  Default is on.
	//	roundTrip - allows round-trip trips.  Default is on.
	//	multiCity - allows multi-city trips.  Default is on.
	//	adults - hides/shows adults dropdown.  Default is on.  When hidden, adultcount will be 1.
	// 	adultCount - number of adults. Default is 1.
	//	children - hides/shows children dropdown.  Default is on.  When hidden, childcount will be 0.
	// 	childrenCount - number of children. Default is 0.
	//	searchPosition - Moves search buttons right and left.  Default is right (advanced booking form).
	//	expanded - Sets expanded on/off.  Default is on. Not expanded is used for home page and sticky booking widget.
	//  defaultDestinationImage - default background image for interstitial. Set to enable the image preloading. Default is off.
	//  milesRadioButton - hides/shows Miles radio button. Default is off.
	//  milesRadioOverride - Overrides the Miles radio selection instead after reading from cookie.  Default is off.
	//  childCountWarning - hides/shows the child traveling alone warning tooltip.  Default is on.
	//  departureLocation - set the from location in booking. Default is undefined.
	//  arrivalLoaction - set the to location for the flight. Ex: HNL
	//  departureDate - date that fills in the depart input. Ex: 20171026T000000
	//  arrivalDate - date that fills in the return input.

	module.directive('haBookingForm', [
		'$document',
		'haHttpService',
		'haConfig',
		'haGlobals',
		'haFeatureFlags',
		'$window',
		'haUtils',
		'haCitiesSvc',
		'haDateUtils',
		'haUnavailableDays',
		'$timeout',
		'haModal',
		'haEcertAPI',
		'$filter',
		'$q',
		'$compile',
		'$log',
		'haSitecoreStrings',
		function ($document,
			haHttpService,
			haConfig,
			haGlobals,
			haFeatureFlags,
			$window,
			haUtils,
			haCitiesSvc,
			haDateUtils,
			haUnavailableDays,
			$timeout,
			haModal,
			haEcertAPI,
			$filter,
			$q,
			$compile,
			$log,
			$scs) {
			return {
				//templateUrl: haConfig.getTemplateUrl('ha-booking-form.html'),	// Moved to razor - /areas/book/views/shared/ha-booking-form.cshtml
				restrict: 'A',
				link: function (scope, el, attrs) {

					var $bookingform = $('form[name="flightSearch"]');

					haCitiesSvc.preloadCities();

					//RT and OW datepickers
					scope.range_datepicker_config = {
						start: "[name='_FlightSearchSegmentList[0].DepartureDate']",
						end: "[name='_FlightSearchSegmentList[1].DepartureDate']"
					};

					//MultiCity datepickers
					scope.multicity_datepicker_configs = [];
					scope.multicity_datepicker_config = function (idx) {
						if (scope.multicity_datepicker_configs[idx]) {
							return scope.multicity_datepicker_configs[idx];
						}

						var cfg = scope.multicity_datepicker_configs[idx] = {
							viewing: getDepart(idx - 1) || moment().startOf('day'),
							range_start: getDepart(idx - 1) || moment().startOf('day'),
							range_end: getDepart(idx + 1) || moment().add(331, 'days').startOf('day'),
							idx: idx
						};
						cascadeMulticityCalendarSettings();
						return cfg;
					};

					function getDepart(idx) {
						var depart = scope.departDate[idx];
						return depart && moment(depart).startOf('day');
					}

					function cascadeMulticityCalendarSettings() {
						//(re)connect the start_range and end_range of each calendar with it's neighbor
						scope.multicity_datepicker_configs.forEach(function (config, idx) {
							var prev_selected = (function () {
								for (var i = idx; i;) {
									if (scope.departDate[--i]) {
										return moment(scope.departDate[i]);
									}
								}
							})();
							if (!getDepart(idx) && prev_selected) {
								transpose(config.viewing, prev_selected);
							}
							transpose(config.range_start, getDepart(idx - 1) || prev_selected || moment());
							transpose(config.range_end, getDepart(idx + 1) || moment().add(331, 'days'));
						});
					}

					function transpose(target, dt) {
						target.year(dt.year()).month(dt.month()).date(dt.date());
					}

					for (var i = 0; i < 6; i++) {
						scope.$watch('departDate[' + i + ']', function () {
							cascadeMulticityCalendarSettings();
						});
					}

					// EVENT HANDLERS
					//*******************
					// Date Input focused
					scope.$on('dateInputFocused', function (e, currentDateChoice, idx) {
						scope.currentDateChoice = currentDateChoice;
						scope.idx = idx;
						angular.forEach(scope.calendarOpen, function (val, key) {
							scope.calendarOpen[key] = false;
						});
						scope.calendarOpen[idx] = true;

						// Timeout to skip the first click event
						wireCalendarCloseEvent();
					});

					// Calendar clicked
					scope.$on('setDate', function (e, date) {

						if (!scope[scope.currentDateChoice]) {
							return;
						}

						// Did they click the already set depart or return
						scope[scope.currentDateChoice][scope.idx] = date || '';
						// reset the other date if needed
						if (scope.currentDateChoice === 'departDate' && scope.returnDate[scope.idx] && date > scope.returnDate[scope.idx]) { //depart
							scope.returnDate[scope.idx] = undefined;
						}
						else if (scope.currentDateChoice === 'returnDate' && scope.departDate[scope.idx] && date < scope.departDate[scope.idx]) {  //return
							scope.departDate[scope.idx] = undefined;
						}
						// reset all prior or next dates before/after date chosen for multi-city
						if (scope.tripType === 0) {
							var c;
							for (c = scope.idx; c > 0; c--) {
								if (scope.departDate[c - 1] && date < scope.departDate[c - 1]) {
									scope.departDate[c - 1] = date;
								}
							}
							for (c = scope.idx; c < scope.legs.length - 1; c++) {
								if (scope.departDate[c + 1] && date > scope.departDate[c + 1]) {
									scope.departDate[c + 1] = date;
								}
							}
						}

						scope.$digest();
					});

					// Airport input focused
					scope.$on('airportInputFocused', function (e, el) {
						var $li = el.closest('li');
						var idx = $li.index();
						scope.idx = idx;

						if (!scope.expanded) {
							scope.setExpanded();
						}
					});

					scope.$on('haWhereWeFlyPinClicked', function () {
						if (!scope.expanded) {
							$timeout(function () {
								scope.setExpanded();
							}, 500);
						}
					});

					// Airport value changed
					scope.$on('airportChanged', function airportChanged() {
						angular.forEach(scope.legs, function (leg) {
							leg.invalidPair = false;
						});
						var legs = scope.legs;
						var isRoundtrip = scope.tripType === 2;
						// The round trip booking form doesn't have <inputs> that bind
						//  to the 2nd leg of the trip- so we need to sync them manually.
						if (legs[0] && isRoundtrip) {
							if (legs.length < 2) {
								legs[1] = {};
							}
							legs[1].origin = legs[0].destination;
							legs[1].destination = legs[0].origin;
						}

						var leg = legs && legs[scope.idx];
						if (leg && (leg.origin && leg.origin.Code) && (leg.destination && leg.destination.Code)) {

							haUnavailableDays.getUnavailableDays(leg, scope)
								.then(function (ud) {
									//ud is an array [depart unavailables, return unavailables]
									//make sure any selected dates are still valid
									if (scope.departDate[scope.idx] && ud[0].CalendarYears.contains(scope.departDate[scope.idx])) {
										delete scope.departDate[scope.idx];
									}
									if (scope.returnDate[scope.idx] && ud[1].CalendarYears.contains(scope.returnDate[scope.idx])) {
										delete scope.returnDate[scope.idx];
									}

									scope.unavailableDays[scope.idx] = angular.isArray(ud) ? ud : [];
									scope.intl = ((leg.origin && leg.origin.Market === 3) || (leg.destination && leg.destination.Market === 3)) ? true : false;
									//recheck passenger count as INTL may have changed
									checkPassengerCount();
								});

							getMessages(scope.idx);
							//reset duplicate flag when an airport changes
							checkDuplicateLegs();
							//non contiguous legs are not supported
							//checkContiguousLegs();

							// preload destination image if the change was on the first segment and preloading is turned on
							if (scope.idx === 0 && scope.defaultDestinationImage !== '') {
								// preload destination image
								var imgURL = leg.destination.ImageURL;
								if (imgURL === '') {
									imgURL = scope.defaultDestinationImage;
								}
								var pic = new Image();
								pic.src = imgURL;
							}
						}

						updateRefundableFareOption(); // update the display of refundable fare checkbox
						updateMilesOption(); // enable/disable miles option
						scope.$broadcast('airport changed'); // for datepicker
					});

					// Search recalled from sticky
					scope.$on('recallSearch', function (e, idx) {
						scope.recallSearch(idx);
					});

					// WATCHERS
					//*******************
					// Watch Passenger Counts
					scope.$watchCollection('[pax.adultCount, pax.childCount]', function () {
						checkPassengerCount();
					});

					// Watch depart date
					scope.$watchCollection('departDate', function () {
						if (scope.departDate[scope.idx] && !scope.returnDate[scope.idx] && scope.tripType === 2) {
							scope.currentDateChoice = 'returnDate';
						} else if (scope.departDate[scope.idx] && scope.returnDate[scope.idx]) {
							delayCalendarClose(scope.idx);
						} else if (scope.tripType !== 2) {
							delayCalendarClose(scope.idx);
						}

						validateLowFareDuration(); // validate low fare duration
					});

					// Watch return date
					scope.$watchCollection('returnDate', function () {
						if (scope.returnDate[scope.idx] && !scope.departDate[scope.idx]) {
							scope.currentDateChoice = 'departDate';
						} else if (scope.returnDate[scope.idx] && scope.departDate[scope.idx]) {
							delayCalendarClose(scope.idx);
						}

						validateLowFareDuration(); // validate low fare duration
					});

					// Watch legs
					scope.$watchCollection('legs', function () {
						if (scope.milesRadioButton) {
							updatePaymentType();
						}

						validateLowFareDuration(); // validate low fare duration
					});

					// Watch recent open/close
					scope.recentSearches = { open: false };
					scope.$watch('recentSearches.open', function (isOpen) {
						if (typeof isOpen === 'undefined') {
							return;
						}

						if (isOpen) {
							$timeout(function () {
								$('body').on('click.recent', function () {
									scope.$apply(function () {
										scope.recentSearches.open = false;
									});
								});
							}, 10);

						} else {
							$('body').off('click.recent');
						}
					});

					// update refundable fare checkbox on miles option change
					scope.$watch('paymentType.type', function () {
						updateRefundableFareOption();
						flexibleDatesCalendar();
					});

					// update refundable fare checkbox on promo selection change
					scope.$root.$watch('selectedPromoId', function () {
						updateRefundableFareOption();
						updateMilesOption();
						updatePromoCodeOption();
					});

					// Change or re-enter promo code will reset the apply flag
					scope.$watch('promoCode.Code', function () {
						if (scope.flightSearch && scope.flightSearch.PromoCode) {
							scope.flightSearch.PromoCode.$setValidity('promoCodeNotFound', true);
						}
					});

					// LOCAL FUNCTIONS
					//*******************
					// Wire calendar close event
					function wireCalendarCloseEvent() {
						$document.off('click.closeCalendar');
						$document.on('click.closeCalendar', function (e) {
							var $targ = $(e.target);

							if ($targ.closest('.originDestinationWrap' + scope.idx).length) {
								return;
							}

							$document.off('click.closeCalendar');
							scope.currentDateChoice = '';
							angular.forEach(scope.calendarOpen, function (val, key) {
								delayCalendarClose(key);
							});
							scope.$digest();
						});
					}

					// delay cal close so user can see selection
					function delayCalendarClose(i) {
						$timeout(function () {
							scope.calendarOpen[i] = false;
						}, 500);
					}

					// Check and validate passenger counts/types
					function checkPassengerCount() {
						scope.isChildCountInvalid = (parseInt(scope.pax.adultCount, 10) === 0 && parseInt(scope.pax.childCount, 10) > 0);
						scope.isSearchDisabled = (scope.pax.adultCount < 1 && scope.pax.childCount < 1);
						var paxCount = parseInt(scope.pax.adultCount, 10) + parseInt(scope.pax.childCount, 10);
						scope.isCountValid = paxCount > 0 && paxCount < 8;
						if (scope.flightSearch) {
							scope.flightSearch.$setValidity('haPassengerCount', scope.isCountValid);
						}
					}

					// Update the selection of payment type radio button
					function updatePaymentType() {
						// if Promo is applied or Multi-City, set to dollars
						if (scope.$root.selectedPromoId && scope.tripType > 0) {
							scope.paymentType.type = '0';
							angular.forEach(scope.legs, function (leg) {
								leg.isMiles = false;
							});
						}
						switch (scope.tripType) {
							case 0:
								scope.paymentType.type = '0';
								break;
							case 1:
								if (scope.legs.length === 1 && scope.legs[0].isMiles) {
									scope.paymentType.type = '1';
								} else {
									scope.paymentType.type = '0';
								}
								break;
							case 2:
								if (scope.legs.length === 2 && scope.legs[0].isMiles && scope.legs[1].isMiles) {
									scope.paymentType.type = '1';
								} else if (scope.legs.length === 2 && !scope.legs[0].isMiles && scope.legs[1].isMiles) {
									scope.paymentType.type = '2';
								} else if (scope.legs.length === 2 && scope.legs[0].isMiles && !scope.legs[1].isMiles) {
									scope.paymentType.type = '3';
								} else {
									scope.paymentType.type = '0';
								}
								break;
						}
						if (scope.milesRadioOverride !== '') {
							scope.paymentType.type = scope.milesRadioOverride;
						}
					}

					// check if all airports are either HA or Ohana
					function isHARoute() {
						var invalidOrigin = false;
						var invalidDestination = false;
						// check the validity of origin if origin is set
						if (scope.legs[0] && scope.legs[0].origin) {
							// origin cannot be code share, unless it's Ohana flights (MKK, JHM, or LNY)
							if (scope.legs[0].origin.IsCodeShare && scope.legs[0].origin.Code !== 'MKK' && scope.legs[0].origin.Code !== 'JHM' && scope.legs[0].origin.Code !== 'LNY') {
								invalidOrigin = true;
							}
						}
						// check the validity of destination if destination is set
						if (scope.legs[0] && scope.legs[0].destination) {
							// destination cannot be code share, unless it's Ohana flights (MKK, JHM, or LNY)
							if (scope.legs[0].destination.IsCodeShare && scope.legs[0].destination.Code !== 'MKK' && scope.legs[0].destination.Code !== 'JHM' && scope.legs[0].destination.Code !== 'LNY') {
								invalidDestination = true;
							}
						}

						return (!invalidOrigin && !invalidDestination);
					}

					// Update the visibility of Refundable fare checkbox
					function updateRefundableFareOption() {
						if (scope.$switch('BookingWidget:enablerefundablefares') && scope.refundableFareOption && !scope.enableTCR) {
							// only show Refundable fare option if it's not multicity, is not miles booking
							if (!scope.$root.selectedPromoId && scope.tripType !== 0 && (scope.paymentType.type === undefined || scope.paymentType.type === '0') && isHARoute()) {
								scope.showRefundableFare = true;
								return;
							}
						}
						scope.showRefundableFare = false;
					}
					function flexibleDatesCalendar() {
						if (scope.paymentType.type === '1') {
							scope.flexibleDatesCalendarChecked = false;
							document.getElementById("flexibleDatesCalendar").checked = false;
							return;
						}
						scope.flexibleDatesCalendar = null;
					}

					// Enable/Disable Miles option
					function updateMilesOption() {
						// disable Miles option if promo is applied, it's not HA Route, or Multicity
						scope.disableMilesOption = (!(!scope.$root.selectedPromoId) || !isHARoute() || scope.tripType === 0);

						// if disabled or it is one way and miles/dollars is selected, default to Dollars
						if (scope.disableMilesOption || (scope.tripType === 1 && parseInt(scope.paymentType.type, 10) > 1)) {
							scope.paymentType.type = '0';
						}
					}

					// Enable/Disable Promo Code option
					function updatePromoCodeOption() {
						// only if promoCodeOption is true, no ecert or ETCO is applied
						scope.promoCodeOptionEnabled = (scope.promoCodeOption && !scope.$root.selectedPromoId && !scope.enableTCR);
					}

					// Check if the duration is valid for Low Fare chart
					function validateLowFareDuration() {
						// if flexiblePriceViewLink is enabled and is Round Trip, check if duration is valid
						if (scope.flexiblePriceViewLink) {
							if (scope.departDate[0] && scope.returnDate[0] && scope.tripType === 2) {
								scope.isValidLowFareDuration = (haDateUtils.numDaysDifference(scope.departDate[0], scope.returnDate[0]) <= 60);
							} else {
								scope.isValidLowFareDuration = true;
							}
						}
					}

					// Check legs for duplicates in multi-city
					function checkDuplicateLegs() {

						//first set/reset the dupe check fo valid
						var legDuplicateCheckValid = true;
						scope.flightSearch.$setValidity('haDuplicateLegs', legDuplicateCheckValid);

						$timeout(function () {
							if (scope.tripType !== 0 || scope.legs.length < 2 || !scope.flightSearch.$valid) {
								return;
							}

							//need to check every leg against every other leg
							var currOrigin;
							var currDestination;
							for (var legIndexOuter = 0; legIndexOuter < scope.legs.length; legIndexOuter++) {
								currOrigin = scope.legs[legIndexOuter].origin.Code;
								currDestination = scope.legs[legIndexOuter].destination.Code;

								for (var legIndexInner = 0; legIndexInner < scope.legs.length; legIndexInner++) {
									if (legIndexOuter !== legIndexInner) {

										legDuplicateCheckValid = scope.tripType !== 0 || currOrigin !== scope.legs[legIndexInner].origin.Code || currDestination !== scope.legs[legIndexInner].destination.Code;

										scope.flightSearch.$setValidity('haDuplicateLegs', legDuplicateCheckValid);

										if (!legDuplicateCheckValid) {
											return;
										}
									}
								}
							}
						});
					}

					//function checkContiguousLegs() {
					//	//first set/reset the dupe check fo valid
					//	var legContiguousCheckValid = true;
					//	scope.flightSearch.$setValidity('haContiguousLegs', legContiguousCheckValid);
					//	if (scope.tripType !== 0 || scope.legs.length < 2 || !scope.flightSearch.$valid) {
					//		return;
					//	}

					//	for (var leg = 0; leg + 1 < scope.legs.length; leg++) {
					//		var currentLegDestination = scope.legs[leg].destination.Code;
					//		var nextLegOrigin = scope.legs[leg + 1].origin.Code;
					//		if (currentLegDestination != nextLegOrigin) {
					//			scope.legs[leg + 1].invalidPair = true;
					//			scope.flightSearch.$setValidity('haContiguousLegs', false);
					//		}
					//	}
					//}

					// Get calendar leg messages
					function getMessages(idx) {
						if (typeof idx === 'undefined') {
							return;
						}
						scope.messages[idx] = [getMessage(idx, 'origin'), getMessage(idx, 'destination')].filter(function (x) {
							return !!x;
						}).map(function (m) {
							return '<div class="cal-message">' +
								'<i class="ha-icon fontIcon24-' + (scope.widgetDatepickerAlertIcon || 'info-circle') + '"></i>' +
								'<p>' + m + '</p>' +
								'</div>';
						}).join('');
					}

					function getMessage(idx, endpoint) {
						return (endpoint = scope.legs[idx][endpoint]) && endpoint.ShowCalendarMessage ? endpoint.CalendarMessage : '';
					}

					var today = new Date(new Date().setHours(0, 0, 0, 0));

					function apply(query, ignoreDates) {
						query = query || {};
						// if date is invalid or if it is multi-city search when multi-city is disabled, don't load the cookie
						var searchIsValid = query.legs && (ignoreDates || query.legs[0].departDate >= today) && !(query.tripType === 0 && scope.multiCity === false);
						if (searchIsValid) {
							scope.legs = [];
							scope.departDate = [];
							scope.returnDate = [];
							scope.unavailableDays = [];
							scope.messages = [];

							query.legs.forEach(function (l, i) {
								haCitiesSvc.getCityMap().then(function (map) {
									var leg = scope.legs[i] = {
										origin: map[l.origin.code],
										destination: map[l.destination.code],
										isMiles: !!l.miles
									};

									scope.intl = leg.origin.Market === 3 || leg.destination.Market === 3;

									haUnavailableDays.getUnavailableDays(leg, scope).then(function (ud) {
										scope.unavailableDays[i] = angular.isArray(ud) ? ud : [];
										checkPassengerCount();
										getMessages(i);
									});
								});

								scope.departDate[i] = l.departDate;

								var segReturn = query.legs[i + 1];
								if (segReturn) {
									scope.returnDate[i] = segReturn.departDate;
								}
							});
						}

						// Travel Credits and NITP bookings can only be redeemed for one adult.
						scope.pax.adultCount = scope.enableTCR || scope.disableAdultFieldForNITP ? 1 : num(query.adults, 1);
						scope.pax.childCount = scope.enableTCR || scope.disableAdultFieldForNITP ? 0 : num(query.children, 0);
						scope.tripType = num(query.tripType, 2);

						if (scope.flightSearch) {
							scope.flightSearch.IsRefundableCheck = !!query.refundable;
						}

						checkPassengerCount();
						updatePaymentType();
					}

					function applyWithAttributes(query) {
						query = query || {};
						scope.legs = [];
						scope.departDate = [];
						scope.returnDate = [];
						scope.unavailableDays = [];
						scope.messages = [];

						query.legs.forEach(function (l, i) {
							haCitiesSvc.getCityMap().then(function (map) {
								var leg = scope.legs[i] = {
									origin: map[l.origin.code],
									destination: map[l.destination.code],
									isMiles: !!l.miles
								};

								if (leg.origin && leg.origin.Market && leg.destination && leg.destination.Market) {
									scope.intl = leg.origin.Market === 3 || leg.destination.Market === 3;
								} else if (leg.origin && !leg.destination) {
									scope.intl = leg.origin.Market === 3;
								} else {
									scope.intl = leg.destination ? leg.destination.Market === 3 : '';
								}

								// Make sure that the leg has a departure date and arrival date
								if (leg.origin && leg.destination) {
									haUnavailableDays.getUnavailableDays(leg, scope).then(function (ud) {
										scope.unavailableDays[i] = angular.isArray(ud) ? ud : [];
										checkPassengerCount();
										getMessages(i);
									});
								}
							});

							scope.departDate[i] = l.departDate;

							var segReturn = query.legs[i + 1];
							if (segReturn) {
								scope.returnDate[i] = segReturn.departDate;
							}
						});

						// Travel Credits and NITP bookings can only be redeemed for one adult.
						scope.pax.adultCount = scope.enableTCR || scope.disableAdultFieldForNITP ? 1 : num(query.adults, 1);
						scope.pax.childCount = scope.enableTCR || scope.disableAdultFieldForNITP ? 0 : num(query.children, 0);
						scope.tripType = num(query.tripType, 2);

						if (scope.flightSearch) {
							scope.flightSearch.IsRefundableCheck = !!query.refundable;
						}

						checkPassengerCount();
						updatePaymentType();
					}

					function num(value, deflt) {
						value = Number(value);
						return isNaN(value) ? deflt : value;
					}

					function processRecentSearches() {
						var recentSearchArr = haUtils.getFlightQueryModelRecentCookie();
						scope.flightQueryCookieArr = recentSearchArr && recentSearchArr.filter(function (search) {
							var depart = search.FlightSearchSegmentList[0].DepartureDate.substr(0, 10);//make sure it's only the date
							depart = moment(depart, "YYYY-MM-DD").toDate();
							return depart >= today;
						});
					}

					// Set booking form attributes
					function setAttributes() {
						//	theme - sets theme a, b, c.  Default is a.
						//	promos - sets promos on/off. default is true/on.
						//  promoCodeValue - presets the promo code input.
						//	travelCredit - sets travelCredit on/off. Default is true/on.
						//	btnSearchFlights - hides/shows search flights button.  Default is true/on.
						//	btnSearchFlightsAndHotels - hides/shows search flights and hotels button. Default is true/on.
						//	recentSearchesSwitch - hides/shows recent searches.  Default is on.
						//	advancedSearchLink - hides/shows advanced search link.  Default is on for all themes except a (which is advanced search).
						//	flexibilePriceViewLink - hides/shows Monthly View Of Fares link.  Default is on.
						//	flightScheduleLink - hides/shows Flight Schedule link.  Default is on
						//  roundTripFlight - sets whether flight is round trip or one-way. Default is true (round trip).
						//	oneWay - allows one-way trips.  Default is on.
						//	roundTrip - allows round-trip trips.  Default is on.
						//	multiCity - allows multi-city trips.  Default is on.
						//	adults - hides/shows adults dropdown.  Default is on.  When hidden, adultcount will be 1.
						// 	adultsCount - number of adults. Default is 1.
						//	children - hides/shows children dropdown.  Default is on.  When hidden, childcount will be 0.
						//  childrenCount - number of children. Default is 0.
						//	searchPosition - Moves search buttons right and left.  Default is right (advanced booking form).
						//	expanded - Sets expanded on/off.  Default is on. Not expanded is used for home page and sticky booking widget.
						//  defaultDestinationImage - default background image for interstitial. Set to enable the image preloading. Default is off.
						//  milesRadioButton - hides/shows Miles radio button. Default is off.
						//  milesRadioOverride - Overrides the Miles radio selection instead after reading from cookie.  Default is off.
						//  childCountWarning - hides/shows the child traveling alone warning tooltip.  Default is on.
						//  refundableFareOption - hides/shows the Refundable Fare Only option.  Default is on.
						//  promoCodeOption - hides/shows the promo code inputs.  Default is off.
						//  departureLocation - set the from location in booking. Default is undefined. EX: HNL
						//  arrivalLoaction - set the to location for the flight. EX: KOA
						//  departureDate - date that fills in the depart input. Ex: 20171026T000000
						//  arrivalDate - date that fills in the return input.
						$timeout(function () {

							// Default properties to true
							"promos,travelCredit,btnSearchFlights,btnSearchFlightsPlusHotels,recentSearchesSwitch,advancedSearchLink,flexiblePriceViewLink,flightScheduleLink,oneWay,roundTrip,multiCity,adults,children,expanded,childCountWarning,refundableFareOption,Enable1AOnLegacy".split(',')
								.forEach(function (prop) {
									scope[prop] = !(attrs[prop] && attrs[prop] === 'false');
								});

							// Default properties to false
							['milesRadioButton', 'promoCodeOption']
								.forEach(function (prop) {
									scope[prop] = !!(attrs[prop] && attrs[prop] === 'true');
								});

							if (scope.isAffiliate || scope.isCorporate) {
								scope.btnSearchFlightsPlusHotels = false;
							}
							// String property defaults
							scope.theme = attrs.theme || 'a';
							scope.milesRadioOverride = attrs.milesRadioOverride || '';
							scope.searchPosition = attrs.searchPosition || 'right';
							scope.defaultDestinationImage = attrs.defaultDestinationImage || '';

							validateLowFareDuration(); // validate low fare duration
							updatePromoCodeOption();
						});
					}

					// SCOPE METHODS
					//*******************
					// Set trip type from click
					scope.setTripType = function (t) {
						// reset submission state so ha-errors doesn't get confused
						// we need this since form elements are getting swapped in and out, but parent form persists
						scope.flightSearch.$submitted = false;
						$bookingform.removeClass('submitted');

						// incase switch to multicity after roundtrip search remove the return leg
						if (scope.tripType != 0 && t === 0) {
							scope.deleteLeg(1);
						}

						scope.tripType = t;
						// for one way, make sure only one leg. Causes error on submit
						if (t === 1) {
							scope.legs = scope.legs.slice(0, 1);
						}
						scope.showChartError = false;
						//for multi-city, remove any eCert Promos if we are on the advanced search page
						if (scope.$root.selectedPromoId && scope.tripType === 0) {
							scope.$root.discountRemoved = true;
							scope.$root.selectedPromoId = null;
						} else { //if going back to round trip or one way, re-show the Promo if the model value exists
							if (scope.PromoModel && scope.PromoModel.Promo && scope.tripType !== 0 && !scope.$root.discountPermanentlyRemoved) {
								scope.$root.selectedPromoId = scope.PromoModel.Promo.OfferId;
								scope.$root.discountRemoved = false;
							}
						}
						// enable/disable update miles option
						updateMilesOption();
						scope.$broadcast('trip type changed', t);
					};

					scope.focusTripTypeSelection = function () {
						scope.expanded = true;
					};

					scope.focusBackOnTripType = function () {
						scope.recentSearches.open = false;
					};

					scope.$on('airportInputFocused', function () {
						scope.recentSearches.open = false;
					});

					scope.$on('haAlertClosed',
						function ($event, alertId) {
							if (alertId === 'promoCodeError') {
								scope.promoCodeNotFound = false;
							}

						});

					function getLeg(i, depart) {
						//Sometimes, it is a roudtrip but scope.legs DOES NOT
						//have a second leg. This makes an ugly mess where
						//we need to reverse the direction of the first leg.
						var origin = (scope.legs[i] && scope.legs[i].origin) || scope.legs[0].destination;
						var destination = (scope.legs[i] && scope.legs[i].destination) || scope.legs[0].origin;
						return {
							origin: {
								code: origin.Code,
								display: origin.DisplayName
							},
							destination: {
								code: destination.Code,
								display: destination.DisplayName
							},
							departDate: depart
						};
					}

					function getLegs(redirection) {
						var legs = [getLeg(0, scope.departDate[0])];
						if (scope.tripType === 2) {
							legs.push(getLeg(1, scope.returnDate[0]));
						}
						else if (scope.tripType === 0 && redirection) {
							for (var i = 1; i < scope.legs.length; i++) {
								legs.push(getLeg(i, scope.departDate[i]));
							}
						}
						return legs;
					}

					// Open price chart modal
					scope.openPriceChart = function (display) {
						display = display || 'calendar';
						if (scope.flightSearch.$valid) {
							haModal(haConfig.getTemplateUrl('/Book/FlightSearch/ha-flexible-price-modal.html'), {
								id: 'FlexiblePriceView',
								backdrop: 'true',
								extendScope: {
									legs: getLegs(false),
									adults: scope.pax.adultCount,
									children: scope.pax.childCount,
									display: display
								}
							});
						} else {
							scope.showChartError = true;
						}
					};

					// Add leg
					scope.addLeg = function (count) {
						$bookingform.removeClass('submitted');
						scope.flightSearch.$submitted = false;
						for (var i = 0; i < count; i++) {
							scope.leg = {};
							scope.legs.push(scope.leg);
						}
					};

					// Delete leg
					scope.deleteLeg = function (idx) {
						scope.legs.splice(idx, 1);
						scope.departDate.splice(idx, 1);
						scope.unavailableDays.splice(idx, 1);
						scope.messages.splice(idx, 1);
						scope.multicity_datepicker_configs.splice(idx, 1);
						cascadeMulticityCalendarSettings();
						checkDuplicateLegs();
						//checkContiguousLegs();
					};

					// Get calendar heading
					scope.getCalendarHeading = function (legType) {
						if ($el.context.textContent && $el.context.textContent.length > 0) {
							var departdateval = $el.context.textContent.split('\n')[4].replace(',', '');
							var returndateval = $el.context.textContent.split('\n')[13].replace(',', '');
							return (legType !== 'returnDate') ? departdateval : returndateval;
						} else {
							return (legType !== 'returnDate') ? $scs('BookingWidget.departdate') : $scs('BookingWidget.returndate');	// TODO: PULL FROM XLations
						}
					};

					// Scroll Form on home page when expanded
					scope.scrollForm = function (value) {
						// Auto-scroll near top of booking widget
						var homepageWidget = angular.element('.homepage-widget');
						if (homepageWidget.length > 0) {
							$document.scrollToElement(homepageWidget, 20, 450, function easeInOutQuart(t) {
								return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
							}).then(function () {
								scope.expanded = value;
							});
						} else {
							scope.expanded = value;
						}
					};

					// Scroll to a specified id
					scope.scrollTo = function (id) {
						// Auto-scroll to element after timeout
						$timeout(function () {
							var element = angular.element('#' + id);
							if (element.length > 0) {
								$document.scrollToElement(element, 20, 450, function easeInOutQuart(t) {
									return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
								});
							}
						}, 0);
					};

					// Check unavailable
					scope.getIsUnavailable = function (date) {

						// Before today
						if (haDateUtils.isBefore(date, today) && !haDateUtils.isSameDay(date, today) || haDateUtils.isAfter331(date, today)) {
							return true;
						}


						// Dates chosen but still not available, picking depart (must be before return), picking return (must be after depart)
						var currentDateChoice = scope.currentDateChoice;
						var isUnavail = haUnavailableDays.isUnavailable(date, scope.unavailableDays[scope.idx], currentDateChoice);
						var departDate = scope.departDate[scope.idx];
						var returnDate = scope.returnDate[scope.idx];

						if (departDate && returnDate && scope.tripType > 0) {
							//if we have both dates, just return unavailable days
							return isUnavail;
						}

						// Unavailable, before depart or after return
						return (isUnavail) ||
							((scope.tripType === 2) &&
								((currentDateChoice === 'returnDate' && departDate && haDateUtils.isBefore(date, departDate)) ||
									(currentDateChoice === 'departDate' && returnDate && haDateUtils.isAfter(date, returnDate))));
					};

					scope.infantInfoModal = function () {
						if (!scope.isCorporate || scope.isAffiliate) {
							haModal(haConfig.getTemplateUrl('ha-child-infant-info-modal.html'),
								{
									id: 'infantModal',
									backdrop: 'true'
								});
						}
						console.log(scope.isAffiliate);
					};

					scope.infantInfoContentCheck = function () {
						var infantIconHide;
						if ($scs('BookingWidget.childinformationmodal') == null || $scs('BookingWidget.childinformationmodal') === "" || $scs('BookingWidget.childinformationmodal') === "[BookingWidget.childinformationmodal]") {
							return infantIconHide = true;
						} else {
							return infantIconHide = false;
						}
					}

					// Handle flights & hotel submit action
					scope.searchFlightsPlusHotels = function (e) {
						e.preventDefault();


						// Promo code is optional even if invalid or has not been applied
						if (scope.flightSearch.PromoCode !== undefined && !scope.flightSearch.PromoCode.$valid) {
							clearPromoCodeInput();
						}

						// Ensure that the form is valid OR that the only error is the passenger count, since we want
						// to defer to the third party site's error.
						if (scope.flightSearch.$valid || (_.size(scope.flightSearch.$error) === 1 && scope.flightSearch.$error.haPassengerCount)) {

							//Set Defaults
							var childAge = 6, numRoom = 1, packageType = 'FlightHotel', cabinClass = 'e', fromTime = 362, toTime = 362;

							var postObject = {};
							var fromDate = scope.departDate[0].YYYY_MM_DD();
							var toDate = scope.returnDate[0].YYYY_MM_DD();
							var fromAirport = scope.legs[0].origin.Code;
							var destination = scope.legs[0].destination.Code;
							var adults = scope.pax.adultCount;
							var children = scope.pax.childCount;

							postObject.FromAirport = fromAirport;
							postObject.Destination = destination;
							postObject.ToTime = toTime;
							postObject.FromTime = fromTime;
							postObject.NumRoom = numRoom;
							postObject.cabinClass = cabinClass;

							//Dynamically adding key value pairs for passengers
							for (var r = 0; r < numRoom; r++) {
								var adultKey = 'NumAdult-Room' + (r + 1);
								var childCountKey = 'NumChild-room' + (r + 1);
								postObject[adultKey] = adults;
								if (children > 0) {
									postObject[childCountKey] = children;
									for (var c = 0; c < children; c++) {
										var childKey = 'Room' + (r + 1) + '-Child' + (c + 1) + 'Age';
										postObject[childKey] = childAge;
									}
								}
							}

							postObject['mdpcid'] = haUtils.webtrends.tokenV2("FLTWIDGET.PACKAGE");

							var queryString = haUtils.createQueryString(postObject);
							var url = $scs('BookingWidget.searchpackagesurl');
							var loc = url + packageType + '/' + fromDate + '/' + toDate + queryString;
							$log.debug(loc);
							location.href = loc;

						} else {
							scope.flightSearch.$submitted = true;
							$bookingform.addClass('submitted');
							scope.$broadcast('validateForm');
							return;
						}
					};

					// Validate promo code when customer presses enter key from the promo code text box
					scope.applyPromoCodeOnEnter = function ($event) {
						if ($event.which === 13) {
							$event.preventDefault();
							scope.applyPromoCode();
						}
					};

					// Handle Affiliate code when customer clicks on button to apply

					function HandleAffiliate(promoCode) {
						var isErrorResponse = false;
						var errorMessage = ''; var error = '';
						haEcertAPI.HandleAffiliates(scope.promoCode.Code)
							.then(function (response) {
								if (response.data.IsSuccess && response.data.RedirectURL != null) {
									response.data.RedirectURL = response.data.RedirectURL;
									$window.location = response.data.RedirectURL;
								}
								else {
									if (response.data.ServiceErrors != null) {
										scope.promoCodeNotFound = true;
										scope.flightSearch.PromoCode.$setValidity('promoCodeNotFound', false);
									}
									else {
										errorMessage = 'Error While Processing Request';
									}
									error(response);
								}
							}, error);
					}

					// Handle NoName PromoCode When customer clicks on apply button
					function HandleNoName(promoCode) {
						var url = "/Ecertificate?ecertId=" + promoCode;
						if (url != null) {
							$window.location = url
						}
						else {
							scope.promoCodeNotFound = true;
							scope.flightSearch.PromoCode.$setValidity('promoCodeNotFound', false);
						}
					}

					// Validate promo code when customer clicks on button to apply
					scope.applyPromoCode = function () {
						haEcertAPI.validateRedeemPromoCode(scope.promoCode.Code).success(function (results) {
							if (results && results.IsSuccess && results.PromoCodePromotion) {
								if (results && results.IsSuccess && results.PromoCodePromotion.OfferType === 10) {
									handlePromoCodeApply(results.PromoCodePromotion);
								}
								else if (results && results.IsSuccess && results.PromoCodePromotion.OfferType === 3) {
									HandleNoName(scope.promoCode.Code);
								}
								scope.flightSearch.PromoCode.$setValidity('promoCodeNotFound', true);
							}
							else if (results.PromoCodePromotion == null) {
								HandleAffiliate(scope.promoCode.Code);
								scope.flightSearch.PromoCode.$setValidity('promoCodeNotFound', true);
							}
							else {
								$log.error('applyPromoCode failed');
								scope.promoCodeNotFound = true;
								scope.flightSearch.PromoCode.$setValidity('promoCodeNotFound', false);
							}
						}).error(function (err) {
							$log.error('Validate Promo Code encountered error: ' + err);
							scope.promoCodeNotFound = true;
							scope.flightSearch.PromoCode.$setValidity('promoCodeNotFound', false);
						})
					};

					scope.removePromoCode = function () {
						scope.promoCode.hasApplied = false;
						handlePromoCodeRemove();
					};

					// Submit form
					scope.submit = function ($event) {

						$event.preventDefault();
						scope.formSubmitted = true;

						// Promo code is optional even if invalid or has not been applied
						if (!scope.Enable1AOnLegacy) {
							if (scope.flightSearch.PromoCode !== undefined && !scope.flightSearch.PromoCode.$valid) {
								clearPromoCodeInput();
							}
						}

						//check form validity
						if (!scope.flightSearch.$valid) {
							scope.$broadcast('validateForm');
							return;
						}

						//Adding exit link tracking
						var trackingDestOrig = [];
						if (scope.legs) {
							angular.forEach(scope.legs, function (lg) {
								trackingDestOrig.push(lg.origin.Code);
								trackingDestOrig.push(lg.destination.Code);
							});
						}


						//Multicity Validation
						if (scope.tripType === 0) {

							if (scope.legs.length === 1) {
								//execute 1 way
								document.flightSearch.FlightQueryTypeId.value = 1;
							} else if (scope.legs.length === 2) {
								//execute round trip or open jaw
								//check city pairs, if they match in reverse, set to round trip, otherwise set to 0 (multicity open jaw)
								document.flightSearch.FlightQueryTypeId.value = (scope.legs[0].origin.Code === scope.legs[1].destination.Code && scope.legs[0].destination.Code === scope.legs[1].origin.Code) ? 2 : scope.tripType;
							}
							//check for duplicate legs
							checkDuplicateLegs();
							//check for contiguous legs
							//checkContiguousLegs();
							//if (scope.flightSearch.$error.haContiguousLegs) {return;}
						}

						//Set miles flag based on selection
						if (scope.milesRadioButton && scope.paymentType && scope.paymentType.type !== undefined) {
							switch (scope.paymentType.type) {
								case '1': // Miles
									scope.legs[0].isMiles = true;
									if (scope.tripType === 2) {
										if (scope.legs[1] === undefined) {
											scope.legs[1] = {
												origin: scope.legs[0].destination,
												destination: scope.legs[0].origin
											};
										}
										scope.legs[1].isMiles = true;
									}
									break;
								case '2': // Dollar Miles
									scope.legs[0].isMiles = false;
									if (scope.legs[1] === undefined) {
										scope.legs[1] = {
											origin: scope.legs[0].destination,
											destination: scope.legs[0].origin
										};
									}
									scope.legs[1].isMiles = true;
									break;
								case '3': // Miles Dollars
									scope.legs[0].isMiles = true;
									if (scope.legs[1] === undefined) {
										scope.legs[1] = {
											origin: scope.legs[0].destination,
											destination: scope.legs[0].origin
										};
									}
									scope.legs[1].isMiles = false;
									break;
								default: // Dollars
									scope.legs[0].isMiles = false;
									if (scope.tripType === 2) {
										if (scope.legs[1] === undefined) {
											scope.legs[1] = {
												origin: scope.legs[0].destination,
												destination: scope.legs[0].origin
											};
										}
										scope.legs[1].isMiles = false;
									}
									break;
							}
						}

						//check form validity again after leg check
						$timeout(function () {
							if (!scope.flightSearch.$valid) {
								scope.$broadcast('validateForm');
								return;
							}

							// ETCO validation check
							if (scope.enableTCR) {
								var today = new Date();
								today.setHours(0, 0, 0, 0);

								scope.ETCOResponseModel.Errors = [];

								// Cannot be codeshare other than ohana.
								if ((scope.legs[0].origin.IsCodeShare && scope.legs[0].origin.Code !== 'MKK' && scope.legs[0].origin.Code !== 'JHM' && scope.legs[0].origin.Code !== 'LNY') ||
									(scope.legs[0].destination.IsCodeShare && scope.legs[0].destination.Code !== 'MKK' && scope.legs[0].destination.Code !== 'JHM' && scope.legs[0].destination.Code !== 'LNY')) {

									scope.ETCOResponseModel.Errors.push('ETCOErrorCodeShare');
								}

								// Today must be between BookBetween dates.
								if (haDateUtils.isBefore(today, new Date(scope.ETCOResponseModel.BookBetweenFrom)) ||
									haDateUtils.isAfter(today, new Date(scope.ETCOResponseModel.BookBetweenTo))) {

									scope.ETCOResponseModel.Errors.push('ETCOErrorNoLongerValid');
								}

								// Travel dates must be between TravelPeriod dates.
								if (haDateUtils.isBefore(scope.departDate[0], new Date(scope.ETCOResponseModel.TravelPeriodFrom)) ||
									(scope.tripType === 1 && haDateUtils.isAfter(scope.departDate[0], new Date(scope.ETCOResponseModel.TravelPeriodTo))) ||
									(scope.tripType === 2 && haDateUtils.isAfter(scope.returnDate[0], new Date(scope.ETCOResponseModel.TravelPeriodTo)))) {

									scope.ETCOResponseModel.Errors.push('ETCOErrorDateRange');
								}

								// show validation error modal if any error
								if (scope.ETCOResponseModel.Errors.length > 0) {

									// get error message from sitecore
									$scs.get('ReservationsTravelCreditRedemption').then(function (data) {
										angular.forEach(scope.ETCOResponseModel.Errors, function (error, index) {
											if (data.hasOwnProperty(error.toLowerCase())) {
												scope.ETCOResponseModel.Errors[index] = data[error.toLowerCase()];
											}
										});

										haModal(haConfig.getTemplateUrl('ha-booking-etco-modal.html'), {
											id: 'promoValidationModal',
											backdrop: 'true',
											scope: scope
										});
									});

									return;
								}
							}
							if (!scope.Enable1AOnLegacy) {
								// Ecert validation check
								if (scope.tripType !== 0 && scope.PromoModel && scope.PromoModel.Promo && !scope.$root.discountRemoved) {
									var promoEligibilityRequest = {
										PromoCode: scope.PromoModel.Promo.OfferId,
										Origin: scope.legs[0].origin.Code,
										Destination: scope.legs[0].destination.Code,
										DepartureDate: $filter('date')(scope.departDate[0], 'yyyy-MM-dd'),
										ReturnDate: scope.returnDate[0] ? $filter('date')(scope.returnDate[0], 'yyyy-MM-dd') : null,
										TripType: scope.tripType,
										AdultCount: (!!scope.flightSearch.AdultCount && !!scope.flightSearch.AdultCount.$modelValue) ? scope.flightSearch.AdultCount.$modelValue : 0,
										ChildCount: (!!scope.flightSearch.ChildCount && !!scope.flightSearch.ChildCount.$modelValue) ? scope.flightSearch.ChildCount.$modelValue : 0,
										InfantCount: (!!scope.flightSearch.InfantCount && !!scope.flightSearch.InfantCount.$modelValue) ? scope.flightSearch.InfantCount.$modelValue : 0
									};

									scope.ActivateValidationSpinner = true;
									scope.PromoModel.Errors = null;
									haEcertAPI.isValidPromo(promoEligibilityRequest).success(function (results) {
										if (!!results && results.IsSuccess === false) {
											scope.ActivateValidationSpinner = false;
											scope.PromoModel.Errors = results.Errors;
											haModal(haConfig.getTemplateUrl('ha-booking-promo-modal.html'), {
												id: 'promoValidationModal',
												backdrop: 'true',
												scope: scope
											});
										} else {
											scope.ActivateValidationSpinner = false;
											$timeout(function () {
												//document.flightSearch.submit();
												submitFlightSearchRequest();
											});
										}
									})
										.error(function (err) {
											scope.ActivateValidationSpinner = false;
											if (err && err.RedirectURL) {
												window.location.href = err.RedirectURL;
											}
											return;
										});
								}
								else if (scope.promoCode != undefined && scope.promoCode.Code != undefined && scope.promoCode.Code != "" && !scope.promoCode.hasApplied) {
									$timeout(function () {
										haModal(haConfig.getTemplateUrl('ha-booking-promo-modal.html'), {
											id: 'promoValidationModal',
											backdrop: 'true',
											scope: scope
										});
									});
								}
								else {
									scope.$root.discountRemoved = true;
									scope.$root.selectedPromoId = null;
									$timeout(function () {
										//document.flightSearch.submit();
										submitFlightSearchRequest();
									});
								}
							}
							else {
								scope.ActivateValidationSpinner = false;
								$timeout(function () {
									//document.flightSearch.submit();
									submitFlightSearchRequest();
								});
							}
						});

					};

					// Submit form without promo
					scope.submitWithoutDiscount = function () {
						scope.$root.discountRemoved = true;
						scope.$root.selectedPromoId = null;
						clearPromoCodeInput();
						scope.$modalCancel();
						$timeout(function () {
							// document.flightSearch.submit();
							submitFlightSearchRequest();
						});
					};

					// Remove ETCO before submitting
					scope.submitWithoutETCO = function () {
						scope.etcoRemoved = true;
						scope.$modalCancel();
						$timeout(function () {
							//document.flightSearch.submit();
							submitFlightSearchRequest();
						});
					};

					// Recall Search
					scope.recallSearch = function (idx) {
						var flightQueryCookie = scope.flightQueryCookieArr[idx];
						apply(getSearchFromCookie(flightQueryCookie));
						scope.recent = false;
					};

					// Expand home page widget
					scope.setExpanded = function () {
						scope.expanded = true;
						//scope.scrollForm(scope.expanded);
						$('.booking-widget.row').addClass('open');
					};

					// Contract home page widget
					scope.setClosed = function () {
						scope.setTripType(2);
						scope.expanded = false;
						$('.booking-widget.row').removeClass('open');
					};

					// menu select for mobile
					scope.mobileSelect = function (tabName) {
						if (scope.tab === tabName) {
							scope.tab = '';
						} else {
							scope.tab = tabName;
						}
					};

					scope.openMobileRecentSearches = function () {
						haModal(haConfig.getTemplateUrl('/Book/FlightSearch/ha-recent-searches-modal.html'), {
							id: 'RecentSearchesModal',
							backdrop: 'true',
							extendScope: {
								flightQueryCookieArr: scope.flightQueryCookieArr,
								recallSearch: scope.recallSearch
							}
						});
					};

					function checkForAttributes() {
						// Set attributes
						['departureDate', 'arrivalDate', 'departureLocation', 'arrivalLocation']
							.forEach(function (prop) {
								scope[prop] = attrs[prop] || '';
							});

						// Default values for child and adult count
						scope.childrenCount = attrs.childrenCount || 0;
						scope.adultsCount = attrs.adultsCount || 1;

						scope.roundTripFlight = attrs.roundTripFlight != null && attrs.roundTripFlight === '1';
						// Set promo code value if attribute is set
						if (attrs.promoCodeValue) {
							scope.promoCode = {};
							scope.promoCode.Code = attrs.promoCodeValue;
						}
						// Check to see if any of the flight information attributes are set
						if (scope.departureDate || scope.arrivalDate || scope.departureLocation || scope.arrivalLocation || attrs.promoCodeValue) {
							scope.searchAttributesSet = true;
							return true;
						} else {
							scope.searchAttributesSet = false;
							return false;
						}
					}

					function getSearchFromAttributes() {
						var legs = [];
						var departureLeg = {
							origin: { code: scope.departureLocation.toUpperCase() },
							destination: { code: scope.arrivalLocation.toUpperCase() },
							departDate: scope.departureDate ? moment(scope.departureDate.substr(0, 10), "YYYY-MM-DD").toDate() : undefined
						};
						legs.push(departureLeg);
						// Check if it is round trip or one way
						if (scope.roundTripFlight === true) {
							var returnLeg = {
								origin: { code: scope.arrivalLocation.toUpperCase() },
								destination: { code: scope.departureLocation.toUpperCase() },
								departDate: scope.arrivalDate ? moment(scope.arrivalDate.substr(0, 10), "YYYY-MM-DD").toDate() : undefined
							};
							legs.push(returnLeg);
						}
						var search = buildQuery(legs, scope.adultsCount, scope.childrenCount);
						return search;
					};

					function getSearchFromQuery() {
						var legs = haUtils.parseLegs(haUtils.querystring('l'));
						return legs && buildQuery(legs, haUtils.querystring('a'), haUtils.querystring('c'));
					}

					//also used to process an item from the Recent Searches cookie
					function getSearchFromCookie(cookie) {
						if (!cookie) {
							return;
						}

						var legs = cookie.FlightSearchSegmentList.map(function (leg) {
							return {
								origin: { code: leg.OriginCityCode.toUpperCase() },
								destination: { code: leg.DestinationCityCode.toUpperCase() },
								departDate: moment(leg.DepartureDate.substr(0, 10), "YYYY-MM-DD").toDate(),
								miles: leg.IsMiles
							};
						});

						var search = buildQuery(legs, cookie.AdultCount, cookie.ChildCount);
						search.refundable = cookie.IsRefundable;
						return search;
					}

					function buildQuery(legs, adults, children) {
						// need to fix this to have an override if the attribute is set already.
						var isRoundTrip = legs.length === 2 && legs[0].origin.code === legs[1].destination.code;
						var noPassengers = !parseInt(adults, 10) && !parseInt(children, 10);
						return {
							legs: clearPastDates(legs),
							adults: noPassengers ? 1 : (parseInt(adults, 10) || 0),
							children: parseInt(children, 10) || 0,
							tripType: isRoundTrip ? 2 : (legs.length === 1 ? 1 : 0)
						};
					}

					function clearPastDates(legs) {
						// Ignore dates that are in the past if clearPastDates is set
						return legs.map(function (leg) {
							var departMoment = moment(leg.departDate);
							if (departMoment.diff(moment().startOf('day')) < 0) {
								leg.departDate = null;
							}
							return leg;
						});
					}

					function hideBookingTabs() {
						angular.element('a[href^="/book/flights"]').parent().addClass('ng-hide');
						angular.element('a[href^="/book/hotels"]').parent().addClass('ng-hide');
						angular.element('a[href^="/book/car-rentals"]').parent().addClass('ng-hide');
						angular.element('a[href^="/book/vacation-packages"]').parent().addClass('ng-hide');
						angular.element('a[href^="/book/activities-and-cruises"]').parent().addClass('ng-hide');
					}

					function showBookingTabs() {
						angular.element('a[href^="/book/flights"]').parent().removeClass('ng-hide');
						angular.element('a[href^="/book/hotels"]').parent().removeClass('ng-hide');
						angular.element('a[href^="/book/car-rentals"]').parent().removeClass('ng-hide');
						angular.element('a[href^="/book/vacation-packages"]').parent().removeClass('ng-hide');
						angular.element('a[href^="/book/activities-and-cruises"]').parent().removeClass('ng-hide');
					}

					function handlePromoCodeApply(promoCodePromotion) {
						scope.promoCode.isValidPromoCode = true;
						scope.promoCode.hasApplied = true;
						scope.promoCode.PromoCodePromotion = promoCodePromotion; // todo: lose this (new value) in the merge
						scope.PromoModel.Promo = promoCodePromotion;
						hideBookingTabs();
						$scs.get('Header.promoappliedmessage').then(function (data) {
							var markup = '<div ha-global-message header="' + data + '" type="success" message-close-time="4700"></div>';
							angular.element('[ha-global-header]').append($compile(markup)(scope));
						});
						scope.promoCodeNotFound = false;
						scope.$root.discountRemoved = false;
						scope.$root.discountPermanentlyRemoved = false;
						scope.$root.selectedPromoId = scope.PromoModel.Promo.OfferId;
					}

					function handlePromoCodeRemove() {
						scope.promoCode.isValidPromoCode = false;
						scope.promoCode.Code = null;
						showBookingTabs();
						// original behavior
						scope.$root.discountRemoved = true;
						scope.$root.discountPermanentlyRemoved = true;
						scope.$root.selectedPromoId = null
						// for promo codes: ajax call to server side method to remove promo code from session
						haEcertAPI.removePromoCodeFromSession();
					}

					function clearPromoCodeInput() {
						scope.promoCode.Code = null;
						scope.promoCodeNotFound = false;
						if (scope.flightSearch.PromoCode !== null && scope.flightSearch.PromoCode !== undefined) {
							scope.flightSearch.PromoCode.$setValidity('promoCodeNotFound', true);
							scope.flightSearch.PromoCode.$validate();
						}
					}

					function submitFlightSearchRequest() {
						var flightSearchQuery = buildSearchRedirectQuery();
						haHttpService.POST('/Book/Home/GetFlightSearchRedirectPath', flightSearchQuery).success(function (results) {
							if (!!results && results.Success === false) {
								$timeout(function () {
									document.flightSearch.submit();
								});
							} else {
								var data = results.RedirectUrl;

								//Get Adobe Visitor ID and append to redirect url
								if (typeof (Visitor) !== 'undefined') {
									let adobeVisitor = Visitor.getInstance("5E29123F5245B2B70A490D45@AdobeOrg", {
										trackingServer: "hawaiianairlines.sc.omtrdc.net",
										trackingServerSecure: "hawaiianairlines.sc.omtrdc.net",
										marketingCloudServer: "hawaiianairlines.sc.omtrdc.net",
										marketingCloudServerSecure: "hawaiianairlines.sc.omtrdc.net"
									});
									data = adobeVisitor.appendVisitorIDsTo(data);
								}

								if (typeof data === "string" && data !== "") {
									window.location.href = data;
								}
								else {
									document.flightSearch.submit();
								}
							}
						}).error(function (err) {
							document.flightSearch.submit();
						});
					}

					function buildSearchRedirectQuery() {
						var searchQuery = {};
						var legs = getLegs(true);
						searchQuery.tripType = scope.tripType;
						searchQuery.pricingType = scope.paymentType.type;
						searchQuery.isRefundable = scope.flightSearch.IsRefundableCheck;
						searchQuery.isflexibleDatesCalendar = scope.flightSearch.flexibleDatesCalendar !== undefined ? scope.flightSearch.flexibleDatesCalendar.$viewValue : false;
						searchQuery.segments = [];
						for (var i = 0; i < legs.length; i++) {
							var leg = {};
							leg.originCityCode = legs[i].origin.code;
							leg.destinationCityCode = legs[i].destination.code;
							leg.departureDate = moment(legs[i].departDate).format("YYYY-MM-DD");
							searchQuery.segments.push(leg);
						}
						searchQuery.promoCode = scope.promoCode !== undefined ? scope.promoCode.Code : scope.flightSearch.PromoCode != undefined ? scope.flightSearch.PromoCode.$modelValue : "";
						searchQuery.currency = scope.$currency;
						searchQuery.adultCount = scope.pax.adultCount;
						searchQuery.childCount = scope.pax.childCount;
						return searchQuery;
					}

					// INITIALIZATION
					//*******************
					function initialize() {

						// Promo Model
						haGlobals('promoJson', function (promoJson) {
							scope.PromoModel = promoJson || undefined;  // todo: old, stays in the merge and new html refers to this

							scope.promoCode = {};// todo: new, goes away in the merge and nobody uses
							scope.promoCodeNotFound = false;// todo: new, goes away in the merge and nobody uses
							scope.promoCode.PromoCodePromotion = scope.PromoModel.Promo; // todo: new, goes away in the merge and nobody uses
							if (scope.PromoModel !== null && scope.PromoModel.Promo !== null) {
								scope.$root.selectedPromoId = scope.PromoModel.Promo.OfferId;
								if (scope.PromoModel.Promo.OfferType === 10) {
									handlePromoCodeApply(scope.PromoModel.Promo);
								}
							}
						});

						haGlobals('enableTCR', function (enableTCR) {
							scope.enableTCR = enableTCR;
						});

						haGlobals('wholesaleDisableDollarMile', function (wholesaleDisableDollarMile) {
							scope.wholesaleDisableDollarMile = wholesaleDisableDollarMile;
						});

						haGlobals('ETCOResponseModel', function (etcoResponseModel) {
							scope.ETCOResponseModel = etcoResponseModel;
						});
						haGlobals('flightSearchModel', function (flightSearchModel) {
							scope.disableAdultFieldForNITP = flightSearchModel.DisableAdultField;
						});
						haGlobals('widgetDatepickerAlertIcon', function (widgetDatepickerAlertIcon) {
							scope.widgetDatepickerAlertIcon = widgetDatepickerAlertIcon;
						});

						scope.ActivateValidationSpinner = false;
						scope.formSubmitted = false;

						// Booking Form Options
						setAttributes();

						// Market
						scope.intl = false;
						scope.isEN = haUtils.isEN();

						// Legs
						scope.legs = [];
						var leg = {};
						scope.legs.push(leg);

						// Dates
						scope.departDate = [];
						scope.returnDate = [];
						scope.currentDateChoice = '';

						// Calendar
						scope.calendarOpen = [];
						scope.messages = [];

						// Unavailable Days
						scope.unavailableDays = [];

						// Passenger stuff
						scope.pax = {};
						scope.passengerCount = [0, 1, 2, 3, 4, 5, 6, 7];
						scope.childrenOnly = false;

						scope.isAffiliate = false;
						if (scope.$root.user && scope.$root.user.accountType == 'A') {
							scope.isAffiliate = true;
						}

						scope.isCorporate = scope.$root.isLoggedIn && scope.$root.corpAccTypes.indexOf(scope.$root.user.accountType) > -1;

						// Miles radio button
						scope.paymentType = {};

						scope.enablepricechart = scope.$switch('LowFare:enablepricechart');
						scope.enablepricecalendar = scope.$switch('LowFare:enablepricecalendar');

						if (scope.ETCOResponseModel) {
							$scs.get('ReservationsTravelCreditRedemption.offallroutesfootertext').then(function (txt) {
								var amount = $filter('localCurrency')(scope.ETCOResponseModel.Amount, scope.$currency);
								scope.offAllRoutesFooterText = txt.replace('{{ETCO.Dollar.Discounts}}', amount);
							});
						}

						var autoOpen = window.location.href.toLowerCase().indexOf('flexibleautoopen') !== -1;
						var search;

						// If autoopen url param set, ensure that the cookie can NOT be used
						if (!autoOpen) {
							search = checkForAttributes() ? getSearchFromAttributes() : getSearchFromQuery() || getSearchFromCookie(haUtils.getFlightQueryModelCookie());
						} else {
							search = getSearchFromAttributes() || getSearchFromQuery();
						}

						scope.promoCodeMaxLength = 15;

						var promoCodeToApplyInUrl = haUtils.querystring('pcodeapply');

						if (promoCodeToApplyInUrl && promoCodeToApplyInUrl.length > 0) {

							scope.promoCode.Code = promoCodeToApplyInUrl.substring(0, scope.promoCodeMaxLength);

							if (scope.$root.$regex.promoCode.test(scope.promoCode.Code)) {
								scope.applyPromoCode();
							}
						}
						else {
							var promoCodeInUrl = haUtils.querystring('pcode');
							if (promoCodeInUrl && promoCodeInUrl.length > 0) {
								scope.promoCode.Code = promoCodeInUrl.substring(0, scope.promoCodeMaxLength);
							}
						}
						var promoCodeUrl = haUtils.querystring('promoCode');
						if (promoCodeUrl && promoCodeUrl.length > 0) {
							scope.promoCode = {};
							scope.promoCode.Code = promoCodeUrl.substring(0, scope.promoCodeMaxLength);
						}
						$timeout(function () {
							if (scope.searchAttributesSet) {
								applyWithAttributes(search);
							} else {
								apply(search, true);
							}
							processRecentSearches();
							scope.$broadcast('trip type changed', scope.tripType);
						}, 0);

						if (autoOpen) {
							//getcities getscalled twise, so protect against two instances
							scope.autoOpened = false;
							scope.$on('citiesavailable', function () {
								if (!scope.autoOpened) {
									scope.autoOpened = true;
									$timeout(function () {
										scope.openPriceChart();
									});
								}
							});
						}


						scope.ready = true;
						$(window).on('load', function () {
							const urlParams = new URLSearchParams(window.location.search);
							const myParam = urlParams == null ? false : urlParams.get('multiCitySearchError');
							scope.isContiguousError = false;
							scope.isNoFlightsError = urlParams == null ? false : urlParams.get('noFlightsFound');
							if (myParam == 'true') {
								scope.setTripType(0);
								scope.focusTripTypeSelection();
								scope.isContiguousError = true;
							}
						});

						// Lazy load flight results strings if routed through Akamai
						if (HA.cdnDynamic) {
							$(window).on('load', function () {
								['BookingWidget', 'StickProgressBar', 'LowFare', 'InflightOptions', 'InFlightOptionsInfo', 'PassengerTripSummary', 'FareSearch', 'PROMO_DISCOUNTS', 'ETCO_CHECK'].map($scs.request);
							});
						}

					}

					initialize();
				}
			};
		}]);

	// DATE INPUT FIELDS
	//module.directive('haBookingDateInput', ['haTemplateCache', 'haConfig', function (haTemplateCache, haConfig) {

	//	// Preload
	//	haTemplateCache.get(haConfig.getTemplateUrl('ha-calendar2.html'));
	//	haTemplateCache.get(haConfig.getTemplateUrl('ha-booking-day-template.html'));
	//	haTemplateCache.get(haConfig.getTemplateUrl('Book/FlightResults/ha-flightresults-selectedsegments.html'));
	//	haTemplateCache.get(haConfig.getTemplateUrl('Book/FlightResults/ha-flightresults-currentsegment.html'));
	//	haTemplateCache.get(haConfig.getTemplateUrl('Book/FlightResults/ha-flightresults-itemendonendwithprice.html'));
	//	haTemplateCache.get(haConfig.getTemplateUrl('Book/FlightResults/ha-flightresults-faregrid.html'));

	//	return {
	//		templateUrl: haConfig.getTemplateUrl('ha-booking-date-input.html'),
	//		scope: {
	//			inputModel: '=',
	//			calendarOpen: '=',
	//			currentDateChoice: '=',
	//			inputName: '@',
	//			displayName: '@',
	//			label: '@',
	//			placeholder: '@',
	//			id: '@',
	//			disabled: '=',
	//			ngRequired: '=',
	//			errorMessage: '@',
	//			idx: '='
	//		},
	//		link: function (scope, element) {

	//			var called = false;	// This is because focus is called twice - TODO - figure out why
	//			//if no displayName passed through, just create one.
	//			scope.displayName = scope.displayName ? scope.displayName : scope.inputName + 'Display';
	//			scope.onFocus = function () {

	//				if (!called) {
	//					scope.currentDateChoice = (scope.id).indexOf('departDate') >= 0 ? 'departDate' : 'returnDate';
	//					scope.$emit('dateInputFocused', scope.currentDateChoice, scope.idx);
	//					called = true;
	//				}
	//			};

	//			scope.onBlur = function () {
	//				scope.$emit('dateInputBlurred', element);
	//				called = false;
	//			};

	//			scope.isActive = function () {
	//				return (scope.id.indexOf(scope.currentDateChoice) > -1 && scope.calendarOpen[scope.idx]);

	//			};
	//		}
	//	};
	//}]);

	// FORMATTER FOR DATE INPUTS
	module.directive('haBookingDateFormatter', ['$filter', '$locale', function ($filter, $locale) {
		return {
			require: 'ngModel',
			link: function (scope, element, attrs, ngModelCtrl) {
				ngModelCtrl.$formatters.push(function (value) {
					if (!(value) || !(value instanceof Date)) {
						return '';
					}
					return $filter('date')(value, 'EEEE, ') + $filter('date')(value, $locale.DATETIME_FORMATS.shortDate);
				});
			}
		};
	}]);

	module.directive('haDatesAvailable', ['haUnavailableDays', function (haUnavailableDays) {
		return {
			restrict: 'A',
			require: 'ngModel',
			link: function ($scope, $el, $attrs, ngModel) {

				var validate = function (viewValue, unavailableDays) {

					var isValid = true;

					if (!unavailableDays || !unavailableDays.length) {
						//if there is no unavailableDays array, just return valid
						ngModel.$setValidity('haDates', isValid);
						return isValid;
					}

					if (ngModel.$modelValue) {
						if ($scope.tripType !== 1) {
							isValid = !haUnavailableDays.isUnavailable(ngModel.$modelValue, unavailableDays, 'departDate') || !haUnavailableDays.isUnavailable(ngModel.$modelValue, unavailableDays, 'returnDate');
						} else {
							isValid = !haUnavailableDays.isUnavailable(ngModel.$modelValue, unavailableDays, 'departDate');
						}
					}


					ngModel.$setValidity('haDates', isValid);

					return viewValue;
				};

				ngModel.$formatters.push(validate);

				$scope.$on('unavailableDaysChanged', function (e, unavailableDays) {
					return validate(ngModel.$viewValue, unavailableDays);
				});
			}
		};
	}]);

	module.factory('haUnavailableDays', ['haHttpService', '$q', function (haHttpService, $q) {
		var unavailable = {};

		function contains(date) {
			/*jshint validthis:true */
			var data = this; //jscs:ignore safeContextKeyword
			return (data = data && data[date.getFullYear()]) && (data = data[date.getMonth() + 1]) && data[date.getDate()];
		}

		function getUnavailableDays(pair, type) {
			pair = pair.split('-');
			return haHttpService.GET('/book/flightschedule/UnAvailableDaysOfOperation', {
				method: 'get',
				params: {
					origin: pair[0],
					destination: pair[1],
					triptype: type || 1
				},
				config: { cache: true }
			});
		}

		return {
			get: function (pair) {
				var promise = unavailable[pair];
				if (promise) {
					return promise;
				}

				return (unavailable[pair] = getUnavailableDays(pair)
					.then(function (response) {
						var data = response.data[pair] || { CalendarYears: {} };
						data.CalendarYears.pair = pair;
						data.CalendarYears.contains = contains;
						return data.CalendarYears;
					})
					.catch(function (err) {
						console.log(err);
						return Promise.reject(err);
					}));
			},

			//legacy code for old datepickers...
			getUnavailableDays: function (leg, $scope) {
				return $q.all([
					this.get(pair(leg), true),
					this.get(pair(leg, true), true)
				])
					.then(function (unavailableDays) {
						unavailableDays = unavailableDays.map(function (ud) {
							return { CalendarYears: ud };
						});
						$scope.$broadcast('unavailableDaysChanged', unavailableDays);
						return unavailableDays;//pass to the next in the chain
					});
			},

			isUnavailable: function (date, unavailableDays, currentDateChoice) {

				if (!unavailableDays || !unavailableDays.length) {
					return;
				}
				//check unavailableDays array
				var ud = (currentDateChoice === 'departDate') ? unavailableDays[0] : unavailableDays[1];
				return ud.CalendarYears.contains(date);
			}
		};
	}]);

	// CLICK DELEGATE DIRECTIVE
	module.directive('haDelegateClick', function () {
		return function ($scope, element, attrs) {
			var fn = attrs.haDelegateClick;
			element.on('click', attrs.haDelegateSelector, function (e) {
				var $target = $(e.target);
				var $dataElement = $target.data('ng-json') ? $target : $target.closest('[data-ng-json]');
				var data = angular.fromJson($dataElement.data('ngJson') || undefined);

				if (typeof $scope[fn] === 'function') {
					$scope[fn](e, data);
				}
			});
		};
	});

	//get an airport pair string from a leg. e.g. "LAX-HNL"
	function pair(leg, reverse) {
		if (!leg || !leg.origin || !leg.origin.Code || !leg.destination || !leg.destination.Code) {
			return;
		}
		return reverse ? (leg.destination.Code + '-' + leg.origin.Code) : (leg.origin.Code + '-' + leg.destination.Code);
	}
})(angular);
;
(function (angular) {

	// ECERT / ETCO
	// --------------------------------------------
	//
	// * **Class:** haEcertEtcoModule
	// * **Author:** Mari Yamaha / Travis Sisti
	//
	// Interchangeable ecert and etco redemption

	'use strict';

	var module = angular.module('haEcertEtcoModule', ['haBookNonMemberEcertAPI']);

	module.directive('haEcertEtco', ['haGlobals', '$timeout', 'haBookNonMemberEcertAPI', function (haGlobals, $timeout, haBookNonMemberEcertAPI) {

		var HaEcertEtcoController = function ($scope, $attrs, $location) {

			// initialize data
			$scope.step = 0; // for determining what step screen to show
			$scope.IsEcertError = false; // for showing Ecert Error
			$scope.IsAccountError = false; // for showing Account Error
			$scope.IsDepositError = false; // for showing Deposit Error
			$scope.showMilesLogin = false; // for toggling miles login elements
			$scope.ecertChecking = false; // for showing loading animation for Ecert
			$scope.accountChecking = false; // for showing loading animation for account

			$scope.Ecert = {};

			// pre-fill ecert/etco number and redemption code (if specified in the query string)
			var urlVars = [];
			var hashes = $location.absUrl().slice($location.absUrl().indexOf('?') + 1).split('&');

			angular.forEach(hashes, function (hash) {
				var hashArray = hash.split('=');
				if (hashArray[0] && hashArray[1]) {
					urlVars[hashArray[0]] = hashArray[1];
				}
			});

			$scope.Ecert.EcertNumber = urlVars.ecertId || '';
			$scope.Ecert.RedemptionCode = urlVars.redemptionCode || '';

			// determine promotion type (ecert, etco, or award miles)
			function evaluateMemberPromotion(results) {

				if (results.IsOfferValid && results.MemberPromotion) {

					if (results.MemberPromotion.OfferType === 3) {

						// console.log('valid ecert');

						$scope.ecertValid = true;
						// redirect to book/home
						$timeout(function () {
							window.location.href = '/Book/home?ecertId=' + $scope.Ecert.EcertNumber;
						}, 200);

					} else if (results.IsOfferValid && results.MemberPromotion && results.MemberPromotion.OfferType === 8) {

						// console.log('valid etco');

						$scope.ecertValid = true;
						// redirect to book/home
						$timeout(function () {
							window.location.href = '/Book/home?ecertId=' + $scope.Ecert.EcertNumber;
						}, 200);

					} else if (results.MemberPromotion.OfferType === 9) {

						// console.log('valid award miles');

						$scope.ecertValid = true;
						$scope.showMilesLogin = true;

						// map response
						$scope.Ecert.Description = results.MemberPromotion.OfferDescription;
						$scope.Ecert.SecondaryDescription = results.MemberPromotion.OfferSecondaryDescription;
						$scope.Ecert.PartnerCode = results.MemberPromotion.HMAwardCode;
						$scope.Ecert.AwardMiles = results.MemberPromotion.AwardMiles;

						// reset form validation
						$scope.ECertForm.$submitted = false;
						$('form[name="ECertForm"]').removeClass('submitted');

					} else {
						alertInvalid();
					}

				} else {
					alertInvalid();
				}
			}

			function alertInvalid() {
				$scope.IsEcertError = true;
				angular.element('html,body').animate({scrollTop: $scope.contentScrollTop + 'px'}, 'fast');
			}

			function finalizeValidateEcert() {
				$scope.IsEcertError = false;
				$scope.ecertChecking = true;

				haBookNonMemberEcertAPI.validatePromo($scope.Ecert).success(function (results) {

					if ($scope.handleExceptions(results, 'haBookNonMemberEcertAPI-validatePromo')) {
						return;
					}

					if ( typeof $scope.$modalCancel == 'function' ) {
						$scope.$modalCancel();
					}

					$scope.ecertChecking = false;

					evaluateMemberPromotion(results);

				}).error(function () {
					$scope.handleExceptions('jsError', 'haBookNonMemberEcertAPI-validatePromo');
				});
			}

			$scope.setForm = function (form) {
				$scope.ECertForm = form;
			};

			$scope.submit = function () {
				if ($scope.step === 0) {
					if ($scope.showMilesLogin) {
						$scope.validateAccount();
					} else {
						$scope.validateEcert();
					}
				} else if ($scope.step === 1) {
					$scope.depositMiles();
				}
			};

			$scope.validateEcert = function () {
				$scope.$broadcast('validateForm');

				if (!$scope.ECertForm.$valid) {
					return false;
				}

				haBookNonMemberEcertAPI.isGAFETCO($scope.Ecert).success(function (results) {
					if ($scope.handleExceptions(results, 'haBookNonMemberEcertAPI-isGAFETCO')) {
						return;
					}
					$scope.Ecert.IsGAFTravelCredit = (results && results.IsGAFETCO);

					if ($scope.Ecert.IsGAFTravelCredit) {
						// display terms modal with confirmation for etco
						haModal('modalContent', {
							backdrop:'true',
							modalClass:'ha-content-modal',
							id:'etco-terms-modal',
							scope: $scope
						});
					} else {
						// simply proceed with validation (_without_ terms) for ecerts and award miles
						finalizeValidateEcert();
					}
				});
			};

			$scope.validateAccount = function () {
				$scope.$broadcast('validateForm');

				if (!$scope.ECertForm.$valid) {
					return false;
				}

				$scope.IsAccountError = false;
				$scope.accountChecking = true;

				haBookNonMemberEcertAPI.validateAccount($scope.Ecert).success(function (results) {
					if ($scope.handleExceptions(results, 'haBookNonMemberEcertAPI-validateAccount')) {
						return;
					}
					$scope.accountChecking = false;

					if (results.IsHMNumberValid) {

						// map response
						$scope.Ecert.FirstName = results.FirstName;
						$scope.Ecert.LastName = results.LastName;
						$scope.Ecert.CostMiles = results.CostPerMile;
						$scope.Currency = results.CurrencyCode || 'USD';

						$scope.step = 1;

						// reset form validation
						$scope.ECertForm.$submitted = false;
						$('form[name="ECertForm"]').removeClass('submitted');

						// scroll to top of main content
						angular.element('html,body').animate({scrollTop: $scope.contentScrollTop + 'px'}, 'fast');
					}
					else {
						$scope.IsAccountError = true;

						// scroll to top of main content
						angular.element('html,body').animate({scrollTop: $scope.contentScrollTop + 'px'}, 'fast');
					}
				}).error(function () {
					$scope.handleExceptions('jsError', 'haBookNonMemberEcertAPI-validateAccount');
				});
			};

			$scope.changeRecipient = function () {
				$scope.IsDepositError = false;
				$scope.step = 0;
			};

			$scope.depositMiles = function () {
				$scope.$broadcast('validateForm');
				if (!$scope.ECertForm.$valid) {
					return false;
				}

				$scope.IsDepositError = false;

				haBookNonMemberEcertAPI.depositMiles($scope.Ecert).success(function (results) {
					if ($scope.handleExceptions(results, 'haBookNonMemberEcertAPI-depositMiles')) {
						return;
					}

					if (results.IsMilesDeposited) {
						$scope.step = 2;
						// scroll to top of main content
						angular.element('html,body').animate({scrollTop: $scope.contentScrollTop + 'px'}, 'fast');
					} else {
						$scope.IsDepositError = true;
					}

				}).error(function () {
					$scope.handleExceptions('jsError', 'haBookNonMemberEcertAPI-depositMiles');
				});
			};

			$scope.acceptTerms = function() {
				finalizeValidateEcert();
			};

			$scope.cancelTerms = function() {
				$scope.$modalCancel();
			};

			$scope.handleExceptions = function (result) {
				if (result !== 'jsError') {
					if (result.ErrorCodeHandle !== undefined) {
						window.location.href = result.RedirectURL + '?ErrorCode=' + result.ErrorCodeHandle;
					} else {
						return false;
					}
				} else {
					window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
				}
			};

			haGlobals(['redemptionInformationLinks', 'memberPromotionResponse'], function (redemptionInformationLinks, memberPromotionResponse) {
				$scope.redemptionLinks = redemptionInformationLinks;

				if (memberPromotionResponse) {
					evaluateMemberPromotion(memberPromotionResponse);
				}
			});
		};

		HaEcertEtcoController.$inject = ['$scope', '$attrs', '$location'];

		var HaEcertEtcoLink = function ($scope) {

			// get top position of main content area so we know where to scroll the user to when selecting flights
			$timeout(function () {
				$scope.contentScrollTop = angular.element('.main-content').offset().top;
			}, 1000);

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaEcertEtcoLink,
			controller: HaEcertEtcoController
		};
	}]);
})(angular);
;
(function (angular) {

	// Ha Book Insufficient Miles
	// --------------------------------------------
	//
	// * **Class:** HaBookInsufficientMiles
	// * **Author:** Mari Yamaha
	//
	// A page that notifies you of additional miles you will need to purchase.

	'use strict';

	var module = angular.module('haBookInsufficientMilesModule', []);

	module.directive('haBookInsufficientMiles', ['haGlobals', function (haGlobals) {

		var HaBookInsufficientMilesController = function ($scope) {

			haGlobals('MilesBookingSummaryVM', function (MilesBookingSummaryVM) {
				$.extend($scope, MilesBookingSummaryVM);
			});

			$scope.$emit('$haBookInsufficientMilesReady');
		};

		HaBookInsufficientMilesController.$inject = ['$scope'];

		var HaBookInsufficientMilesLink = function () {

			// set height of columns to match
			setTimeout(function () {
				$('.list').each(function () {
					var colHeight = $(this).find('.value').height();
					$(this).find('.label').height(colHeight + 'px');
				});
			}, 10);

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaBookInsufficientMilesLink,
			controller: HaBookInsufficientMilesController
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haFlexiblePriceViewModule', ['haPriceApiModule', 'ngAnimate', 'haFeatureFlagsModule']);

	// Flag to keep track of animation status
	var animating = false;
	var CEILING_FACTOR = 0.33;            // Proportion of max price to pad the top of the chart with
	var MAX_DAYS = 331;                   // How far into the future we can scroll
	var VISIBLE_DAYS_COUNT = 25;          // How many days are shown at a time.
	var MOVE_TIMEOUT_MILLESECONDS = 1000; // How long to wait before assuming the user is done moving.
	var moveTimeout;                      // Holds the timeout object
	var monthKeys = ['januarytext', 'februarytext', 'marchtext', 'apriltext', 'maytext', 'junetext', 'julytext', 'augusttext', 'septembertext', 'octobertext', 'novembertext', 'decembertext'];


	module.directive('haFlexiblePriceView', [
		'haFeatureFlags',
		'haSitecoreStrings',
		'haHttpService',
		'haPriceApiService',
		'haConfig',
		'haDateUtils',
		'$filter',
		'$animate',
		'haUtils',
		'$timeout',
		'$rootScope',
		function (haFeatureFlags, $scs, http, priceApi, haConfig, haDateUtils, $filter, $animate, haUtils, $timeout, $rootScope) {
			return {
				templateUrl: haConfig.getTemplateUrl('/Book/FlightSearch/ha-flexible-price-view.html'),
				restrict: 'A',
				link: function ($scope, element) {

					// FEATURE FLAGS
					//*******************

					// EVENT HANDLERS
					//*******************
					// Calendar forward/backward
					$scope.$on('calendarForward', function () {
						$scope.model.loading = true;
						calendarAction(1);
					});
					$scope.$on('calendarBackward', function () {
						$scope.model.loading = true;
						calendarAction(-1);

					});

					// WATCHERS
					//*******************
					// Watch tripLength
					$scope.$watch('model.tripLength', function (nv, ov) {
						if (nv !== ov) {
							$scope.legs[1].departDate = moment($scope.legs[0].departDate).add(nv, 'days').toDate();
							$scope.maxForward = {}; // reset the value as the trip length has changed
							$scope.updatePrices();
						}
					});

					// Watch Ajax results for initial load
					$scope.initialLoad = true;
					$scope.$watch('results', function (nv) {
						if (nv && nv.length) {
							$scope.initialLoad = false;
							$animate.enabled(false);
							//resetDates();
							addResultsToBuffer(nv);
							refreshData();
							updateMonths();
							if ($rootScope.isMobile) {
								refreshBoundariesMobile();
							}

						}
					});

					// LOCAL FUNCTIONS
					//*******************
					// Build JSON for form POST
					function buildRequestJSON() {

						//MOCK DATA///////////////////////////////////////////////
						//var unavailableDaysObj1 = { "PPT-HNL": { "Origin": "PPT", "Destination": "HNL", "CalendarYears": { "2015": { "3": { "9": 1, "10": 1, "11": 1, "12": 1, "13": 1, "15": 1, "16": 1, "17": 1, "18": 1, "19": 1, "20": 1, "22": 1, "23": 1, "24": 1, "25": 1, "26": 1, "27": 1, "29": 1, "30": 1 }, "4": { "1": 1, "2": 1, "3": 1, "5": 1, "6": 1, "8": 1, "9": 1, "10": 1, "12": 1, "13": 1, "14": 1, "15": 1, "16": 1, "17": 1, "19": 1, "20": 1, "21": 1, "22": 1, "23": 1, "24": 1, "26": 1, "27": 1, "28": 1, "29": 1, "30": 1 }, "5": { "1": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "10": 1, "11": 1, "12": 1, "13": 1, "14": 1, "15": 1, "17": 1, "18": 1, "19": 1, "20": 1, "21": 1, "22": 1, "24": 1, "25": 1, "26": 1, "27": 1, "28": 1, "29": 1, "31": 1 }, "6": { "1": 1, "2": 1, "3": 1, "4": 1, "5": 1, "7": 1, "8": 1, "9": 1, "10": 1, "11": 1, "12": 1, "14": 1, "15": 1, "16": 1, "17": 1, "18": 1, "19": 1, "21": 1, "22": 1, "23": 1, "24": 1, "25": 1, "26": 1, "28": 1, "29": 1, "30": 1 }, "7": { "1": 1, "2": 1, "3": 1, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1, "10": 1, "12": 1, "13": 1, "14": 1, "15": 1, "16": 1, "17": 1, "19": 1, "20": 1, "21": 1, "22": 1, "23": 1, "24": 1, "26": 1, "27": 1, "28": 1, "29": 1, "30": 1, "31": 1 }, "8": { "2": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "9": 1, "10": 1, "11": 1, "12": 1, "13": 1, "14": 1, "16": 1, "17": 1, "18": 1, "19": 1, "20": 1, "21": 1, "23": 1, "24": 1, "25": 1, "26": 1, "27": 1, "28": 1, "30": 1, "31": 1 }, "9": { "1": 1, "2": 1, "3": 1, "4": 1, "6": 1, "7": 1, "8": 1, "9": 1, "10": 1, "11": 1, "13": 1, "14": 1, "15": 1, "16": 1, "17": 1, "18": 1, "20": 1, "21": 1, "22": 1, "23": 1, "24": 1, "25": 1, "27": 1, "28": 1, "29": 1, "30": 1 }, "10": { "1": 1, "2": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1, "11": 1, "12": 1, "13": 1, "14": 1, "15": 1, "16": 1, "18": 1, "19": 1, "20": 1, "21": 1, "22": 1, "23": 1, "25": 1, "26": 1, "27": 1, "28": 1, "29": 1, "30": 1 }, "11": { "1": 1, "2": 1, "3": 1, "4": 1, "5": 1, "6": 1, "8": 1, "9": 1, "10": 1, "11": 1, "12": 1, "13": 1, "15": 1, "16": 1, "17": 1, "18": 1, "19": 1, "20": 1, "22": 1, "23": 1, "24": 1, "25": 1, "26": 1, "27": 1, "29": 1, "30": 1 }, "12": { "1": 1, "2": 1, "3": 1, "4": 1, "6": 1, "7": 1, "8": 1, "9": 1, "10": 1, "11": 1, "13": 1, "14": 1, "15": 1, "16": 1, "17": 1, "18": 1, "20": 1, "21": 1, "22": 1, "23": 1, "24": 1, "25": 1, "27": 1, "28": 1, "29": 1, "30": 1, "31": 1 } }, "2016": { "1": { "1": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "10": 1, "11": 1, "12": 1, "13": 1, "14": 1, "15": 1, "17": 1, "18": 1, "19": 1, "20": 1, "21": 1, "22": 1 } } } }, "HNL-PPT": { "Origin": "HNL", "Destination": "PPT", "CalendarYears": { "2015": { "3": { "9": 1, "10": 1, "11": 1, "12": 1, "13": 1, "15": 1, "16": 1, "17": 1, "18": 1, "19": 1, "20": 1, "22": 1, "23": 1, "24": 1, "25": 1, "26": 1, "27": 1, "29": 1, "30": 1 }, "4": { "1": 1, "2": 1, "3": 1, "5": 1, "6": 1, "8": 1, "9": 1, "10": 1, "12": 1, "13": 1, "14": 1, "15": 1, "16": 1, "17": 1, "19": 1, "20": 1, "21": 1, "22": 1, "23": 1, "24": 1, "26": 1, "27": 1, "28": 1, "29": 1, "30": 1 }, "5": { "1": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "10": 1, "11": 1, "12": 1, "13": 1, "14": 1, "15": 1, "17": 1, "18": 1, "19": 1, "20": 1, "21": 1, "22": 1, "24": 1, "25": 1, "26": 1, "27": 1, "28": 1, "29": 1, "31": 1 }, "6": { "1": 1, "2": 1, "3": 1, "4": 1, "5": 1, "7": 1, "8": 1, "9": 1, "10": 1, "11": 1, "12": 1, "14": 1, "15": 1, "16": 1, "17": 1, "18": 1, "19": 1, "21": 1, "22": 1, "23": 1, "24": 1, "25": 1, "26": 1, "28": 1, "29": 1, "30": 1 }, "7": { "1": 1, "2": 1, "3": 1, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1, "10": 1, "12": 1, "13": 1, "14": 1, "15": 1, "16": 1, "17": 1, "19": 1, "20": 1, "21": 1, "22": 1, "23": 1, "24": 1, "26": 1, "27": 1, "28": 1, "29": 1, "30": 1, "31": 1 }, "8": { "2": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "9": 1, "10": 1, "11": 1, "12": 1, "13": 1, "14": 1, "16": 1, "17": 1, "18": 1, "19": 1, "20": 1, "21": 1, "23": 1, "24": 1, "25": 1, "26": 1, "27": 1, "28": 1, "30": 1, "31": 1 }, "9": { "1": 1, "2": 1, "3": 1, "4": 1, "6": 1, "7": 1, "8": 1, "9": 1, "10": 1, "11": 1, "13": 1, "14": 1, "15": 1, "16": 1, "17": 1, "18": 1, "20": 1, "21": 1, "22": 1, "23": 1, "24": 1, "25": 1, "27": 1, "28": 1, "29": 1, "30": 1 }, "10": { "1": 1, "2": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1, "11": 1, "12": 1, "13": 1, "14": 1, "15": 1, "16": 1, "18": 1, "19": 1, "20": 1, "21": 1, "22": 1, "23": 1, "25": 1, "26": 1, "27": 1, "28": 1, "29": 1, "30": 1 }, "11": { "1": 1, "2": 1, "3": 1, "4": 1, "5": 1, "6": 1, "8": 1, "9": 1, "10": 1, "11": 1, "12": 1, "13": 1, "15": 1, "16": 1, "17": 1, "18": 1, "19": 1, "20": 1, "22": 1, "23": 1, "24": 1, "25": 1, "26": 1, "27": 1, "29": 1, "30": 1 }, "12": { "1": 1, "2": 1, "3": 1, "4": 1, "6": 1, "7": 1, "8": 1, "9": 1, "10": 1, "11": 1, "13": 1, "14": 1, "15": 1, "16": 1, "17": 1, "18": 1, "20": 1, "21": 1, "22": 1, "23": 1, "24": 1, "25": 1, "27": 1, "28": 1, "29": 1, "30": 1, "31": 1 } }, "2016": { "1": { "1": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "10": 1, "11": 1, "12": 1, "13": 1, "14": 1, "15": 1, "17": 1, "18": 1, "19": 1, "20": 1, "21": 1, "22": 1 } } } } };
						//var unavailableDaysObj2 = { "BNE-HNL": { "Origin": "BNE", "Destination": "HNL", "CalendarYears": { "2015": { "3": { "9": 1, "11": 1, "13": 1, "16": 1, "18": 1, "20": 1, "23": 1, "25": 1, "27": 1, "30": 1 }, "4": { "1": 1, "3": 1, "6": 1, "8": 1, "10": 1, "13": 1, "15": 1, "17": 1, "20": 1, "22": 1, "24": 1, "27": 1, "29": 1 }, "5": { "1": 1, "4": 1, "6": 1, "8": 1, "11": 1, "13": 1, "15": 1, "18": 1, "20": 1, "22": 1, "25": 1, "27": 1, "29": 1 }, "6": { "1": 1, "3": 1, "5": 1, "8": 1, "10": 1, "12": 1, "15": 1, "17": 1, "19": 1, "22": 1, "24": 1, "26": 1, "29": 1 }, "7": { "1": 1, "3": 1, "6": 1, "8": 1, "10": 1, "13": 1, "15": 1, "17": 1, "20": 1, "22": 1, "24": 1, "27": 1, "29": 1, "31": 1 }, "8": { "3": 1, "5": 1, "7": 1, "10": 1, "12": 1, "14": 1, "17": 1, "19": 1, "21": 1, "24": 1, "26": 1, "28": 1, "31": 1 }, "9": { "2": 1, "4": 1, "7": 1, "9": 1, "11": 1, "14": 1, "16": 1, "18": 1, "21": 1, "23": 1, "25": 1, "28": 1, "30": 1 }, "10": { "2": 1, "5": 1, "7": 1, "9": 1, "12": 1, "14": 1, "16": 1, "19": 1, "21": 1, "23": 1, "26": 1, "28": 1, "30": 1 }, "11": { "2": 1, "4": 1, "6": 1, "9": 1, "11": 1, "13": 1, "16": 1, "18": 1, "20": 1, "23": 1, "25": 1, "27": 1, "30": 1 }, "12": { "2": 1, "4": 1, "7": 1, "9": 1, "11": 1, "14": 1, "16": 1, "18": 1, "21": 1, "23": 1, "25": 1, "28": 1, "30": 1 } }, "2016": { "1": { "1": 1, "4": 1, "6": 1, "8": 1, "11": 1, "13": 1, "15": 1, "18": 1, "20": 1, "22": 1 } } } }, "HNL-BNE": { "Origin": "HNL", "Destination": "BNE", "CalendarYears": { "2015": { "3": { "10": 1, "12": 1, "15": 1, "17": 1, "19": 1, "22": 1, "24": 1, "26": 1, "29": 1, "31": 1 }, "4": { "2": 1, "5": 1, "7": 1, "9": 1, "12": 1, "14": 1, "16": 1, "19": 1, "21": 1, "23": 1, "26": 1, "28": 1, "30": 1 }, "5": { "3": 1, "5": 1, "7": 1, "10": 1, "12": 1, "14": 1, "17": 1, "19": 1, "21": 1, "24": 1, "26": 1, "28": 1, "31": 1 }, "6": { "2": 1, "4": 1, "7": 1, "9": 1, "11": 1, "14": 1, "16": 1, "18": 1, "21": 1, "23": 1, "25": 1, "28": 1, "30": 1 }, "7": { "2": 1, "5": 1, "7": 1, "9": 1, "12": 1, "14": 1, "16": 1, "19": 1, "21": 1, "23": 1, "26": 1, "28": 1, "30": 1 }, "8": { "2": 1, "4": 1, "6": 1, "9": 1, "11": 1, "13": 1, "16": 1, "18": 1, "20": 1, "23": 1, "25": 1, "27": 1, "30": 1 }, "9": { "1": 1, "3": 1, "6": 1, "8": 1, "10": 1, "13": 1, "15": 1, "17": 1, "20": 1, "22": 1, "24": 1, "27": 1, "29": 1 }, "10": { "1": 1, "4": 1, "6": 1, "8": 1, "11": 1, "13": 1, "15": 1, "18": 1, "20": 1, "22": 1, "25": 1, "27": 1, "29": 1 }, "11": { "1": 1, "3": 1, "5": 1, "8": 1, "10": 1, "12": 1, "15": 1, "17": 1, "19": 1, "22": 1, "24": 1, "26": 1, "29": 1 }, "12": { "1": 1, "3": 1, "6": 1, "8": 1, "10": 1, "13": 1, "15": 1, "17": 1, "20": 1, "22": 1, "24": 1, "27": 1, "29": 1, "31": 1 } }, "2016": { "1": { "3": 1, "5": 1, "7": 1, "10": 1, "12": 1, "14": 1, "17": 1, "19": 1, "21": 1 } } } } };

						////Set depart
						//$scope.unavailableDays[0] = unavailableDaysObj2[$scope.legs[0].origin.Code + '-' + $scope.legs[0].destination.Code];
						////Set return
						//$scope.unavailableDays[1] = unavailableDaysObj2[$scope.legs[0].destination.Code + '-' + $scope.legs[0].origin.Code];
						//END MOCK DATA///////////////////////////////////////////////

						var legs = $scope.legs;
						var midRangeDepart = new Date(legs[0].departDate);
						var midRangeReturn = new Date(legs[0].departDate).dateAdd('day', $scope.model.tripLength);

						function getSegment(i, depart) {
							return {
								OriginCityCode: legs[i].origin.code,
								DestinationCityCode: legs[i].destination.code,
								DepartureDate: moment(depart).format('YYYY-MM-DD'),
								SegmentID: i + 1
							};
						}


						var requestJSON = {
							searchRequest: {
								FlightQueryTypeId: $scope.legs.length,
								AdultCount: $scope.adults,
								ChildCount: $scope.children,
								FlightSearchSegmentList: [
									getSegment(0, midRangeDepart)
								]
							},
							isCalendar: $scope.display === 'calendar'
						};
						if ($scope.legs.length === 2) {
							requestJSON.searchRequest.FlightSearchSegmentList.push(getSegment(1, midRangeReturn));
						}

						return requestJSON;
					}

					// Set the Current Date value
					// (lowest visible value = (Selected Date - 12 [half of 25 visible])
					function resetDates() {
						$scope.selectedPrice = {
							date: new Date($scope.legs[0].departDate),
							price: 0
						};
						if ($rootScope.isMobile) {
							$scope.initialPrice = {
								date: new Date($scope.legs[0].departDate),
								price: 0
							};
						}

						// For calendar, set date to 1st day of the month in which the depart date occurs
						// For chart, it's selected date minus 12 days (since the chart shows 12 days on each side of the selected date)
						if ($scope.display === 'chart') {
							$scope.currentDate = new Date($scope.selectedPrice.date);
							$scope.currentDate.setDate($scope.currentDate.getDate() - parseInt(VISIBLE_DAYS_COUNT / 2));
						} else {
							$scope.currentDate = new Date($scope.legs[0].departDate.getFullYear(), $scope.legs[0].departDate.getMonth(), 1);
						}
					}

					// NOTE: resultsArray: [{Price: <number>, DepartDate: <String> }]
					function addResultsToBuffer(resultsArray) {

						// for mobile, only show mostly complete months
						if ($rootScope.isMobile && resultsArray.length > 90) {

							var completeMonths = [];
							// find complete months (have at least 20 days)
							for (var i = 0, len = resultsArray.length; i < len; i++) {
								var dateString = resultsArray[i].DepartDate;

								var mo = moment(dateString).month();
								if (moment(dateString).date() >= 20 && completeMonths.indexOf(mo) < 0) {
									completeMonths.push(mo);
								}
							}
							// purge partial months
							for (var i = 0; i < resultsArray.length; i++) {
								var dateString = resultsArray[i].DepartDate;
								if (completeMonths.indexOf(moment(dateString).month()) < 0) {
									resultsArray.splice(i, 1);
									i--;
								}
							}

						}

						//if first time through, init.  If not, set
						$scope.firstBufferDate = $scope.firstBufferDate ? $scope.firstBufferDate : new Date('1/1/2100');
						$scope.lastBufferDate = $scope.lastBufferDate ? $scope.lastBufferDate : new Date('1/1/1900');

						for (var i = 0, len = resultsArray.length; i < len; i++) {
							var result = resultsArray[i];
							var depart = result.DepartDate;
							if (depart === $scope.selectedPrice.date.YYYY_MM_DD()) {
								$scope.selectedPrice.price = result.Price;
							}
							if ($rootScope.isMobile && depart === $scope.initialPrice.date.YYYY_MM_DD()) {
								$scope.initialPrice.price = result.Price;
							}
							$scope.buffer[depart] = {
								price: result.Price,
								departDate: depart,
								returnDate: result.ReturnDate
							};

							depart = moment(result.DepartDate).toDate();
							//make sure as we look through the set that the lastBufferDate/firstBufferDate is the latest/earliest date.
							$scope.lastBufferDate = haDateUtils.isAfter(depart, $scope.lastBufferDate) ? depart : $scope.lastBufferDate;
							$scope.firstBufferDate = haDateUtils.isBefore(depart, $scope.firstBufferDate) ? depart : $scope.firstBufferDate;

						}
					}

					// Build Chart and Calendar Data Models
					function refreshData() {
						var tempDate;
						var departDate;
						var returnDate;
						var price;

						$scope.chartData = [];  //chartData is an array
						$scope.calendarData = {}; //calendarData is Dictionary

						// Reset min/max
						$scope.minPrice = Number.POSITIVE_INFINITY;
						$scope.maxPrice = Number.NEGATIVE_INFINITY;

						// Build chart data and calendar data
						var dataRange;

						if ($rootScope.isMobile) {
							// If mobile, show the whole buffer
							dataRange = haDateUtils.numDaysDifference(new Date($scope.firstBufferDate), new Date($scope.lastBufferDate));

						} else {
							// We want to loop from the first visible day to the last day of the buffer.
							dataRange = haDateUtils.numDaysDifference($scope.currentDate, new Date($scope.lastBufferDate));
							//if we are the end of the buffer, use visible
							dataRange = (dataRange > VISIBLE_DAYS_COUNT) ? dataRange : VISIBLE_DAYS_COUNT;
						}
						for (var i = 0; i < dataRange + 1; i++) {
							// If mobile, build from the first buffer date, otherwise use currentDate
							var baseDate = $rootScope.isMobile ? $scope.firstBufferDate : $scope.currentDate;
							tempDate = new Date(baseDate);
							tempDate.setDate(baseDate.getDate() + i);
							returnDate = new Date(tempDate);
							returnDate.setDate(tempDate.getDate() + parseInt($scope.model.tripLength));
							departDate = tempDate;
							price = undefined;  //reset price

							if (departDate.YYYY_MM_DD() === $scope.selectedPrice.date.YYYY_MM_DD()) {
								if ($scope.buffer[tempDate.YYYY_MM_DD()] && $scope.buffer[tempDate.YYYY_MM_DD()].price) {
									$scope.selectedPrice.price = $scope.buffer[tempDate.YYYY_MM_DD()].price;
								} else {
									$scope.selectionError = true;
								}
								//$scope.selectedPrice.price = $scope.buffer[tempDate.YYYY_MM_DD()].price;
							}

							if ($scope.buffer[departDate.YYYY_MM_DD()]) {
								price = $scope.buffer[departDate.YYYY_MM_DD()].price;
								if (price) {
									// Calculate min/max price in this pass, but only for visible days on the chart
									if (i <= VISIBLE_DAYS_COUNT || $rootScope.isMobile) {
										$scope.minPrice = Math.min($scope.minPrice, price);
										$scope.maxPrice = Math.max($scope.maxPrice, price);
									}
									// Calendar uses min only, can show min for all dates in the range
									$scope.minCalendarPrice = Math.min($scope.minCalendarPrice, price);
								}
							}

							//Keep Chart Data and Calendar Data in Sync
							//Build Calendar Data
							$scope.calendarData[departDate.YYYY_MM_DD()] = {
								departDate: departDate,
								returnDate: returnDate,
								price: price
							};

							//Build Chart Data only for the visible range
							if (i <= VISIBLE_DAYS_COUNT || $rootScope.isMobile) {
								$scope.chartData.push({ departDate: departDate, returnDate: returnDate, price: price });
							}

						}

						// Include the selected price in the calculation, for proper scaling, only for the chart
						if ($rootScope.isMobile) {
							$scope.minPrice = Math.min($scope.minPrice, $scope.initialPrice.price);
							$scope.maxPrice = Math.max($scope.maxPrice, $scope.initialPrice.price);
						} else {
							$scope.minPrice = Math.min($scope.minPrice, $scope.selectedPrice.price);
							$scope.maxPrice = Math.max($scope.maxPrice, $scope.selectedPrice.price);
						}

						// Raise the ceiling for the max price in order to keep some padding at the top of the chart.
						$scope.maxPrice += Math.round($scope.maxPrice * CEILING_FACTOR);

						calculatePriceRatios();
						window.setTimeout(function () {
							$animate.enabled(true);
						}, 0);
					}

					// Caluclate and set the price ratios for the results
					function calculatePriceRatios() {
						for (var i = 0; i < $scope.chartData.length; i++) {
							if ($scope.chartData[i].price) {
								if ($scope.chartData[i].price === $scope.minPrice && $scope.chartData[i].price === $scope.maxPrice) {
									$scope.chartData[i].ratio = 1;
								} else {
									$scope.chartData[i].ratio = $scope.chartData[i].price / $scope.maxPrice;
								}
							}
						}

						$scope.selectedPrice.ratio = Math.min($scope.selectedPrice.price / $scope.maxPrice, 1);
						if ($rootScope.isMobile) {
							$scope.initialPrice.ratio = Math.min($scope.initialPrice.price / $scope.maxPrice, 1);
						}
					}

					// Check to see if the buffer has run out
					function findBufferGap(direction) {

						// Forward click
						if (direction > 0) {
							var lastBufferDate = new Date($scope.lastBufferDate);
							var newLastVisibleDate = new Date($scope.currentDate);
							newLastVisibleDate.dateAdd('day', VISIBLE_DAYS_COUNT - 1);
							if (haDateUtils.isAfter(newLastVisibleDate, lastBufferDate)) {
								return true;
							}
						} else { // Backward click
							var firstBufferDate = new Date($scope.firstBufferDate);
							var newFirstVisibleDate = new Date($scope.currentDate);
							if (haDateUtils.isBefore(newFirstVisibleDate, firstBufferDate)) {
								return true;
							}
						}
						return false;
					}

					// Calendar forward/backward
					function calendarAction(direction) {

						if ($scope.error) {
							$scope.model.loading = false;
							return;
						}
						var dateStart;
						var tempDate;
						var fetch = true;

						if (direction > 0) {
							dateStart = new Date($scope.lastBufferDate);
							//Cycle forward 1 month.  Set the new current date to be the first of the month
							$scope.currentDate = new Date($scope.currentDate.getFullYear(), $scope.currentDate.getMonth() + 1, 1);
							tempDate = new Date($scope.currentDate.getFullYear(), $scope.currentDate.getMonth() + 2, 1);
							fetch = (haDateUtils.isAfter(tempDate, $scope.lastBufferDate)) ? true : false;
							fetch = ($scope.maxForward && $scope.maxForward.date) ? false : fetch; // if max date has been reached, overwrite to false
						} else {
							dateStart = new Date($scope.firstBufferDate.getTime());
							//Cycle backward 1 month.  Set the new current date to be the first of the month
							$scope.currentDate = new Date($scope.currentDate.getFullYear(), $scope.currentDate.getMonth() - 1, 1);
							// Are we querying for dates before the buffer AND no further in the past than today?
							fetch = haDateUtils.isBefore($scope.currentDate, $scope.firstBufferDate) && !moment(new Date()).isSame(dateStart, 'day');
						}

						// if max date has been reached, update the flag for forward arrow
						if ($scope.maxForward && $scope.maxForward.date) {
							$scope.maxForward.disableForward = (haDateUtils.isAfter(tempDate, $scope.lastBufferDate)) ? true : false;
						}

						if (fetch) {
							$scope.model.loading = true;
							priceApi.fetchPrices(moment(dateStart).add(direction, 'day').format('YYYY-MM-DD'), direction, { cache: true }).success(function (response) {

								if (response.IsSuccess) {
									addResultsToBuffer(response.LowFareTripResponseList);
									// if max date range was reached in the backend, save the last returned date to avoid trying to get any more results
									if (response.isMaxDateReached) {
										$scope.maxForward = {
											date: $scope.results[$scope.results.length - 1].DepartDate,
											disableForward: true
										};
									}
									refreshData();
								}
								$scope.model.loading = false;
							});
						} else {
							refreshData();
							$scope.model.loading = false;
						}
					}

					// Move Callback, retrieves updated data
					function moveCallback(direction) {

						// Determine what to query the API for
						var getData = findBufferGap(direction);
						$scope.moving = false;
						$scope.model.loading = true;
						if (getData) {
							var dateStart = (direction > 0) ? new Date($scope.lastBufferDate) : new Date($scope.firstBufferDate);
							priceApi.fetchPrices(moment(dateStart).format('YYYY-MM-DD'), direction, { cache: true }).success(function (response) {
								if (response.IsSuccess) {
									addResultsToBuffer(response.LowFareTripResponseList);
									refreshData();
								}
								$scope.model.loading = false;
							});
						} else {
							refreshData();
							$scope.model.loading = false;
						}
						$scope.$apply();
					}

					// Calculate how many days for each visible month should be shown
					function updateMonths() {
						$scope.preventLeft = false;
						$scope.preventRight = false;
						$scope.endOfMonthIndex = null;
						var visibleMonths = [];
						var tempDate = $scope.chartData[0].departDate;
						var month = {
							month: tempDate.getMonth(),
							year: tempDate.getFullYear(),
							days: 1
						};
						for (var i = 1; i < VISIBLE_DAYS_COUNT; i++) {
							tempDate = $scope.chartData[i].departDate;
							if (tempDate.getMonth() !== month.month) {
								visibleMonths.push(month);
								$scope.endOfMonthIndex = i - 1;
								month = {
									month: tempDate.getMonth(),
									year: tempDate.getFullYear(),
									days: 1
								};
							} else {
								month.days++;
							}

							if (tempDate <= Date.now()) {
								$scope.preventLeft = true;
							}
							if (haDateUtils.numDaysDifference(new Date(), tempDate) >= MAX_DAYS) {
								$scope.preventRight = true;
							}
						}
						visibleMonths.push(month);
						$scope.visibleMonths = visibleMonths;
					}

					// SCOPE METHODS
					//*******************
					// Update Prices
					$scope.updatePrices = function () {
						$scope.error = false;
						$scope.model.loading = true;
						var requestJSON = buildRequestJSON();
						$timeout(function () {
							priceApi.fetchInitialPrices({
								data: requestJSON,
								config: { cache: true }
							}).then(function (response) {
								if (response.IsSuccess) {
									$scope.results = response.LowFareTripResponseList;
									// if max date range was reached in the backend, save the last returned date to avoid trying to get any more results
									if (response.isMaxDateReached) {
										$scope.maxForward = {
											date: $scope.results[$scope.results.length - 1].DepartDate,
											disableForward: true
										};
									}
								} else {
									$scope.error = true;
									$scope.results = [];
								}
								$timeout(function() {
									$scope.model.loading = false;
								});
							}, 50);
						});

					};

					// Move a specified number of days
					$scope.move = function (days) {
						if (animating || $scope.error) {
							$scope.model.loading = false;
							return;
						}
						$scope.moving = true;
						animating = true;
						$scope.currentDate.setDate($scope.currentDate.getDate() + days);
						var direction = days / Math.abs(days); // 1 or -1
						window.clearTimeout(moveTimeout);
						moveTimeout = window.setTimeout(function () {
							moveCallback(direction);
						}, MOVE_TIMEOUT_MILLESECONDS);
						refreshData();
						updateMonths();
					};

					$scope.monthFilter = function (value) {
						return $scs('LowFare.' + monthKeys[value]);
					};

					$scope.getFlightResultsLink = function (depart) {
						var url = '/Book/FlightResults?l=' + $scope.legs[0].origin.code + '-' + $scope.legs[0].destination.code + '+' + moment(depart).format('YYYY-MM-DD');
						if ($scope.legs[1]) {
							url += ',' + $scope.legs[1].origin.code + '-' + $scope.legs[1].destination.code + '+' + moment(depart).add($scope.model.tripLength, 'days').format('YYYY-MM-DD');
						}
						if ($scope.adults) {
							url += '&a=' + $scope.adults;
						}
						if ($scope.children) {
							url += '&c=' + $scope.children;
						}

						return url;
					};

					// Get Lowline Style (top)
					$scope.getLowLineStyle = function () {
						return 'calc(' + ((1 - $scope.selectedPrice.ratio) * 100).toString() + '% + 45px)';
					};

					// Get Price Bar Style (top)
					$scope.getBarStyle = function (r) {
						return r.price ? ((1 - (r.ratio)) * 100).toString() + '%' : (($scope.model.loading || $scope.moving) ? '100%' : '-23px');
					};

					// Get Month Style (width/left)
					$scope.getMonthStyle = function (month, dim, idx) {
						return (dim === 'width') ? ((month.days / 25) * 100).toString() + '%' : (idx ? ((1 - (month.days / 25)) * 100).toString() : '0') + '%';
					};

					// Get Tooltip Style (top)
					$scope.getTipStyle = function (r) {
						return r.price ? (Math.min((1 - (r.ratio)) * 100, 84).toString()) + '%' : (($scope.model.loading || $scope.moving) ? '100%' : '-23px');
					};

					// Set display
					$scope.setDisplay = function (d) {
						$scope.display = d;
						initialize();
					};

					$scope.dayPlus = function (date, duration) {
						return (angular.isDate(date)) ? (new Date(+date)).dateAdd('day', duration) : new Date();
					};

					// for mobile
					$scope.selectDate = function (date, event) {
						if ($rootScope.isMobile) {
							event.preventDefault();
							if ($scope.calendarData[date.YYYY_MM_DD()]) {
								$timeout(function () {
									$scope.legs[0].departDate = date;
									$scope.selectedPrice.date = date;
									$scope.selectedPrice.price = $scope.calendarData[date.YYYY_MM_DD()].price;
								});
							}
						}
					}

					function getScrollPosition() {
						var modalNode = element.closest('.ha-modal')[0];
						return modalNode.scrollHeight - modalNode.scrollTop;
					};

					$scope.moveMobile = function (direction) {
						if ($scope.error || !$rootScope.isMobile) {
							return false;
						}
						$scope.model.loading = true;
						var dateStart = (direction > 0) ? new Date($scope.lastBufferDate) : new Date($scope.firstBufferDate);

						priceApi.fetchPrices(moment(dateStart).format('YYYY-MM-DD'), direction, { cache: true }).success(function (response) {
							if (response.IsSuccess) {
								// If we are loading previous dates, we want to store the current scroll position
								if (direction < 0) {
									var oldScrollPosition = getScrollPosition();
									$timeout(function () {
										var modalNode = element.closest('.ha-modal')[0];
										modalNode.scrollTop = modalNode.scrollHeight - oldScrollPosition;
									}, 0);
								}
								addResultsToBuffer(response.LowFareTripResponseList);
								refreshData();
								// add more months to calendar
								var event = (direction > 0) ? 'calendarGoForward' : 'calendarGoBackward';
								$scope.$broadcast(event, 3);
							}
							if (response.isMaxDateReached) {
								$scope.preventRight = true;
							}
							refreshBoundariesMobile();
							$timeout(function() {
								$scope.model.loading = false;
							});
						});
					};
					function refreshBoundariesMobile() {
						if (haDateUtils.numDaysDifference(new Date($scope.firstBufferDate), new Date(moment(Date.now()).utc().format("MM/DD/YYYY"))) <= 1) {
							$scope.preventLeft = true;
						}
						if (haDateUtils.numDaysDifference(new Date(), new Date($scope.lastBufferDate)) >= MAX_DAYS) {
							$scope.preventRight = true;
						}
					}
					// hide the load previous button under the modal-header until user scrolls up
					$scope.setScrollPos = function () {
						if ($('#load-previous-row').length) { // this will only exist on mobile
							// var offset = $('#load-previous-row').outerHeight(); // doesn't render string in time, incorrect height
							$('#FlexiblePriceView')[0].scrollTop = 82; // offset;
						}
					}
					var initChartPos = true;
					$scope.setChartPos = function () {
						if (initChartPos && $rootScope.isMobile) {
							initChartPos = false;
							$timeout(function () {
								var $selectedPriceElement = element.find('.selected-price');
								if ($selectedPriceElement.length) {
									element.closest('.ha-modal').scrollTop($selectedPriceElement.offset().top - ($(window).height() / 2));
								}
							}, 0);
						}
					}

					// INITIALIZATION
					//*******************
					function initialize() {

						// Initializations
						if (typeof $scope.legs === 'string') {
							$scope.legs = haUtils.parseLegs($scope.legs);
							//adding read DisplayNames will require an API call- so for now just use the Airport codes
							$scope.legs[0].origin.display = $scope.legs[0].origin.code;
							$scope.legs[0].destination.display = $scope.legs[0].destination.code;
						}

						$scope.minPrice = Number.POSITIVE_INFINITY;
						$scope.maxPrice = Number.NEGATIVE_INFINITY;
						$scope.buffer = {};    // The days we've loaded so far
						$scope.visibleMonths = [];
						$scope.display = $scope.display || 'calendar';
						$scope.maxForward = {}; // for keeping track of the last max date backend returned

						//Since the modal can be opened and closed, and the views share scope hard reset values when it's opened and when the view changes
						$scope.chartData = []; //set or reset
						$scope.calendarData = {}; //set or reset
						$scope.currentDate = undefined; //set or reset
						$scope.firstBufferDate = undefined; //set or reset
						$scope.lastBufferDate = undefined; //set or reset
						$scope.visibleMonths = []; //set or reset

						// Trip Length
						$scope.model = {
							tripLength: $scope.legs.length === 2 ? moment($scope.legs[1].departDate).diff($scope.legs[0].departDate, 'days') : 0,
							loading: false
						};
						//Don't bother detecting multi-destination.
						//This modal is only applicable for 1 way or round trips.
						//If someone sends in invalid data- we'll have problems everywhere.
						$scope.tripType = $scope.legs.length;

						// Build day array
						$scs.fetch('LowFare.nighttext,LowFare.nightstext').then(function (values) {
							$scope.nightValues = angular.copy(Array(61)).map(function (x, i) {
								return i + ' ' + values[1];
							});
							$scope.nightValues[0] = '0 ' + values[0];
							$scope.nightValues[1] = '1 ' + values[0];
							$scope.$apply();
						});

						resetDates();
						$scope.updatePrices();
					}

					initialize();
				}
			};
		}
	]);

	// Animations
	module.animation('.month-animation', function () {
		return {
			enter: function (element, done) {
				element.css({ 'opacity': 0 });
				element.delay(500).animate({ 'opacity': 1 }, {
					complete: done
				});
			}
		};
	});
	module.animation('.price-item-animation', function () {
		return {
			enter: function (element, done) {
				if (element.hasClass('first-item')) {
					// The very first element
					var oldMargin = element.css('margin-left');
					element.css({
						'margin-left': 0
					});
					element.animate({
						'margin-left': oldMargin
					}, {
						easing: 'linear',
						duration: 500,
						complete: done
					});
					return function () {
						element.css({
							'margin-left': ''
						});
						animating = false;
					};
				} else {
					element.animate({
						'background-color': 'transparent'
					}, {
						duration: 500,
						complete: done
					});
				}
			},
			leave: function (element, done) {
				element.animate({
					'margin-left': 0
				}, {
					easing: 'linear',
					duration: 500,
					complete: done
				});

				if (element.hasClass('first-item')) {
					var newFirst = element.nextAll(':not(.ng-leave-prepare)').first();
					newFirst.css({
						'margin-left': 0
					});
					return function () {
						newFirst.css({
							'margin-left': ''
						});
						animating = false;
					};
				}
			}
		};
	});

})(angular);
;
(function (angular) {

    'use strict';

    var module = angular.module('haVerticalSeatmapModule', []);

    // Author: Jamie Perkins
    // just the seat map itself, relies on service to load svg
    module.directive('haVerticalSeatmap', ['haVerticalSeatmapService', 'haConfig', '$filter', '$timeout', '$log', '$rootScope', '$window', function (haVerticalSeatmapSvc, haConfig, $filter, $timeout, $log, $rootScope, $window) {

        return {
            restrict: 'A',
            scope: {},
            templateUrl: haConfig.getTemplateUrl('VerticalSeatmap/ha-vertical-seatmap.html'),
            link: function ($scope) {

                $scope.svc = haVerticalSeatmapSvc;
                $scope.seat = {}; // for tooltip
                $scope.isTouchDevice = false; // for iPads
                if (!$rootScope.isMobile) {
                    $scope.isTouchDevice = Boolean(navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i));
                }

                var colors = {
                    gray: '#D0D0D0',
                    darkViolet: '#4D2E91',
                    stroke: '#3C3C34'
                };
                var showTooltipTimeout;
                var cabinHeaderClass = 'header-uppercase-7';
                if ($rootScope.$switch('Global:EnableVIC') || $rootScope.vicilicious) {
                    cabinHeaderClass = 'medium-titlecase-3';
                }

                $scope.$on('renderSeatmapAtIndex', function (event, data) {
                    renderSeatmapAtIndex(data);
                });
                $scope.$on('updateSeatmapAtIndex', function (event, data) {
                    updateSeatmapAtIndex(data);
                });
                $scope.$on('cancelTooltip', hideTooltip);
                $scope.$on('refreshSeatmapTooltip', refreshTooltip);

                $window.addEventListener('orientationchange', function () {
                    $log.debug('orientation change');
                    $rootScope.$broadcast('cancelTooltip');
                });

                // determine price to display in tooltip
                $scope.calculateSeatDisplayCost = function (seat) {

                    var cost = seat.price,
						selectingPaxSeat = $scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex],
						fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;

                    // do they have a seat credit? (change seats/reshop)
                    if (selectingPaxSeat.originalSeat && selectingPaxSeat.originalSeat.credit > 0) {
                        cost -= selectingPaxSeat.originalSeat.credit;
                    }
                        // did they buy an EC/PS fare
                    else if (!(seat.Type > 0 && /coach/gi.test(fareClass))) {
                        cost -= seat.price;
                    }
                    seat.SeatAmountDisplay = cost;
                    return cost;
                }

                // select seat function - handles jquery event
                function selectSeat(event) {
                    if (event.type === 'keyup' && event.keyCode !== 13) {
                        return; // only respond to enter key
                    }

                    var $seat = $(this),
						id = $seat.attr('id'),
						info = id.split('-');
                    if (id && info.length) {

                        var name = info[2],
							seatObj = $scope.svc.legs[$scope.svc.activeLegIndex].seatModel.seats[name];

                        if (seatObj.isUnavailable) { return; }

                        $scope.seat = seatObj;
                        $scope.$emit('seatWasSelectedOrDeselected', seatObj);

                    }
                }

                function showTooltip() {

                    var $seat = $(this);
                    var id = $seat.attr('id'),
						info = id.split('-'),
						name = info[2],
						seatObj = $scope.svc.legs[$scope.svc.activeLegIndex].seatModel.seats[name],
						popoverId = (seatObj.isSelected) ? '#seatPopoverSelected' : '#seatPopover';
                    seatObj.isFiltered = isFiltered(seatObj);
                    $scope.seat = seatObj;

                    if ($scope.isTouchDevice && seatObj.isSelected) { // deselecting on tablet
                        $scope.$emit('seatWasSelectedOrDeselected', seatObj);
                        return;
                    }
                    calculateTooltipPositionAndDisplay($seat, popoverId);
                }

                function refreshTooltip() {
                    $('#seatPopover').fadeOut('fast');
                    $('#seatPopoverSelected').fadeOut('fast');
                    $('#seatPopoverArrow').fadeOut('fast', function () {
                        // don't show tooltip after deselect on tablet
                        if ($scope.isTouchDevice && !$scope.seat.isSelected) { return; }
                        // refresh
                        var $seat = $('#' + $scope.svc.activeLegIndex + '-seat-' + $scope.seat.name),
							popoverId = ($scope.seat.isSelected) ? '#seatPopoverSelected' : '#seatPopover';
                        calculateTooltipPositionAndDisplay($seat, popoverId);
                    });
                }
                function hideTooltip() {
                    $timeout.cancel(showTooltipTimeout);
                    $('#seatPopover').hide();
                    $('#seatPopoverSelected').hide();
                    $('#seatPopoverArrow').hide();
                }
                // hideTooltip() responds to events much faster than the scope equivalent.
                // $scope fn is accessed through ng-click. therefore, 2 references to hideTooltip fn.
                $scope.hideTooltip = function () {
                    hideTooltip();
                };

                function calculateTooltipPositionAndDisplay($seat, popoverId) {
                    $timeout.cancel(showTooltipTimeout);
                    showTooltipTimeout = $timeout(function () {
                        var $rect = $seat.find('rect'),
							$container = $seat.parents('.seatmap'),
							containerWidth = $container.width(),
							seatWidth = Number($rect.attr('width')) * $scope.svc.svgScaleFactor,
							tooltipWidth = $(popoverId).width(),
							tooltipHeight = $(popoverId).height(),
							maxTooltipLeft = containerWidth - tooltipWidth,
							rectTop = Number($rect.attr('y')) * $scope.svc.svgScaleFactor,
							rectLeft = Number($rect.attr('x')) * $scope.svc.svgScaleFactor,
							svgWidth = $seat.parents('svg').attr('width') * $scope.svc.svgScaleFactor,
							svgLeft = (containerWidth - svgWidth) / 2,
							seatmapLeft = $container.position().left,
							seatLeft = seatmapLeft + svgLeft + rectLeft,
							arrowHeight = 11,
							arrowHalfWidth = 10,
							position = {
							    tooltipTop: rectTop - tooltipHeight - arrowHeight,
							    tooltipLeft: seatLeft + seatWidth / 2 - tooltipWidth / 2,
							    arrowTop: rectTop - arrowHeight,
							    arrowLeft: seatLeft + seatWidth / 2 - arrowHalfWidth
							};
                        if (position.tooltipLeft < 0) { position.tooltipLeft = 0; }
                        if (position.tooltipLeft > maxTooltipLeft) { position.tooltipLeft = maxTooltipLeft; }
                        // console.log('rectLeft', rectLeft);
                        // console.log('svgLeft', svgLeft);
                        // console.log('containerWidth', containerWidth);
                        // console.log('svgWidth', svgWidth);
                        // console.log('seatmapLeft', seatmapLeft);
                        // console.log('position.tooltipLeft', position.tooltipLeft);
                        // console.log('position.arrowTop', position.arrowTop);
                        // console.log('position.arrowLeft', position.arrowLeft);
                        // console.log('--')
                        $(popoverId).show().css({
                            top: position.tooltipTop,
                            left: position.tooltipLeft
                        });
                        $('#seatPopoverArrow').show().css({
                            top: position.arrowTop,
                            left: position.arrowLeft
                        });

                    }, 250);
                }


                function isFiltered(seatObj) {
                    return (
						(seatObj.isStandard && !$scope.svc.seatTypeFilters.standard.value) ||
						(seatObj.isPreferred && !$scope.svc.seatTypeFilters.preferred.value) ||
						(seatObj.isExtraComfort && !$scope.svc.seatTypeFilters.extra.value) ||
						(seatObj.isLieFlat && !$scope.svc.seatTypeFilters.flat.value) ||
						(seatObj.isFirstClass && !$scope.svc.seatTypeFilters.first.value) ||
						(seatObj.isExitRow && !$scope.svc.seatTypeFilters.exit.value)
					);
                }

                // for updating seat against filtering, selection
                function updateSeat(name, seatObj, legIndex) {
                    var $seat = $('#' + legIndex + '-seat-' + name);
                    if (!$seat[0]) { return; }

                    var $rect = $seat.find('rect'),
						origFill = $rect.attr('orig-fill');

                    // filtering
                    var filtered = isFiltered(seatObj);
                    if (filtered) {
                        $seat[0].classList.add('filtered');
                        if ($seat.attr('tabindex')) {
                            var restoreTabIndex = $seat.attr('tabindex');
                            $seat.removeAttr('tabindex');
                            $seat.attr('restore-tabindex', restoreTabIndex);
                        }
                    } else {
                        $seat[0].classList.remove('filtered');
                        if ($seat.attr('restore-tabindex')) {
                            var restoreTabIndex = $seat.attr('restore-tabindex');
                            $seat.removeAttr('restore-tabindex');
                            $seat.attr('tabindex', restoreTabIndex);
                        }
                    }

                    // select
                    if (seatObj.isSelected && !origFill) {
                        // select
                        $seat[0].classList.add('hide-text');
                        var oldFill;
                        if (seatObj.isLieFlat) {
                            oldFill = $seat.find('path').attr('fill');
                            $seat.find('path').attr('fill', colors.darkViolet);
                            $seat.find('line').attr('stroke', 'none');
                        } else {
                            oldFill = $rect.attr('fill');
                            $rect.attr('fill', colors.darkViolet);
                        }
                        $rect.attr('orig-fill', oldFill);
                        drawCheckmarkForSeat($seat, name, legIndex);
                    }
                    // deselect
                    if (!seatObj.isSelected && origFill) {
                        $seat.children('[id^=checkmark]').remove();
                        $seat[0].classList.remove('hide-text');
                        if (seatObj.isLieFlat) {
                            $seat.find('path').attr('fill', origFill);
                            $seat.find('line').attr('stroke', colors.stroke);
                        } else {
                            $rect.attr('fill', origFill);
                        }
                        $rect.removeAttr('orig-fill');
                    }
                    if (seatObj.isSelected) {
                        $seat[0].classList.remove('filtered');
                    }

                }
                // for mocking seat price
                /* not currently used *
				function mockSeatPrice (seatObj) {
					var price = 0;
					if (seatObj.isExtraComfort || seatObj.isPreferred) { price += 60; }
					if (seatObj.isWindow) { price += 10; }
					if (seatObj.isAisle) { price += 20; }
					return price;
				}*/

                var cols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J']; // for assigning tabindex
                //Update each seat name attribute with Seat Type
                function updateSeatNameAttribute(seat, attributeValue) {
                    if (seat.attr('name')) {
                        seat.attr('name', seat.attr('name') + ',' + attributeValue);
                    }
                    else {
                        seat.attr('name', attributeValue);
                    }
                }
                // show unavailable seats, prices, and build out seat model based on svg
                function renderSeatmapAtIndex(legIndex) {

                    // already rendered?
                    if ($('#svgSeatmap' + legIndex).find('svg').attr('rendered')) {
                        updateSeatmapAtIndex(legIndex);
                        return;
                    }

                    var renderStartTime = new Date().getTime();

                    $('g[id^=seat]').off();
                    // event binding for selection
                    if ($scope.svc.isForSelection) {
                        if (!$rootScope.isMobile) {
                            if ($scope.isTouchDevice) {
                                $('g[id^=seat]').on('click keyup', showTooltip);
                            } else {
                                $('g[id^=seat]').on('click keyup', selectSeat);
                                $('g[id^=seat]').hover(showTooltip, hideTooltip);
                            }
                        } else {
                            $('g[id^=seat]').on('click', selectSeat);
                        }
                    }

                    var model = $scope.svc.legs[legIndex].seatModel, highestPrice = 0;
                    model.availableFirstClassSeats = 0;
                    model.availableExtraComfortSeats = 0;
                    model.availablePreferredSeats = 0;
                    if (!model.seatsUnavailable) {
                        // build out model from seat properties and render price text & tabindex or unavailable
                        $('g[id^=seat]').each(function () {

                            var $seat = $(this),
								$rect = $seat.find('rect'),
								//$text = $seat.find('text'),
								id = $seat.attr('id').split('-'),
								name = id[1],
								rowNumber = parseInt(name, 10),
								props = (id[2]) ? id[2] : '';

                            $seat.attr('id', legIndex + '-seat-' + name);
                            $seat[0].classList.add('seat');

                            if (!model.seats[name]) {
                                model.seats[name] = {
                                    isUnavailable: true
                                };
                            }
                            if ($scope.svc.isForSelection && !model.seats[name].isUnavailable && !$seat.attr('tabindex')) {
                                // assign tabindex
                                var row = parseInt(name),
									col = name.match(/[A-Z]/)[0],
									colIndex = cols.indexOf(col),
									tabindex = 100 + (row * 10) + colIndex;
                                $seat.attr({
                                    'tabindex': tabindex,
                                    'aria-label': row + ' ' + col
                                });
                            }

                            /*	assign properties from SVG:
								e = extra comfort
								p = preferred
								w = window
								a = aisle
								x = exit row
								l = limited recline
								r = rear facing
								f = first class
								i = lie flat
							*/
                            model.seats[name].name = name;
                            model.seats[name].Type = 0;
                            if (props.indexOf('f') >= 0) {
                                updateSeatNameAttribute($seat, "firstclass");
                                model.seats[name].isFirstClass = true;
                            }
                            if (props.indexOf('i') >= 0) {
                                updateSeatNameAttribute($seat, "lieflat");
                                model.seats[name].isLieFlat = true;
                            }
                            if (props.indexOf('x') >= 0) {
                                updateSeatNameAttribute($seat, "exitrow");
                                model.seats[name].isExitRow = true;
                            }
                            if (props.indexOf('a') >= 0) {
                                updateSeatNameAttribute($seat, "aisle");
                                model.seats[name].isAisle = true;
                            }
                            if (props.indexOf('w') >= 0) {
                                updateSeatNameAttribute($seat, "window");
                                model.seats[name].isWindow = true;
                            }
                            if (props.indexOf('e') >= 0) {
                                updateSeatNameAttribute($seat, "extracomfort");
                                model.seats[name].isExtraComfort = true;
                            }
                            if (props.indexOf('p') >= 0) {
                                updateSeatNameAttribute($seat, "preferred");
                                model.seats[name].isPreferred = true;
                            }
                            if (props.indexOf('r') >= 0) {
                                updateSeatNameAttribute($seat, "rearfacing");
                                model.seats[name].isRearFacing = true;
                            }
                            if (props.indexOf('l') >= 0) {
                                updateSeatNameAttribute($seat, "limitedrecline");
                                model.seats[name].isLimitedRecline = true; // not used
                            }
                            if (!model.seats[name].isExtraComfort && !model.seats[name].isPreferred && !model.seats[name].isFirstClass) {
                                updateSeatNameAttribute($seat, "standard");
                                model.seats[name].isStandard = true;
                            }

                            if (model.seats[name].price && model.seats[name].price > highestPrice) {
                                highestPrice = model.seats[name].price;
                            }

                            // disabling cabins
                            if ($scope.svc.legs[legIndex].disableFirstClass && model.seats[name].isFirstClass) {
                                model.seats[name].isUnavailable = true;
                            }
                            if ($scope.svc.legs[legIndex].disableMainCabin && !model.seats[name].isFirstClass) {
                                model.seats[name].isUnavailable = true;
                            }

                            //disabling row 4 for  infant travelers on 717
                            if (rowNumber === 4 && $scope.svc.legs[legIndex].EquipmentName === 'Boeing 717' && $scope.svc.disableRow4) {
                                model.seats[name].isUnavailable = true;
                            }
                            // disabling row 4 on change seats
                            if (rowNumber === 4 && $scope.svc.legs[legIndex].EquipmentName === 'Boeing 717' && $scope.svc.BlockRowFour) {
                                model.seats[name].isUnavailable = true;
                            }

                            // disabling exit rows
                            if ($scope.svc.disableExitRows && model.seats[name].isExitRow) {
                                model.seats[name].isUnavailable = true;
                            }

                            // disabling upgrades
                            if (model.disableSeatUpgradeFlx) {
                                $scope.svc.disableSeatUpgrades = model.disableSeatUpgradeFlx
                            }
                            if ($scope.svc.disableSeatUpgrades && (model.seats[name].isExtraComfort || model.seats[name].isPreferred)) {
                                model.seats[name].isUnavailable = true;
                            }

                            // assign properties of entire seatmap
                            if (model.seats[name].isFirstClass) {
                                $seat[0].classList.add('firstClass');
                                model.hasFirstClass = true;
                            }
                            if (model.seats[name].isLieFlat) {
                                $seat[0].classList.add('lieFlat');
                                model.hasLieFlats = true;
                            }
                            if (model.seats[name].isRearFacing) {
                                model.hasRearFacing = true;
                            }
                            if (model.seats[name].isExtraComfort) {
                                $seat[0].classList.add('extraComfort');
                                model.hasExtraComfort = true;
                                if (model.seats[name].price) {
                                    model.extraComfortPrice = model.seats[name].price;
                                }
                            }
                            if (model.seats[name].isPreferred) {
                                $seat[0].classList.add('preferred');
                                model.hasPreferred = true;
                                if (model.seats[name].price) {
                                    model.preferredSeatPrice = model.seats[name].price;
                                }
                            }
                            if (model.seats[name].isStandard) {
                                $seat[0].classList.add('standard');
                            }

                            if (model.seats[name].isUnavailable) {

                                if (model.seats[name].isLieFlat) {
                                    $seat.find('line').attr('stroke', 'none');
                                    $seat.find('path').attr('fill', colors.gray);
                                    drawXForSeat($seat, 7.7);
                                } else {
                                    $rect.attr('fill', colors.gray);
                                    drawXForSeat($seat, 7);
                                }
                            }
                            else {
                                $seat[0].classList.add('available');

                                if (model.seats[name].isFirstClass) {
                                    model.availableFirstClassSeats++;
                                }
                                if (model.seats[name].isExtraComfort) {
                                    model.availableExtraComfortSeats++;
                                    model.seats[name].Type = 1;
                                }
                                if (model.seats[name].isPreferred) {
                                    model.availablePreferredSeats++;
                                    model.seats[name].Type = 2;
                                }
                                // add text to seat (for variable seat pricing)
                                /*
								if (!$text[0] && !model.seats[name].isFirstClass) {
									var coords = [Number($rect.attr('x')), Number($rect.attr('y'))],
										tx = coords[0] + 17,
										ty = coords[1] + 22,
										txt = document.createElementNS('http://www.w3.org/2000/svg', 'text');
									txt.setAttribute('fill', '#fff');
									txt.setAttribute('x', tx);
									txt.setAttribute('y', ty);
									txt.setAttribute('text-anchor', 'middle');
									txt.classList.add('seat-price');
									txt.textContent = $filter('noFractionCurrency')(model.seats[name].price);
									$seat[0].appendChild(txt);
								}*/
                            }

                            updateSeat(name, model.seats[name], legIndex);

                        }); // end $('g[id^=seat]').each


                        // set text and style cabin headings
                        var firstClassLabel = ($scope.svc.legs[legIndex].IsInternational) ? $scope.svc.strings['businessclass'] : $scope.svc.strings['firstclass'];
                        var $headingFirstClass = $('#heading-firstClass');
                        styleAndPositionHeading($headingFirstClass, firstClassLabel, cabinHeaderClass, legIndex);
                        var $headingmainCabin = $('#heading-mainCabin');
                        styleAndPositionHeading($headingmainCabin, $scope.svc.strings.maincabin, cabinHeaderClass, legIndex);
                        /* no sub-heading until variable seat pricing
						var $subheadingmainCabin = $('#subheading-mainCabin');
						var subheading = '';
						if (highestPrice > 0) {
							subheading = $filter('noFractionCurrency')(0) +' - '+ $filter('noFractionCurrency')(highestPrice);
							subheading += ' more per seat';
						} else {
							subheading = 'No extra charge';
						}
						styleAndPositionHeading($subheadingmainCabin, subheading, 'bodycopy-sans-4', legIndex);
						*/

                        var renderEndTime = new Date().getTime();
                        var time = renderEndTime - renderStartTime;
                        $log.debug('rendered seatmap in ' + time + 'ms');

                        $('#svgSeatmap' + legIndex).find('svg').attr({
                            rendered: true,
                            id: 'svg' + legIndex
                        }).removeAttr('data-name').children('title').remove();

                        if ($rootScope.IsChangeFlightBooking) {
                            $scope.svc.applyAnyAvailableSeatCredits(legIndex);
                        }
                    }
                    else {
                        $('#svgSeatmap' + legIndex).find('svg').remove();
                    }
                    $timeout(function () {
                        $scope.$emit('haStickyResize');
                    });
                }

                // update seatmap for filtering, selection
                function updateSeatmapAtIndex(legIndex) {
                    var renderStartTime = new Date().getTime();
                    for (var name in $scope.svc.legs[legIndex].seatModel.seats) {
                        var seatObj = $scope.svc.legs[legIndex].seatModel.seats[name];
                        updateSeat(name, seatObj, legIndex);
                    }
                    var renderEndTime = new Date().getTime();
                    var time = renderEndTime - renderStartTime;
                    $log.debug('updated seatmap at index ' + legIndex + ' in ' + time + 'ms');
                }

                function styleAndPositionHeading($heading, string, typeClass, legIndex) {
                    if ($heading[0]) {
                        var $text = $heading.find('text'),
							transform = $text.attr('transform'),
							transformVal = transform.substr(transform.indexOf('(') + 1, transform.length - transform.indexOf('(') - 2),
							transformCoords = transformVal.split(' '),
							width = Number($('#svgSeatmap' + legIndex).find('svg').attr('width')),
							tx = width / 2,
							ty = Number(transformCoords[1]);
                        $text.removeAttr('font-family letter-spacing');
                        $text.attr({
                            transform: 'translate(' + tx + ' ' + ty + ')',
                            'text-anchor': 'middle',
                            class: typeClass
                        }).text(string);
                        var name = $heading.attr('id');
                        $heading.attr('id', legIndex + '-' + name);
                    }
                }

                // add √
                function drawCheckmarkForSeat($seat, name, legIndex) {

                    if ($('#checkmark' + name)[0]) { return; }

                    var checkmark = document.createElementNS('http://www.w3.org/2000/svg', 'path'),
						drawCheckmark1 = 'l-2.217-2.217c-0.307-0.306-0.676-0.457-1.111-0.457c-0.438,0-0.801,0.151-1.107,0.457',
						drawCheckmark2 = 'l-4.792-4.812c-0.305-0.305-0.674-0.455-1.109-0.455c-0.434,0-0.803,0.15-1.107,0.455l-2.218,2.218c-0.306,0.306-0.457,0.675-0.457,1.109s0.151,0.804,0.457,1.11l8.12,8.118c0.306,0.306,0.675,0.456,1.109,0.456c0.434,0,0.804-0.15,1.108-0.456l14.022-14.021c0.303-0.305,0.459-0.674,0.459-1.109',
						rectX = Number($seat.find('rect').attr('x')),
						rectY = Number($seat.find('rect').attr('y')),
						rectWidth = Number($seat.find('rect').attr('width')),
						translate = (rectWidth - 34) / 2,
						cx = rectX + 29,
						cy = rectY + 10,
						moveto = 'M' + cx + ',' + cy,
						lx = cx - 15.134,
						ly = cy + 8.498,
						lineto = 'L' + lx + ',' + ly,
						c1x = cx + 0.459,
						c1y = cy + 0.677,
						c2x = cx + 0.303,
						c2y = cy + 0.305,
						curveto = 'C' + c1x + ',' + c1y + ',' + c2x + ',' + c2y + ',' + cx + ',' + cy + 'z',
						draw = moveto + drawCheckmark1 + lineto + drawCheckmark2 + curveto;
                    checkmark.setAttribute('fill', '#fff');
                    checkmark.setAttribute('d', draw);
                    checkmark.setAttribute('id', 'checkmark' + legIndex + '-' + name);
                    checkmark.setAttribute('transform', 'translate(' + translate + ',' + translate + ')');
                    $seat[0].appendChild(checkmark);
                }

                // add unavailable "x"
                function drawXForSeat($seat, inset) {
                    var line1 = document.createElementNS('http://www.w3.org/2000/svg', 'line'),
						line2 = document.createElementNS('http://www.w3.org/2000/svg', 'line'),
						rectX = Number($seat.find('rect').attr('x')),
						rectY = Number($seat.find('rect').attr('y')),
						rectWidth = Number($seat.find('rect').attr('width')),
						farInset = rectWidth - inset;
                    line1.setAttribute('x1', rectX + inset);
                    line1.setAttribute('y1', rectY + inset);
                    line1.setAttribute('x2', rectX + farInset);
                    line1.setAttribute('y2', rectY + farInset);
                    line2.setAttribute('x1', rectX + farInset);
                    line2.setAttribute('y1', rectY + inset);
                    line2.setAttribute('x2', rectX + inset);
                    line2.setAttribute('y2', rectY + farInset);
                    line1.setAttribute('stroke', colors.stroke);
                    line2.setAttribute('stroke', colors.stroke);
                    line1.setAttribute('stroke-linecap', 'round');
                    line2.setAttribute('stroke-linecap', 'round');
                    $seat[0].appendChild(line1);
                    $seat[0].appendChild(line2);
                }
            }
        };
    }]);

})(angular);

/*
 * classList.js: Cross-browser full element.classList implementation.
 * 1.1.20150312
 *
 * By Eli Grey, http://eligrey.com
 * License: Dedicated to the public domain.
 *   See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
 */

/* jshint ignore:start */

if ("document" in self) {

    // Full polyfill for browsers with no classList support
    // Including IE < Edge missing SVGElement.classList
    if (!("classList" in document.createElement("_")) || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg", "g"))) {

        (function (view) {

            "use strict";

            if (!('Element' in view)) { return; }

            var classListProp = "classList",
				protoProp = "prototype",
				elemCtrProto = view.Element[protoProp],
				objCtr = Object,
				strTrim = String[protoProp].trim || function () {
				    return this.replace(/^\s+|\s+$/g, "");
				},
				arrIndexOf = Array[protoProp].indexOf || function (item) {
				    var i = 0,
						len = this.length;
				    for (; i < len; i++) {
				        if (i in this && this[i] === item) {
				            return i;
				        }
				    }
				    return -1;
				},
				// Vendors: please allow content code to instantiate DOMExceptions
				DOMEx = function (type, message) {
				    this.name = type;
				    this.code = DOMException[type];
				    this.message = message;
				},
				checkTokenAndGetIndex = function (classList, token) {
				    if (token === "") {
				        throw new DOMEx(
							"SYNTAX_ERR",
							"An invalid or illegal string was specified"
						);
				    }
				    if (/\s/.test(token)) {
				        throw new DOMEx(
							"INVALID_CHARACTER_ERR",
							"String contains an invalid character"
						);
				    }
				    return arrIndexOf.call(classList, token);
				},
				ClassList = function (elem) {
				    var trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
						classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
						i = 0,
						len = classes.length;
				    for (; i < len; i++) {
				        this.push(classes[i]);
				    }
				    this._updateClassName = function () {
				        elem.setAttribute("class", this.toString());
				    };
				},
				classListProto = ClassList[protoProp] = [],
				classListGetter = function () {
				    return new ClassList(this);
				};
            // Most DOMException implementations don't allow calling DOMException's toString()
            // on non-DOMExceptions. Error's toString() is sufficient here.
            DOMEx[protoProp] = Error[protoProp];
            classListProto.item = function (i) {
                return this[i] || null;
            };
            classListProto.contains = function (token) {
                token += "";
                return checkTokenAndGetIndex(this, token) !== -1;
            };
            classListProto.add = function () {
                var tokens = arguments,
					i = 0,
					l = tokens.length,
					token,
					updated = false;
                do {
                    token = tokens[i] + "";
                    if (checkTokenAndGetIndex(this, token) === -1) {
                        this.push(token);
                        updated = true;
                    }
                }
                while (++i < l);

                if (updated) {
                    this._updateClassName();
                }
            };
            classListProto.remove = function () {
                var tokens = arguments,
					i = 0,
					l = tokens.length,
					token,
					updated = false,
					index;
                do {
                    token = tokens[i] + "";
                    index = checkTokenAndGetIndex(this, token);
                    while (index !== -1) {
                        this.splice(index, 1);
                        updated = true;
                        index = checkTokenAndGetIndex(this, token);
                    }
                }
                while (++i < l);

                if (updated) {
                    this._updateClassName();
                }
            };
            classListProto.toggle = function (token, force) {
                token += "";

                var result = this.contains(token),
					method = result ? force !== true && "remove" : force !== false && "add";

                if (method) {
                    this[method](token);
                }

                if (force === true || force === false) {
                    return force;
                } else {
                    return !result;
                }
            };
            classListProto.toString = function () {
                return this.join(" ");
            };

            if (objCtr.defineProperty) {
                var classListPropDesc = {
                    get: classListGetter,
                    enumerable: true,
                    configurable: true
                };
                try {
                    objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
                } catch (ex) { // IE 8 doesn't support enumerable:true
                    if (ex.number === -0x7FF5EC54) {
                        classListPropDesc.enumerable = false;
                        objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
                    }
                }
            } else if (objCtr[protoProp].__defineGetter__) {
                elemCtrProto.__defineGetter__(classListProp, classListGetter);
            }

        }(self));

    } else {
        // There is full or partial native classList support, so just check if we need
        // to normalize the add/remove and toggle APIs.

        (function () {
            "use strict";

            var testElement = document.createElement("_");

            testElement.classList.add("c1", "c2");

            // Polyfill for IE 10/11 and Firefox <26, where classList.add and
            // classList.remove exist but support only one argument at a time.
            if (!testElement.classList.contains("c2")) {
                var createMethod = function (method) {
                    var original = DOMTokenList.prototype[method];

                    DOMTokenList.prototype[method] = function (token) {
                        var i, len = arguments.length;

                        for (i = 0; i < len; i++) {
                            token = arguments[i];
                            original.call(this, token);
                        }
                    };
                };
                createMethod('add');
                createMethod('remove');
            }

            testElement.classList.toggle("c3", false);

            // Polyfill for IE 10 and Firefox <24, where classList.toggle does not
            // support the second argument.
            if (testElement.classList.contains("c3")) {
                var _toggle = DOMTokenList.prototype.toggle;

                DOMTokenList.prototype.toggle = function (token, force) {
                    if (1 in arguments && !this.contains(token) === !force) {
                        return force;
                    } else {
                        return _toggle.call(this, token);
                    }
                };

            }

            testElement = null;
        }());

    }
}
/* jshint ignore:end */
;
(function (angular) {

	'use strict';

	var module = angular.module('haVerticalSeatSelectionModule', ['ui.router', 'ui.bootstrap', 'ngSanitize', 'haSeatMapAPI']);
    var upgraded = location.hash.toLowerCase().indexOf('upgraded') > -1;

	if ((location.pathname.toLowerCase().indexOf('/book/inflightoptions') > -1) ||
		(location.pathname.toLowerCase().indexOf('/uidocs/demo/verticalseatmap') > -1) ||
		(location.pathname.toLowerCase().indexOf('/my-account/my-trips/select-or-upgrade-seats') > -1)) {

		module.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
			try {
				$stateProvider.state('index', {
					url: '/flight/:flightId'
				});
				$urlRouterProvider.otherwise('/flight/1');
			} catch (error) {
				console.error('Tried to establish "index" state');
			}
		}]);
	}

	// Author: Jamie Perkins
	// the entire seat selection package - pax, tabs, legend, seatmap, etc
	module.directive('haVerticalSeatSelection', ['$rootScope', 'haGlobals', 'haVerticalSeatmapService', '$timeout', 'haConfig', '$state', '$stateParams', 'haSeatMapAPI', '$log', 'haModal', '$window', 'haSitecoreStrings', '$interval', '$filter', function ($rootScope, haGlobals, haVerticalSeatmapSvc, $timeout, haConfig, $state, $stateParams, haSeatMapAPI, $log, haModal, $window, $scs, $interval, $filter) {
		return {
			restrict: 'A',
			scope: {},
			templateUrl: haConfig.getTemplateUrl('VerticalSeatmap/ha-vertical-seat-selection.html'),
			link: function ($scope, element, attrs) {

				$scope.upgradedToMainCabin = upgraded && !$rootScope.IsMainCabinUpgradeAvailable;
				$scope.standalone = typeof attrs.standalone === "string";

				$rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off

				// In some cases we need to use the VSM in existing mini SPAS
				// so we mock the ui-router features and hook straight into
				// the VSM state change handlers
				var disableRouting = typeof attrs.disableRouting === "string";
				var mcbSeatMapModal = "mcbSeatMapModal";
				if (disableRouting) {
					// We can catch any ui-sref directives here and pipe them through our phony $state.
					$scope.$on('$stateNotFound', function (event, unfoundState, fromState, fromParams) {
						event.preventDefault();
						$state.go(unfoundState.to, unfoundState.toParams)
					});

					// Override $state and $stateParams
					$state = {
						go: function (route, params) {
							if (route !== 'index' || !params) {
								throw 'Invalid call to mock $state.go with disabledRouting set.';
							}
							$stateParams = params;
							$scope.$broadcast('$stateChangeSuccess');
						}
					};

					// TODO: hardcoded for now
					$stateParams = {
						flightId: 1
					}
				}
                $scope.svc = haVerticalSeatmapSvc;
                $scope.entertaimentTooltipVisible = false;
                $scope.inFlightEntertainmentInfo = null;
                $scope.inFlightEntertainmentAvailable = null;
                $rootScope.adultPresent = { PaxType: 0 };
                $scope.upgradeSeatAvailable = false;
                $scope.MainCabinPriceDifference = $rootScope.MainCabinPriceDifference;
                $scope.upgradeToMainCabin = function () {
	                $scope.svc.upgradeToMainCabin()
		                .then(function () {
			                document.body.dispatchEvent(new CustomEvent('UpgradeToMainCabin', {
				                'detail': {
				                    'pageName': window.digitalData.page.pageInfo.name + ":" +  mcbSeatMapModal
				                }
			                }));
			                $window.location.href = $window.location.pathname + '?#upgraded';
		                });
                };

                haGlobals('HA', function (ha) {
                    if (ha && ha.SCStrings && ha.SCStrings.entertainment_data) {
                        $scope.inFlightEntertainmentInfo = ha.SCStrings.entertainment_data;
                    }
					if ($rootScope.IsMainCabinUpgradeAvailable) {
						haModal({
						    id: mcbSeatMapModal,
							templateUrl: '/Templates/VerticalSeatmap/ha-seatmap-MCB-upgrade.html',
							backdrop: 'true',
							modalLock: true,
							size: 'modal-md',
							scope: $scope
						});
					}
				});

				if (getUrlVars()['user']) {
					$scope.svc.selectedPassengerIndex = +getUrlVars()['user'].replace(/\/#\/flight\/./, '');
				}

				if ($rootScope.PassengerTripSummary) {
					$rootScope.adultPresent = $filter('filter')($rootScope.PassengerTripSummary.Passengers, { PaxType: 0 })[0];
				}
				if (window.sessionStorage.getItem('haMinor') && window.sessionStorage.getItem('haMinor') !== 'false') {
					$rootScope.adultPresent = { PaxType: 1 };
				}
				$scope.fareRulesLink = $window.fareRulesLink;
				$scope.showSeatingFilters = false;

				// get VM with segments, equipment type, passengers, etc.
				$scope.svc.getFlightVM();

				$scope.checkIFE = function (aircraft) {
					if (!aircraft) {
						return false;
					}
					var ifeOffered = false;
					if ($scope.inFlightEntertainmentInfo !== undefined && $scope.inFlightEntertainmentInfo.airplanes.length) {
						for (var i = 0; i < $scope.inFlightEntertainmentInfo.airplanes.length; i++) {
							var planes = $scope.inFlightEntertainmentInfo.airplanes[i];
							if (planes['airplane name'].toLowerCase() === aircraft.toLowerCase()) {
								ifeOffered = true;
							}
						}
					}
					//Check if In Flight Entertainment is available on this trip
					if (ifeOffered) {
						$scope.inFlightEntertainmentAvailable = ifeOffered;
					}
					return ifeOffered;
				}


				$scope.$watch('svc.pageLoading', function () {

					if ($scope.svc.pageLoading === false) {
						$scope.svc.activeLegIndex = $stateParams.flightId - 1;
						if ($scope.svc.legs && $scope.svc.legs.length) {
							if (!$scope.svc.legs[$scope.svc.activeLegIndex]) {
								// leg entered in url which does not exist. use first leg
								$state.go('index', { flightId: 1 });
								return;
							}
							if ($scope.inFlightEntertainmentInfo === null) {
								$scope.svc.loadIFE().then(function (data) {
									$scope.inFlightEntertainmentInfo = data[0];
									renderSeatmapForLegIndex($scope.svc.activeLegIndex);
								});
							} else {
								renderSeatmapForLegIndex($scope.svc.activeLegIndex);
							}


							// if initial leg is codeshare, trigger bkgd load of other leg
							if ($scope.svc.legs[$scope.svc.activeLegIndex].IsCodeShare) {
								$scope.svc.getNextLeg();
							}
						}
						else {
							$scope.svc.handleError('Missing leg data for flights.');
						}
					}
				});

				$scope.$on('$stateChangeSuccess', function () {
					if (!$scope.svc.pageLoading && $stateParams.flightId - 1 !== $scope.svc.activeLegIndex && $scope.svc.legs[$stateParams.flightId - 1]) {
						$scope.$broadcast('cancelTooltip');
						$scope.svc.activeLegIndex = $stateParams.flightId - 1;
						$rootScope.$broadcast('seatSelectionLegChanged');
						renderSeatmapForLegIndex($scope.svc.activeLegIndex);
						document.documentElement.scrollTop = document.body.scrollTop = 0; // scroll to top
					}
				});

				function getUrlVars() {
					var vars = [], hash;
					var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
					for (var i = 0; i < hashes.length; i++) {
						hash = hashes[i].split('=');
						vars.push(hash[0]);
						vars[hash[0]] = hash[1];
					}
					return vars;
				}

				function renderSeatmapForLegIndex(legIndex) {
					if (haVerticalSeatmapSvc.disallowAdvanceSeatSelection($scope.svc.legs[legIndex], $scope.$switch('InFlightOptionsInfo:EnableFAARestriction'), 'ppg', $scope.svc)) {
						return haVerticalSeatmapSvc.setFAARestriction($scope.svc.legs[$scope.svc.activeLegIndex], $scope.svc);
					}

					if (!$scope.svc.legs[legIndex].IsCodeShare) {

						$scope.svc.legs[legIndex].error = null;

						if (!$scope.svc.legs[legIndex].promise) {
							$scope.svc.legs[legIndex].promise = $scope.svc.getAvailableSeatsForLeg(legIndex);
						}

						$scope.svc.legs[legIndex].promise.then(function () {
							// needs to be sequential because loading svg depends on equip type from seats call
							$scope.svc.loadSvgSeatmapForLeg(legIndex).then(function () {

								// conditional filtering/unavailability of cabins
								resetFilters();
								checkForUpgradableSeats();

								var fareClass = $scope.svc.legs[legIndex].SelectedFareClass;
								if ((fareClass === 'first' || fareClass === 'business') && $scope.svc.legs[legIndex].EquipmentCode !== 'AT5') {
									filterMainCabin();
									// only in reshop can user downgrade from first
									if (!$scope.IsChangeFlightBooking) {
										$scope.svc.legs[legIndex].disableMainCabin = true;
									}
								}
								// make FC cabin unavailable:
								if (fareClass !== 'first' && fareClass !== 'business') {
									// if changing seats
									if ($scope.svc.isChangeSeats) {
										$scope.svc.legs[legIndex].disableFirstClass = true;
										filterFirstClass();
									}
									// under certain booking conditions, disable first
									else if ($rootScope.PassengerTripSummary) {
										if (// E-cert
											($rootScope.PassengerTripSummary.SelectedPromo !== null) ||
											// Miles Redemption
											($rootScope.PassengerTripSummary.BookingType > 0) ||
											// Purchased miles
											($rootScope.PassengerTripSummary.MilesPurchaseDetails !== null) ||
											// Orbitz APH Path
											($rootScope.PassengerTripSummary.SelectedHotel !== null)) {

											$scope.svc.legs[legIndex].disableFirstClass = true;
											filterFirstClass();
										}
									}
								}

								// pre-assign seat selections if they exist
								angular.forEach($scope.svc.passengers, function (pax) {

									if (pax.Seats[legIndex].SeatLocation) {
										$scope.svc.legs[legIndex].seatModel.seats[pax.Seats[legIndex].SeatLocation] = {
											price: pax.Seats[legIndex].SeatAmount,
											isSelected: true,
											pax: pax
										};
										if (pax.Seats[legIndex].SeatAmount > 0) {
											pax.Seats[legIndex].SurchargeOk = true;
										}
										$scope.svc.includeSeatNumber = true;
									}
								});

								if (!$scope.svc.legs[legIndex].resolved) {
									$scope.svc.legs[legIndex].resolved = true;
									$rootScope.$broadcast('legResolvedAtIndex', legIndex);
								}

								$scope.$broadcast('renderSeatmapAtIndex', legIndex);
								calculateSeatsTotal();
								$scope.$emit('haStickyResize');

								// for mobile, adjust height for svg scaling
								if ($rootScope.isMobile) {
									var $svg = $('#svgSeatmap' + legIndex).find('svg');
									if (!$svg.attr('scaled')) {
										var waitForRender = $interval(function () {
											if ($('#svgSeatmap' + legIndex).outerWidth() > 100) {
												$interval.cancel(waitForRender);
												var initialWidth = parseFloat($svg.attr('width')),
													initialHeight = parseFloat($svg.attr('height')),
													scaledWidth = $('#svgSeatmap' + legIndex).outerWidth(),
													scaleFactor = scaledWidth / initialWidth,
													scaledHeight = scaleFactor * initialHeight;
												$log.debug('svg scale factor:', scaleFactor);
												$scope.svc.svgScaleFactor = scaleFactor;
												$svg.attr({
													height: scaledHeight,
													scaled: true
												});
											}
										}, 50);
									}
								}

							}, handlePromiseError);

						}, handlePromiseError);
					}
				}

				function handlePromiseError(error) {
					$scope.svc.legs[$scope.svc.activeLegIndex].resolved = true;
					$scope.svc.legs[$scope.svc.activeLegIndex].error = error;
					$log.error(error);
				}

				function checkForUpgradableSeats() {
					if ($scope.svc.legs && $scope.svc.legs.length && !$scope.upgradeSeatAvailable) {
						$scope.svc.legs.forEach(function (leg) {
							if (typeof leg.seatModel !== 'undefined' && typeof leg.seatModel.seats !== 'undefined') {
								for (var seat in leg.seatModel.seats) {
									if (leg.seatModel.seats[seat].price > 0) {
										$scope.upgradeSeatAvailable = true;
										break;
									}
								}
							}
						});
					}
				}

				function getSelectedFareClass(seat) {
					if (seat.isStandard) {
						return 'coach';
					}
					if (seat.isPreferred) {
						return 'preferred';
					}
					if (seat.isExtraComfort) {
						return 'extracomfort';
					}
					if (seat.isFirstClass) {
						return 'first';
					}
				}

				// SELECTING SEATS

				$scope.$on('seatWasSelectedOrDeselected', function (event, data) {
					$scope.selectSeat(data);
					$scope.svc.includeSeatNumber = true;
				});

				var seatConfirmed = false; // for eliminating double-confirms on mobile

				$scope.assignSeat = function () {
					$scope.$emit('closeModal');
					seatConfirmed = false;
					var seatObj = $scope.seat;
					var pax = $scope.svc.passengers[$scope.svc.selectedPassengerIndex],
						paxSeat = pax.Seats[$scope.svc.activeLegIndex],
						fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;
					// if some other seat was selected, deselect it
					if (paxSeat.SeatLocation) {
						$scope.svc.legs[$scope.svc.activeLegIndex].seatModel.seats[paxSeat.SeatLocation].isSelected = false;
					}
					// finally, select seat
					seatObj.isSelected = true;
					paxSeat.SeatLocation = seatObj.name;
					paxSeat.SeatAmount = seatObj.price;
					paxSeat.Type = seatObj.Type;
					if (paxSeat.originalSeat) {
						paxSeat.originalSeat.isChanged = false;
					}
					seatObj.pax = pax; // assign pax for tooltip

					// IsUpgrade used in TripSummary.html
					paxSeat.IsUpgrade = seatObj.Type > 0 && /coach/gi.test(fareClass);

					// isSurcharge used for showing seat charges on seat selection
					if ((seatObj.Type === 1 && fareClass !== 'extracomfort') || (seatObj.Type === 2 && fareClass !== 'preferred')) {
						paxSeat.isSurcharge = true;
					} else {
						paxSeat.isSurcharge = false;
					}

					$rootScope.$broadcast('ancelaryStateChange');
					$rootScope.$broadcast('refreshSeatmapTooltip');
					$scope.$broadcast('updateSeatmapAtIndex', $scope.svc.activeLegIndex);
					nextPassenger();
					calculateSeatsTotal();
				}

				$scope.selectSeat = function (seatObj) {

					var pax = $scope.svc.passengers[$scope.svc.selectedPassengerIndex],
						paxSeat = pax.Seats[$scope.svc.activeLegIndex];

					if (typeof paxSeat.SeatLocation === 'undefined' || paxSeat.SeatLocation === null || paxSeat.SeatLocation.length === 0) {
						var fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;
					} else {
						var fareClass = getSelectedFareClass($scope.svc.legs[$scope.svc.activeLegIndex].seatModel.seats[paxSeat.SeatLocation]);
					}

					// deselect seat
					if (seatObj.isSelected) {
						delete seatObj.pax;
						seatConfirmed = false;
						angular.forEach($scope.svc.passengers, function (passenger, i) {
							if (passenger.Seats[$scope.svc.activeLegIndex].SeatLocation &&
								passenger.Seats[$scope.svc.activeLegIndex].SeatLocation === seatObj.name) {
								seatObj.isSelected = false;
								passenger.Seats[$scope.svc.activeLegIndex].SeatLocation = null;
								passenger.Seats[$scope.svc.activeLegIndex].SeatAmount = null;
								passenger.Seats[$scope.svc.activeLegIndex].Type = 0;
								$scope.svc.selectedPassengerIndex = i;
								$rootScope.$broadcast('ancelaryStateChange'); // ugh spelling
								if ($rootScope.isMobile) {
									$scope.$broadcast('cancelTooltip');
								} else {
									$rootScope.$broadcast('refreshSeatmapTooltip');
								}
								$scope.$broadcast('updateSeatmapAtIndex', $scope.svc.activeLegIndex);
								calculateSeatsTotal();
								return;
							}
						});
					}
					// select seat
					else {

						// confirm First Class upgrade
						if (seatObj.isFirstClass && (fareClass !== 'first' && fareClass !== 'business')) {
							$timeout(function () {
								haModal({
									id: 'ConfirmUpgradeModal',
									backdrop: 'true',
									templateUrl: '/upgradeToFirstModal.html',
									scope: $scope
								});
							});
							return;
						}

						// confirm EC/Preferred upgrade
						if (!paxSeat.SurchargeOk && (
							// EC fare
							(!$scope.svc.isChangeSeats && seatObj.isExtraComfort && (fareClass !== 'extracomfort' || paxSeat.DowngradeOk)) ||
							// PS fare
							(seatObj.isPreferred && (fareClass !== 'preferred' || paxSeat.DowngradeOk)) ||
							// seat credit
							(paxSeat.originalSeat && paxSeat.originalSeat.price < seatObj.price && !paxSeat.originalSeat.isChanged) ||
							// change seats
							($scope.svc.isChangeSeats && seatObj.isExtraComfort && !paxSeat.originalSeat))) {

							$scope.seat = seatObj;
							$timeout(function () {
								haModal({
									id: 'ConfirmUpgradeModal',
									backdrop: 'true',
									templateUrl: '/upgradeSeatModal.html',
									scope: $scope
								});
							});

							return;
						}

						// confirm downgrade / difference
						if (!paxSeat.DowngradeOk && fareClass !== 'first' && (
							// EC fare
							(fareClass === 'extracomfort' && $scope.svc.legs[$scope.svc.activeLegIndex].seatModel.availableExtraComfortSeats > 0 && !seatObj.isExtraComfort) ||
							// PS fare
							(fareClass === 'preferred' && $scope.svc.legs[$scope.svc.activeLegIndex].seatModel.availablePreferredSeats > 0 && !seatObj.isPreferred) ||
							// seat credit
							(paxSeat.originalSeat && paxSeat.originalSeat.price > seatObj.price && !paxSeat.originalSeat.isChanged))) {

							$scope.seat = seatObj;
							$scope.originalSeat = paxSeat.originalSeat;

							if (!seatObj.isExtraComfort) {
								$timeout(function () {
									haModal({
										id: 'ConfirmDowngradeModal',
										backdrop: 'true',
										templateUrl: '/downgradeSeatModal.html',
										scope: $scope
									});
								});
								return;
							} else if (!paxSeat.DifferenceOk) {
								$timeout(function () {
									haModal({
										id: 'ConfirmDowngradeModal',
										backdrop: 'true',
										templateUrl: '/differenceSeatModal.html',
										scope: $scope
									});
								});
								return;
							}

							if (!paxSeat.DifferenceOk) {
								return;
							}
						}

						// confirm exit row compliance
						if (seatObj.isExitRow && !paxSeat.ExitOk) {
							$scope.seat = seatObj;
							$timeout(function () {
								haModal({
									id: 'ConfirmExitRowModal',
									backdrop: 'true',
									template: angular.element('.confirmExitRowModal'),
									scope: $scope
								});
							});
							return;
						}

						// confirm selection if mobile
						if ($rootScope.isMobile) {
							if (seatConfirmed) {
								$scope.assignSeat();
							} else {
								// determine seat "charge"
								seatObj.charge = 0;
								if ((seatObj.Type === 1 && fareClass !== 'extracomfort') || (seatObj.Type === 2 && fareClass !== 'preferred')) {
									seatObj.charge = seatObj.price;
								}
								$scope.seat = seatObj;
								// confirm modal
								$timeout(function () {
									haModal({
										id: 'ConfirmSelectionMobileModal',
										backdrop: 'true',
										templateUrl: '/confirmSelectionMobileModal.html',
										scope: $scope
									});
								});
							}
						}
						// else assign seat
						else {
							$scope.seat = seatObj;
							$scope.assignSeat();
						}
					}
				};

				$scope.seatsChanged = function () {
					var changed = false;
					angular.forEach($scope.svc.passengers, function (passenger, i) {
						angular.forEach(passenger.Seats, function (seat, j) {
							if (seat.SeatLocation && seat.SeatLocation !== $scope.svc.passengersInitial[i].Seats[j].SeatLocation) {
								changed = true;
							}
						});
					});

					return changed;
				};

				$scope.confirmUpgrade = function () {
					$scope.$emit('closeModal');
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].DowngradeOk = false;
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].SurchargeOk = true;
					if ($scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat) {
						$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat.isChanged = true;
					}
					seatConfirmed = true;
					$scope.selectSeat($scope.seat);

					var offerType = '';
					if ($scope.seat.isExtraComfort) {
						offerType = 'extracomfort';
					} else if ($scope.seat.isPreferred) {
						offerType = 'preferred';
					}
					document.body.dispatchEvent(new CustomEvent('UpgradeSeatSelected', { 'detail': {
						'seatType': offerType
					}}));
				};

				$scope.confirmDowngrade = function () {
					$scope.$emit('closeModal');
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].DowngradeOk = true;
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].SurchargeOk = false;
					if ($scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat) {
						$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat.isChanged = true;
					}
					seatConfirmed = true;
					$scope.selectSeat($scope.seat);
				};

				// if a user has a higher valued EC seat, and selects a new flight with a lower valued EC seat
				$scope.confirmDifference = function () {
					// if EC/PC seat is selected and pax previously downgraded, add back the PremiumSeatAmount
					if ($rootScope.PassengerTripSummary && $scope.seat.Type > 0 &&
						$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].DowngradeOk) {
						$rootScope.PassengerTripSummary.Passengers[$scope.svc.selectedPassengerIndex].PremiumSeatAmount += $scope.seat.price;
					}
					var legSeatAmount = $scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].SeatAmount;
					if ($rootScope.PassengerTripSummary) {
						$rootScope.PassengerTripSummary.Passengers[$scope.svc.selectedPassengerIndex].PremiumSeatAmount -= legSeatAmount;
					}
					$scope.$emit('closeModal');
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].SurchargeOk = true;
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].DifferenceOk = true;
					if ($scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat) {
						$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat.isChanged = true;
					}
					seatConfirmed = true;
					$scope.selectSeat($scope.seat);
				};


				$scope.confirmExitRow = function () {
					$scope.$emit('closeModal');
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].ExitOk = true;
					$scope.selectSeat($scope.seat);
				};

				$scope.scrollToTop = function () {
					var offsetElem = ($('#SeatSelection').length) ? '#SeatSelection' : 'body';
					var offset = $(offsetElem).offset().top;
					$('body, html').animate({ scrollTop: offset }, 'fast');
				};

				function calculateSeatsTotal() {
					$timeout(function () { // ensures digest cycle is triggered
						$scope.svc.seatChargeSubtotals[$scope.svc.activeLegIndex] = 0;
						angular.forEach($scope.svc.passengers, function (passenger) {

							var passengerSeat = passenger.Seats[$scope.svc.activeLegIndex],
								cost = passengerSeat.SeatAmount,
								fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;

							// do they have a seat credit? (change seats/reshop)
							if (passengerSeat.originalSeat && passengerSeat.originalSeat.credit > 0) {
								cost -= passengerSeat.originalSeat.credit;
							}
							// did they buy an EC/PS fare
							else if (passengerSeat.IsUpgrade != null && !passengerSeat.IsUpgrade) {
								cost -= passengerSeat.SeatAmount;
							}
							passengerSeat.SeatAmountDisplay = cost;
							$scope.svc.seatChargeSubtotals[$scope.svc.activeLegIndex] += cost;

						});
					}, 0);
				}

				function nextPassenger() {
					if ($scope.svc.selectedPassengerIndex === $scope.svc.passengers.length - 1) {
						$scope.svc.selectedPassengerIndex = 0;
					} else {
						$scope.svc.selectedPassengerIndex++;
					}
				}

				$scope.paxHaveSeatsForLegCount = function () {
					var count = 0;
					for (var i = 0, len = $scope.svc.passengers.length; i < len; i++) {
						if ($scope.svc.passengers[i].Seats[$scope.svc.activeLegIndex].SeatLocation) {
							count++;
						}
					}
					return count;
				};

				// FILTERING

				function filterFirstClass() {
					for (var filter in $scope.svc.seatTypeFilters) {
						if (filter === 'first') {
							$scope.svc.seatTypeFilters[filter].value = false;
						}
					}
				}
				function filterMainCabin() {
					for (var filter in $scope.svc.seatTypeFilters) {
						if (filter === 'standard' || filter === 'extra' || filter === 'preferred') {
							$scope.svc.seatTypeFilters[filter].value = false;
						}
					}
				}
				function resetFilters() {
					for (var filter in $scope.svc.seatTypeFilters) {
						$scope.svc.seatTypeFilters[filter].value = true;
					}
				}

				$scope.$watch('svc.seatTypeFilters', function () {
					$scope.$broadcast('updateSeatmapAtIndex', $scope.svc.activeLegIndex);
				}, true);

				$scope.$watch('svc.rangeFilter', function () {
					$scope.$broadcast('updateSeatmapAtIndex', $scope.svc.activeLegIndex);
				}, true);


				function seatTypeToSeatTypeString(type) {
					switch (type) {
						case 2:
							return 'Preferred';
						case 1:
							return 'ExtraComfort';
						default:
							return 'Standard';
					}
				}

				// STANDALONE BUTTON CONTROLS
				$scope.submitStandalone = function () {
					if (!$scope.seatsChanged()) {
						$scope.cancelStandalone();
						return;
					}
					var seatSelectionData = {};
					angular.forEach($scope.svc.legs, function (leg, i) {
						var legData = [];
						angular.forEach($scope.svc.passengers, function (passenger, j) {
							legData.push({
								id: passenger.TravelerID || passenger.ID,
								seat: passenger.Seats[i].SeatLocation,
								amount: passenger.Seats[i].SeatAmount
								//type: seatTypeToSeatTypeString(passenger.Seats[i].Type)
							})
						});
						seatSelectionData[leg.id] = legData;
					});
					$rootScope.$broadcast('$standaloneVerticalSeatmapSubmit', seatSelectionData);
				};
				$scope.cancelStandalone = function () {
					$rootScope.$broadcast('$standaloneVerticalSeatmapCancel');
				};

				// CANCEL SEAT CHANGE
				$scope.cancelSeatChange = function() {
					$window.location.href='/my-account/my-trips/itinerary-details';
				};

				// SUBMITTING SEATS
				$scope.submitSeatSelection = function (skipSeatSelection) {
					$scope.svc.setIFE($scope.inFlightEntertainmentAvailable).then($.noop, $.noop);

					// next flight button
					if (!skipSeatSelection && $scope.svc.activeLegIndex !== $scope.svc.legs.length - 1) {
						$scope.svc.selectedPassengerIndex = 0;
						$state.go('index', { flightId: $scope.svc.activeLegIndex + 2 });
						return;
					}

					if ($scope.standalone) {
						// If this is a standalone VSM we don't want to interact with the APIs here,
						// but rather leave that up to the context in which VSM is situated via
						// broadcasting events.
						$scope.submitStandalone();
						return;
					}


					if (!skipSeatSelection) {
						$scope.submitting = true;
					}

					var postObject = $scope.svc.createSeatsPostObject();
					$scope.svc.setUpgradableSeat($scope.upgradeSeatAvailable).finally(function () {
						// for change seats?
						if ($scope.svc.isChangeSeats) {
							delete postObject.InFlightOptions;
							delete postObject.TotalPremiumSeatAmount;
							//$log.debug('post object for change seats', postObject);
							// legacy endpoints first validate then submit
							haSeatMapAPI.ValidateEditSeatFareDifference(postObject).success(function (result) {
								$log.debug('validate seat fare diff result', result);
								switch (result) {
									case 1: // seats have been upgraded
										angular.element('#seatMapForm').submit();
										break;
									case 2: // seats have been downgraded
									case 3: // seats were changed with new seats of equal value
										haSeatMapAPI.ConfirmEditSeatsService(postObject).success(function (response) {
											$log.debug('confirm edit seats service response', response);
											angular.element('#seatMapForm').submit();
										}).error(function (error) {
											$scope.svc.handleError(error);
											$scope.submitting = false;
										});
										break;
									case 4: // no seats were changed
										$scope.svc.handleError(noSeatChangeErrorText);
										$scope.submitting = false;
										break;
									default: // should not happen
										$scope.svc.handleError('Unhandled result for ValidateEditSeatFareDifference request: ' + result);
										$scope.submitting = false;
										break;
								}

                            }).error(function (error) {
                                $scope.svc.handleError(error);
                                $scope.submitting = false;
                            });
                        }
                        // regular booking or reshop
                        else {
                            if ($rootScope.IsChangeFlightBooking) {
                                haSeatMapAPI.UpdateChangeFlightInflightoptions(postObject, JSON.stringify(postObject)).success(function () {
                                    angular.element('#seatMapForm').submit();
                                }).error(function () {
                                    angular.element('#seatMapForm').submit();
                                });
                            }
                            else {
                                haSeatMapAPI.UpdateInflightOptionsService(postObject, JSON.stringify(postObject)).success(function () {

									// Mpulse tracking - trip insurance quote request.
	                                if (window.BOOMR && BOOMR.version) {
		                                window.BOOMR.sendMetric("Alnz_Quote_Rq", 1);
									}

	                                angular.element('#seatMapForm').submit();
                                }).error(function (error) {
                                    $scope.svc.handleError(error);
									$scope.submitting = false;
                                });
                            }
						}
					});
				};

				var $body = $('body');
				$scope.upgradeTracking = function (type, leg) {
					var seatUpgradeOffer = '';
					if (type == "EXTRACOMFORT" && (!$body.data('UpgradeSeatOffered') || $body.data('UpgradeSeatOffered') !== leg)) {
						seatUpgradeOffer = 'extracomfort';
					}

					if (type == "PREFERREDSEAT" && (!$body.data('UpgradeSeatOffered') || $body.data('UpgradeSeatOffered') !== leg)) {
						seatUpgradeOffer = 'preferred';
					}

					if (seatUpgradeOffer) {
						$body.data('UpgradeSeatOffered', leg);
						document.body.dispatchEvent(new CustomEvent('UpgradeSeatOffered', { 'detail': {
							'seatType': seatUpgradeOffer
						}}));
					}

					return true;
				}
			}
		};
	}]);

	// for attaching InFlightVM to scope, for child directive's scope inheritance
	module.controller('haInFlightOptionsController', ['$scope', 'haGlobals', '$rootScope', 'haPassengersService', '$timeout', 'haVerticalSeatmapService', function ($scope, haGlobals, $rootScope, $pax, $timeout, haVerticalSeatmapSvc) {

		haGlobals('InFlightVM', function (InFlightVM) {
			$.extend($rootScope, InFlightVM);
		});

		haGlobals('infantlap', function (infantlap) {
			$rootScope.infantLapList = infantlap;
		});

		haGlobals('groupPNRSelectedPax', function (groupPNRSelectedPax) {
			$rootScope.groupPNRSelectedPax = groupPNRSelectedPax;
		});

		// update haPassengersService for sticky progress bar
		// picked up from $scope.updatePassengerList() in ha-seat-selection.js
		var firstUser = true;

		if ($rootScope.TripSummary) {
			angular.forEach($rootScope.TripSummary.Passengers, function (passenger) {

				var paxType = 'Adult';
				if (passenger.ActualPaxTypeSelected) {
					paxType = passenger.ActualPaxTypeSelected;
				} else if (passenger.Type) {
					paxType = passenger.Type;
				}

				if ($rootScope.IsChangeFlightBooking) {
					switch (passenger.PaxFareCode) {
						case 'ADT':
							paxType = 'Adult';
							break;
						case 'CHD':
							paxType = 'Child';
							break;
						default:
							paxType = 'Adult';
							break;
					}
				}

				$pax.add({
					type: paxType,
					isUser: firstUser,
					FirstName: passenger.FirstName,
					LastName: passenger.LastName,
					AvatarUrl: passenger.AvatarImage,
					Id: passenger.TravelerAssociationID
				});
				if (firstUser === true) {
					firstUser = false;
				}
			});
			// for ha-receipt
			//update selected segments from trip summary
			$scope.selectedSegments = [];
			angular.forEach($rootScope.TripSummary.Trips, function (trip) {
				var segment = trip.Flights[0];
				segment.IsMileagePricing = trip.IsMileagePricing;

				for (var i = 0; i < segment.AvailBookingFares.length; i++) {
					segment.selectedSeatClass = segment.AvailBookingFares[i].Name;
					segment[segment.selectedSeatClass] = segment.AvailBookingFares[i];
				}
				$scope.selectedSegments.push(segment);
			});

			$scope.$on('ancelaryStateChange', function () {
				// need to set $scope.seatSelection so that seat choices can be
				// reflected in ha-receipt for reshop
				$scope.seatSelection = haVerticalSeatmapSvc.createSeatsPostObject();
				$rootScope.TripSummary.TotalPremiumSeatAmount = $scope.seatSelection.TotalPremiumSeatAmount;
			});
		}

	}]);

})(angular);
;
(function (angular) {
	'use strict';

	var mod = angular.module('haKisaTermsModule', []);

	mod.directive('haKisaTermsCheckboxLaunch', ['$window', '$rootScope', 'haConfig', 'haModal', function ($window, $rootScope, haConfig, haModal) {

		return {
			restrict: 'A',
			scope: false,
			link: function ($scope, element, attrs) {
				var termsName = attrs.contentVar || 'termsAndConditions';

				// If a checkbox ngModel is present, hook it up to modal success/failure events
				if (attrs.ngModel) {
					$rootScope.$on('termsModalFailure', function(e, terms) {
						if (terms === termsName) {
							$scope.$eval(attrs.ngModel + ' = false');
						}
					});
					$rootScope.$on('termsModalSuccess', function(e, terms) {
						if (terms === termsName) {
							$scope.$eval(attrs.ngModel + ' = true');
						}
					});
				}

				// Generate and append label markup
				if ($window[termsName] && $window[termsName].ParentCheckBoxText) {
					element.after('<label for="' + attrs.id + '" class="required">' +
									$window[termsName].ParentCheckBoxText +
								   '</label>');
				}

				// Event handler to open modal
				$scope.termsStart = function() {
					if (!$window[termsName]) {
						return;
					}
					$rootScope.$broadcast('termsModalStart', termsName);

					haModal(haConfig.getTemplateUrl('ha-kisa-terms-modal.html'), {
						id: 'kisaTermsModal',
						backdrop: true,
						modalLock: true,
						scope: $scope,
						extendScope: {
							scContent: $window[termsName],
							termsName: termsName
						}
					});
				};
			}
		};
	}])

	mod.directive('haKisaTermsModal', ['$rootScope', 'haConfig', 'haModal', function ($rootScope, haConfig, haModal) {

		return {
			restrict: 'A',
			scope: false,
			link: function ($scope) {

				$rootScope[$scope.termsName] = $rootScope[$scope.termsName] || {
					allAccepted: false,
					themes: $scope.scContent.PrivacyPolicies
				}
				$scope.terms = $rootScope[$scope.termsName];

				$scope.termsToggleAll = function() {
					var changeto;

					if ( !$scope.terms.allAccepted ) {
						changeto = false;
					} else {
						changeto = true;
					}

					$scope.terms.themes.forEach( function(v, i) {
						v.accepted = changeto;
					});
				};

				$scope.termsToggleTheme = function(i) {
					if (!$scope.terms.themes[i].accepted) {
						$scope.terms.allAccepted = false;
					}
				};

				$scope.cancelTerms = function() {
					$rootScope.$broadcast('termsModalFailure', $scope.termsName);
					$scope.$modalCancel();
				};

				$scope.submitTerms = function() {
					$rootScope.$broadcast('termsModalSuccess', $scope.termsName);
					$scope.$modalSuccess();
				};
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module;
	try {
		module = angular.module('haFormNativeappLinkSmsModule');
	} catch (e) {
		module = angular.module('haFormNativeappLinkSmsModule', ['haHttpService', 'haGeoDataModule']);
	}

	module.directive('haFormNativeappLinkSms', [
		'$q',
		'haGlobals',
		'haHttpService',
		'haGeoDataSvc',

		function ($q, haGlobals, http, geo) {
			var link = function ($scope, el, attrs) {

				$scope.IsCountryDataReady = false;

				// Fetch list of Countries
				geo.getCountries().then(
					function (data) { // success
						$scope.countries = data;
						angular.extend($scope, {
							countryCodeData: geo.getPhoneCountryCodes()
						});

						// get phoneRegex by CountryCode
						// This should be called after countries data set in cache
						$scope.phoneRegexByCC = function (cc) {
							var country = geo.lookupCountryByCode(cc);
							if (country != null) {
								return geo.getPhoneNumberRegex(country.Key);
							}
							return /.*/;
						};

						$scope.phoneCountryCodeByCC = function (cc) {
							var countryByCC = $scope.countries.filter(function (country) {
								if (country.IsoCode === cc) {
									return country;
								}
							});

							if (countryByCC.length === 1) {
								return countryByCC[0].PhoneCountryCode;
							}
							return '';
						};

						$scope.IsCountryDataReady = true;
					},
					function () { // failure
						// failed in loading countries data. hide the form
						if (!!$scope.form) {
							$($scope.form).hide();
						}
					}
				);

				$(document).ready(function () {
					if (!$scope.isModal) {
						$scope.form = $('#' + attrs.formName);
						$scope.msgSent = el.find('.msgSent');
						$scope.msgFailed = el.find('.msgFailed');
					}
				});

				// init recipient
				$scope.recipient = {};
				$scope.recipient.CountryCode = "USA"; // set default to USA
				haGlobals('defaultCountryCode', function (code) {
					$scope.recipient.CountryCode = code;
				});
				$scope.recipient.Number = "";

				$scope.formNativeappLinkSmsSubmit = function () {
					if ($scope.isModal && !$scope.form) {
						$scope.form = $('#' + attrs.formName);
						$scope.msgSent = el.find('.msgSentModal');
						$scope.msgFailed = el.find('.msgFailedModal');
					}
					$scope.sendSmsRequest()
						.success(function () {
							$($scope.msgSent).fadeIn("slow");
							setTimeout(function () { $($scope.msgSent).fadeOut("slow"); }, 5000);

							$scope.isSubmitted = false;

						}).error(function () {
							$($scope.msgFailed).fadeIn("slow");
							setTimeout(function () { $($scope.msgFailed).fadeOut("slow"); }, 5000);

							$scope.isSubmitted = false;
						});
				};

				$scope.isSubmitted = false;

				$scope.sendSmsRequest = function () {
					// loading spinner
					$scope.isSubmitted = true;
					var elements = $scope.form[0].elements;
					var phone = '+' + $(elements.PhoneCountryCode).val() + $(elements.PhoneNumber).val().replace(/\D/g,'');
					var area = $(elements.area).val();
					return http.POST('/api/v2/shared/TextAppLink', { phone: phone, area: area });
				};
			};

			return {
				restrict: 'A',
				scope: true,
				link: link
			};
		}]
	);
})(angular);

;
(function (angular) {

	'use strict';

	var module = angular.module('haNativeAppModalModule', []);

	module.directive('haNativeAppModal', [
		'$location',
		'$rootScope',
		'haGlobals',
		'haUtils',

		function ($location, $rootScope, haGlobals, haUtils) {

			var link = function ($scope) {
				//>>> Native App Modal
				// daysToHide is global from Sitecore to _PartialModalNativeappLinkSms.cshtml
				haGlobals('daysToHide', function (val) {

					if (!val) { // if undefined
						$scope.daysToHide = 0;
						return;
					}

					if (val < 0) {
						$scope.daysToHide = 0;
						return;
					}

					$scope.daysToHide = val;
				});

				$scope.createModalHideExpirationCookie = function (created_date_string) {
					var currentDate = new Date();
					if (!!created_date_string) {
						currentDate = new Date(created_date_string);
					}

					var expiredDate = new Date();
					expiredDate.setTime(currentDate.getTime() + ($scope.daysToHide.toString() * 24 * 60 * 60 * 1000));

					var expires = "expires=" + expiredDate.toUTCString();

					if (HA.CookiesRequireSsl) {
						document.cookie = ['ModalHideExpirationCookie=' + currentDate.toUTCString() + '; ', expires, '; secure; ', '; path=/'].join('');
					} else {
						document.cookie = ['ModalHideExpirationCookie=' + currentDate.toUTCString() + '; ', expires, '; path=/'].join('');
					}
				};

				$scope.getModalHideExpirationCookie = function () {

					var theValue = '';

					var checkCookie = haUtils.readCookie('ModalHideExpirationCookie');
					if (!!checkCookie) {
						theValue = decodeURIComponent(checkCookie);
					}

					return theValue;
				};

				$scope.removeModalHideExpirationCookie = function () {
					document.cookie = encodeURIComponent('ModalHideExpirationCookie') + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + "; domain=; path=";
				};

				$(window).load(function () {

					// check if USA
					haGlobals('defaultCountryCode', function (code) {
						if (code !== "USA") {
							return;
						}
					});

					var cookieVal = '';
					cookieVal = $scope.getModalHideExpirationCookie();
					if (cookieVal !== '' && cookieVal !== "Invalid Date") {
						// cookie found. check the date of creation.
						var created_date_string = cookieVal;
						var current_date_string = new Date().toUTCString();

						// Update the cookie as daysToHide might have been changed.
						$scope.createModalHideExpirationCookie(created_date_string);

						// Check the Sitecore value and the day passed.
						var created_date = moment(created_date_string).isValid() ? moment(created_date_string) : moment();
						var current_date = moment(current_date_string).isValid() ? moment(current_date_string) : moment();
						if ($scope.daysToHide > current_date.diff(created_date, 'days')) {
							// no need to show modal
							return;

						} else {
							// show modal
							haModal({
								id: 'modal-nativeapp-link-sms',
								backdrop: 'true',
								template: angular.element('#modal-nativeapp-link-sms'),
								scope: $scope
							});
						}
					} else { // cookie not found
						// show modal
						haModal({
							id: 'modal-nativeapp-link-sms',
							backdrop: 'true',
							template: angular.element('#modal-nativeapp-link-sms'),
							scope: $scope,
							cancel: {
								label: 'Close',
								fn: $scope.createModalHideExpirationCookie
							}
						});
					}
				});
				//<< Native App Modal
			};

			return {
				restrict: 'A',
				scope: true,
				link: link
			};
		}]
	);
})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haUpsellGridModule', []);
	var EQUIPMENT_PRIORITY = ['717', '321', '332']; // Higher index is higher priority
	var BRAND_SC_KEYS = {
		'COACH': 'UpsellGrid.maincabintext',
		'EXTRACOMFORT': 'UpsellGrid.extracomforttext',
		'FIRST': 'UpsellGrid.firstclasstext',
		'BUSINESS': 'UpsellGrid.businessclasstext',
		'PREFERRED': 'UpsellGrid.preferredseatstext'
	};
	var OPTION_TYPE_MAP = {
		'DEPARTING': 0,
		'RETURNING': 1,
		'BOTH': 2
	};

	module.directive('haUpsellGrid', ['haGlobals', 'haConfig', 'haSitecoreStrings', function (haGlobals, haConfig, $scs) {

		var controller = ['$scope', function ($scope) {
			$scope.OPTION_TYPE_MAP = OPTION_TYPE_MAP;

			// Assign some values for convenience
			$scope.gridDetails = $scope.UpsellGrid.UpSellGridDetails;
			$scope.pst = $scope.PassengerTripSummary;
			$scope.originCode = $scope.pst.AvailGridTrips[0].Origin;
			$scope.destinationCode = $scope.pst.AvailGridTrips[0].Destination;

			// Holds the selection state of the upsell grid.
			$scope.trips = [];

			$scope.hideCoach = function() {
				return $scope.gridDetails.UpsellStaticRules.InboundSelectedBrand === 'MAINCABINBASIC' &&
				       $scope.trips[2] === false &&
				       $scope.trips.indexOf('COACH') !== -1;
			};

			// Does the brand already have a selected option in `$scope.trips`
			$scope.brandAlreadySelected = function(brand, option) {
				// Special treatment is needed for the "BOTH" option
				if (option.OptionType === 'BOTH') {
					// if `$scope.trips` indicates that "BOTH" is selected, then we can ignore the rest of the array.
					if ($scope.trips[2] === brand.BrandType) {
						return false;
					}
					// In the cases where a brand is preselected from flight results, we want to ignore those entries in `$scope.trips`
					if (brand.ShowDepartingFlight || brand.ShowReturningFlight) {
						return false;
					}
				}

				var optionIndex = OPTION_TYPE_MAP[option.OptionType];
				for (var i = 0; i < $scope.trips.length; i++) {
					if (i === optionIndex || !$scope.trips[i]) {
						continue;
					}

					if (brand.BrandType === $scope.trips[i]) {
						return true;
					}
				}
				return false;
			};

			// Should the given option be checked?
			$scope.isChecked = function(brand, value, option) {
				return $scope.trips[OPTION_TYPE_MAP[option.OptionType]] === value && (!$scope.brandAlreadySelected(brand, option) || option.OptionType == 'BOTH');
			};

			// Keypress handling
			$scope.brandKeyPress = function (brand, value, option, $event) {
				if ($event.keyCode === 32) {
					// space
					$event.preventDefault();
					$scope.makeSelection(brand, value, option);
				}
			};

			$scope.getBrandFromBrandType = function(brandType) {
				return $scope.gridDetails.UpsellStaticRules.UpsellOptions.filter(function(brand) {
					return brand.BrandType === brandType;
				})[0];
			};

			// Updates internal selection state (`$scope.trips`) and flight results trip summary state via `selectSeatClass`
			$scope.makeSelection = function(brand, value, option, $event) {
				if (!option.IsAvailable) {
					// Disabled state
					return;
				}

				var checked = $scope.isChecked(brand, value, option);
				if ($event) {
					// Get the click target
					var target = $($event.target);
				}
				if (checked && target && !target.is('label')) {
					// Ensure we can only deselect via the radio button
					return;
				}
				
				if (option.OptionType === 'DEPARTING' || option.OptionType === 'RETURNING') {
					// Change idx1 and idx2 based on which leg we're selecting
					var idx1 = option.OptionType === 'DEPARTING' ? 0 : 1; 
					var idx2 = option.OptionType === 'DEPARTING' ? 1 : 0; 
					$scope.trips[idx1] = checked ? false : value; // Select or deselect the option
					// This determines whether or not we need to clear the other leg's value from a prior 'BOTH' selection.
					// Typically when the user has preselected a flight class from results.
					var onlyBoth = ($scope.trips[2] && $scope.getBrandFromBrandType($scope.trips[2]).Options.length === 1) ? $scope.trips[2] : false;
					$scope.selectSeatClass(brand.BrandType, idx1);  // Update the trip summaries
					if ($scope.gridDetails.UpsellStaticRules.InboundSelectedBrand === 'MAINCABINBASIC') {
						// Special rules for MCB. Auto upgrade other leg to COACH
						$scope.trips[idx2] = checked ? false : 'COACH';
						if ($scope.trips[2] !== 'COACH') {
							// if coach wasn't already selected
							$scope.selectSeatClass('COACH', idx2);
						}
					} else if (onlyBoth) {
						$scope.selectSeatClass(onlyBoth, idx2);
					}
					$scope.trips[2] = false; // Clear out any 'BOTH' options
				} else {
					// 'BOTH' case
					if (brand.Options && brand.Options.length === 1 && brand.Options[0].OptionType === 'BOTH') {
						// When 'BOTH' is the only option for a brand, don't fill in the whole `$scope.trips` array
						// to prevent autoselection of a nonexistent option.
						$scope.trips[0] = $scope.trips[1] = false;
						$scope.trips[2] = checked ? false : value;
					} else {
						// Fill out `$scope.trips` so that this brand is selected for the other leg wjen 
						// departing or returning is auto selected for another brand.
						$scope.trips[0] = $scope.trips[1] = $scope.trips[2] = checked ? false : value;
					}
					$scope.selectSeatClass(brand.BrandType, 0);
					$scope.selectSeatClass(brand.BrandType, 1);
				}
			};

			$scope.getTrip = function(tid) {
				var trip = $scope.gridDetails.UpSellGridTrips.filter(function(trip) {
					return trip.TripId === tid;
				})[0];
				return trip;
			};

			$scope.getFareDetails = function(tid, brand) {
				var trip = $scope.getTrip(tid);
				if (!trip) {
					return;
				}
				var fare = trip.GridFares.filter(function(fare) {
					return fare.Cabin === brand;
				})[0];
				return fare;
			};

			var brandStrings = {};
			$scope.getBrandText = function(brandType) {
				if(brandStrings[brandType]) {
					return brandStrings[brandType];
				}
				return $scs.get(BRAND_SC_KEYS[brandType]).then(function(result) {
					brandStrings[brandType] = result;
				});
			};

			var equipmentType;
			$scope.getEquipmentType = function() {
				if (equipmentType) {
					return equipmentType;
				}
				var eqIndex = -1;
				var searchTrip = function(trip) {
					if (trip && trip.TripSlice) {
						trip.TripSlice.Segments.forEach(function(segment) {
							eqIndex = Math.max(eqIndex, EQUIPMENT_PRIORITY.indexOf(segment.EquipmentType));
						});
					}
				};

				if ($scope.gridDetails.UpSellGridTrips[0]) {
					searchTrip($scope.gridDetails.UpSellGridTrips[0]);
				}
				if ($scope.gridDetails.UpSellGridTrips[1]) {
					searchTrip($scope.gridDetails.UpSellGridTrips[1]);
				}

				if (eqIndex >= 0) {
					equipmentType = EQUIPMENT_PRIORITY[eqIndex];
					return equipmentType;
				}
			}

			// advertisement type (if applicable)
			if ($.inArray('EXTRACOMFORT', $scope.UpsellGrid.UpSellGridDetails.AvailableCabins) > -1) {
				$scope.UpsellGrid.UpSellGridDetails.advertisementType = 'extracomfort';
			} else {
				$scope.UpsellGrid.UpSellGridDetails.advertisementType = 'preferred';
			}
		}];

		return {
			restrict: 'A',
			scope: true,
			controller: controller,
			templateUrl: haConfig.getTemplateUrl('ha-upsell-grid.html')
		};
	}]);

})(angular);
;
(function (angular) {
	'use strict';
	var PER_PAGE = 3;
	var module = angular.module('haDealTilesModule', []);

	module.directive('haDealTiles', [function () {

		var haDealTilesLink = function($scope, $el, $attrs) {
			var viewModel = angular.fromJson($attrs.dealsData);
			$scope.deals = viewModel.DealTiles;
			console.log(viewModel);
			var dealGroups = [];
			var groupIndex = -1;

			for (var i = 0; i < $scope.deals.length; i++) {
				var deal = $scope.deals[i];
				if (i % PER_PAGE === 0) {
					dealGroups.push({ group: [] });
					groupIndex++;
				}
				dealGroups[groupIndex].group.push(deal);
			}
			$scope.dealGroups = dealGroups;

		}
		return {
			restrict: 'A',
			scope: true,
			link: haDealTilesLink,
		};
	}]);
	// TripType Enum From C# Viewmodel
	// OneWay = 0,
	// RoundTrip =1,
	// Package =2,
	// GenericRichText =3,
	// CustomFareSale =4,
	// Standard =5,
	// None
	module.directive('haDealTile', ['haConfig', '$rootScope', function (haConfig, $rootScope) {
		var templateUrl = "ha-deal-tile-base-template.html";
		var haDealTileLink = function($scope, $el, $attrs) {
			console.log('deal tile 2020 directive link');
			// $scope.model = $attrs['model']
			console.log($scope.model);

			$scope.model.LowestFare = Math.min($scope.model.MainCabinBasicFare || Number.POSITIVE_INFINITY, $scope.model.Fare);

			// Ref Marks/Disclaimers
			$scope.referenceMark = $scope.referenceMark || '*';
			if (!$rootScope.footnotes || !$rootScope.footnotes.numeric || !$rootScope.references) {
				$rootScope.footnotes = { numeric: [] };
				$rootScope.references = {};
			}

			if (!$rootScope.references[$scope.model.DisclaimerGuid]) {

				if ($scope.model.Disclaimer && $scope.model.TripType != 3) {
					$rootScope.references[$scope.model.DisclaimerGuid] = $scope.referenceMark + (Object.keys($rootScope.references).length + 1);
					$scope.model.DisclaimerReferenceMark = $rootScope.references[$scope.model.DisclaimerGuid];

					var isMCBFare = $scope.model.LowestFare && $scope.model.LowestFare === $scope.model.MainCabinBasicFare;
					var lowFareDisclaimer = isMCBFare ? $scope.model.MainCabinBasicDisclaimer : $scope.model.Disclaimer;
					$rootScope.footnotes.numeric.push({
						id: '*' + ($rootScope.footnotes.numeric.length + 1),
						text: lowFareDisclaimer
					});
				}

			} else {
				$scope.model.DisclaimerReferenceMark = $rootScope.references[$scope.model.DisclaimerGuid];
			}
		}
		if (window.location.pathname == "/") {
			templateUrl = "ha-deal-tile-base-template-lazyload.html";
		}
		return {
			restrict: 'A',
			scope: {
				'model': '='
			},
			link: haDealTileLink,
			templateUrl: haConfig.getTemplateUrl(templateUrl)
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('bookingWidgetSlimModule', []);

	module.directive('bookingWidgetSlim', ['$rootScope', function ($rootScope) {
		var PEEK_OFFSET = $rootScope.isMobile ? 0 : 40; // How much space above the widget should be shown upon expansion.
		var SCROLL_THRESHOLD = 2; // How far a user can scroll in the expanded state while
		                          // still saving the old scroll position.
		var bookingWidgetSlimLink = function ($scope, $element, $attrs) {

			var overlayEL = angular.element('<div id="booking-widget-overlay"></div>');

			$scope.$watch('tab', function (nv, ov) {
				if ((ov != nv && nv != 'bookflights')) {
					$scope.expanded = true;
				}
			});

			var oldScroll = null;
			$scope.$watch('expanded', function(nv, ov) {
				if ($rootScope.isMobile) { return; }
				if (nv) {
					$element.before(overlayEL);
					overlayEL.click(function(e) {
						$scope.expanded = false;
						$scope.$apply();
					});
					oldScroll = window.pageYOffset;
					$('html, body').animate({
						scrollTop: $element.offset().top - PEEK_OFFSET
					});
					// window.scrollTo({left: 0, top: $element.offset().top - PEEK_OFFSET, behavior: 'smooth'});
				} else {
					overlayEL.remove();
					var expandedHeight = $element.outerHeight();
					if (oldScroll != null && Math.abs(window.pageYOffset - ($element.offset().top - PEEK_OFFSET)) <= SCROLL_THRESHOLD) {
						window.scrollTo({left: 0, top: oldScroll});
					}
					oldScroll = null;
					$scope.tab = 'bookflights';
				}
			});

			$element.bind("keydown keypress", function (event) {
                if(event.which === 27) {
                  $scope.expanded = false;
                  $scope.$apply();
                  event.preventDefault();
                }
            });

		};

		return {
			restrict: 'A',
			scope: true,
			link: bookingWidgetSlimLink
		};
	}]);

})(angular);
;
(function (angular) {

    'use strict';

    var module = angular.module('haVariableSeatmapModule', []);

    var CURRENCY_SYMBOLS = {
        USD: '$',
        AUD: '$',
        NZD: '$',
        CNY: '¥',
        KRW: '₩',
        JPY: '¥',
        TWD: 'NT$'
    };
    var TRUNCATED_CURRENCIES = ['CNY', 'KRW', 'JPY', 'TWD'];
    var MAX_PRICE_DIGITS = 3;

    // Author: Jamie Perkins (Updated for VSP by Mike Toymil and Travis Sisti)
    // just the seat map itself, relies on service to load svg
    module.directive('haVariableSeatmap', ['haVariableSeatmapService', 'haConfig', '$filter', '$timeout', '$log', '$rootScope', '$window', function (haVariableSeatmapSvc, haConfig, $filter, $timeout, $log, $rootScope, $window) {

        return {
            restrict: 'A',
            scope: {},
            templateUrl: haConfig.getTemplateUrl('VariableSeatmap/ha-variable-seatmap.html'),
            link: function ($scope) {

                $scope.svc = haVariableSeatmapSvc;
                $scope.seat = {}; // for tooltip
                $scope.isTouchDevice = false; // for iPads
                if (!$rootScope.isMobile) {
                    $scope.isTouchDevice = Boolean(navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i));
                }

                function compareSeatTypes(seat, seatGroup) {
                    switch(seatGroup.SeatType) {
                        case "MainCabin":
                            if (seat.isStandard) {
                                return true;
                            }
                            break;
                        case "ExtraComfort":
                            if (seat.isExtraComfort) {
                                return true;
                            }
                            break;
                        case "Preferred":
                            if (seat.isMainCabinPreferred) {
                                return true;
                            }
                            break;
                        case "FirstClass":
                            if (seat.isFirstClass) {
                                return true
                            }
                            break;
                        case null:
                            // If no seat type is provided in the tooltip strings, assume it's a match
                            return true;
                    }
                    return false;
                }

                // Fetch the tooltip description strings
                function initTooltipsForLeg(legIndex) {
                    var leg = $scope.svc.legs[legIndex];
                    function processLeg() {

                        var data = $scope.svc.tooltipDescriptions;
                        // map strings to seats
                        var code = leg.EquipmentCode;
                        var name = leg.EquipmentName;

                        angular.forEach(leg.seatModel.seats, function(seat, key) {
                            // First check description overrides
                            if (data.SeatMapOverrideDetails) {
                                for (var i = 0; i < data.SeatMapOverrideDetails.length; i++) {
                                    var codeGroup = data.SeatMapOverrideDetails[i];
                                    if (codeGroup.EquipmentCodes.indexOf(code) !== -1) {
                                        // Leg EQ found in code group. Check for seat
                                        for (var j = 0; j < codeGroup.Seats.length; j++) {
                                            var seatGroup = codeGroup.Seats[j];
                                            if (seatGroup.SeatNumber.indexOf(key) !== -1 && compareSeatTypes(seat, seatGroup)) {
                                                seat.SeatDescription = seatGroup.SeatDescription;
                                                seat.FeatureList = seatGroup.FeatureList;
                                                return;
                                            }
                                        }
                                    }
                                }
                            }

                            // No override found, fall back to default
                            if (data.Aircraft) {
                                for (var i = 0; i < data.Aircraft.length; i++) {
                                    var defaultGroup = data.Aircraft[i];
                                    if (name.match(defaultGroup.AircraftName)) {
                                        for (var j = 0; j < defaultGroup.Seats.length; j++) {
                                            var defaultSeatGroup = defaultGroup.Seats[j];
                                            if (defaultSeatGroup.SeatNumber.indexOf(key) !== -1 && compareSeatTypes(seat, defaultSeatGroup)) {
                                                seat.SeatDescription = defaultSeatGroup.SeatDescription;
                                                seat.FeatureList = defaultSeatGroup.FeatureList;
                                                return;
                                            }
                                        }
                                    }
                                }
                            }
                        });

                    }
                    if ($scope.svc.tooltipDescriptions) {
                        processLeg();
                        return;
                    }
                    $scope.svc.getSeatmapTooltips().success(function(data, status, headers) {
                        if (!$scope.svc.tooltipDescriptions) {
                            $scope.svc.tooltipDescriptions = data;
                        }
                        processLeg();

    				});
                }

                var colors = {
                    gray: '#D0D0CE',
                    white: '#FFFFFF',
                    unavailableBG: '#F1F3F3',
                    unavailableLine: '#C4C4C4',
                    darkViolet: '#4D2E91'
                };
                var showTooltipTimeout;

                $scope.$on('renderSeatmapAtIndex', function (event, data) {
                    initTooltipsForLeg(data);
                    renderSeatmapAtIndex(data);
                });
                $scope.$on('updateSeatmapAtIndex', function (event, data) {
                    updateSeatmapAtIndex(data);
                });
                $scope.$on('cancelTooltip', hideTooltip);
                $scope.$on('refreshSeatmapTooltip', refreshTooltip);

                $window.addEventListener('orientationchange', function () {
                    $log.debug('orientation change');
                    $rootScope.$broadcast('cancelTooltip');
                });

                // determine price to display in tooltip
                $scope.calculateSeatDisplayCost = function (seat) {
                    // Check to see if we're an (non corp) elite member in preview mode and this
                    // is a preferred seat, because apparently the backend cannot determine this.
                    // If so, we need to undo the elite member seat waiver
                    var showOriginalPrice;
                    if (seat.isMainCabinPreferred &&
                        $scope.svc.isPreview &&
                        $scope.svc.legs[$scope.svc.activeLegIndex].isEliteMember &&
                        !$scope.svc.legs[$scope.svc.activeLegIndex].isCorporateMember)
                    {
                        showOriginalPrice = true;
                    }
                    if ($scope.svc.isGroupBooking() && !$scope.svc.passengers[$scope.svc.selectedPassengerIndex].IsMemberEligibleForDiscount && seat.isMainCabinPreferred) {
                        showOriginalPrice = true;
                    }

                    var cost = showOriginalPrice ? seat.originalPrice : seat.price;
					var	fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;

                    if ($scope.svc.isForSelection) {
                        var	selectingPaxSeat = $scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex];

                        // do they have a seat credit? (change seats/reshop)
                        if (selectingPaxSeat.originalSeat && selectingPaxSeat.originalSeat.credit > 0) {
                            cost -= selectingPaxSeat.originalSeat.credit;
                        }
                        // did they buy an EC/PS fare?
                        else if (!seat.isUnavailable && !(seat.Type > 0 && /coach/gi.test(fareClass)) && !selectingPaxSeat.DowngradeOk) {
                            // Subtract the original fare amount
                            cost -= $scope.svc.originalPassengerFares[$scope.svc.selectedPassengerIndex][$scope.svc.activeLegIndex].SeatAmount;
                        }
                    }
                    cost = Math.max(cost, 0);
                    seat.SeatAmountDisplay = cost;
                    return cost;
                }

                // select seat function - handles jquery event
                function selectSeat(event) {
                    if (event.type === 'keyup' && event.keyCode !== 13) {
                        return; // only respond to enter key
                    }

                    var $seat = $(this),
						id = $seat.attr('id'),
						info = id.split('-');
                    if (id && info.length) {

                        var name = info[2],
							seatObj = $scope.svc.legs[$scope.svc.activeLegIndex].seatModel.seats[name];

                        if (seatObj.isUnavailable) { return; }
                        if (!seatObj.isSelected && seatObj.isPriceTruncated) { return; }

                        $scope.seat = seatObj;
                        $scope.$emit('seatWasSelectedOrDeselected', seatObj);

                    }
                }

                $scope.selectSeatFromTooltip = function(seatObj) {
                    if (seatObj.isUnavailable) { return; }
                    $scope.seat = seatObj;
                    $scope.$emit('seatWasSelectedOrDeselected', seatObj);
                }

                function showTooltip() {
                    $('#seatPopoverSelected').hide();
                    var $seat = $(this);
                    var id = $seat.attr('id'),
						info = id.split('-'),
						name = info[2],
						seatObj = $scope.svc.legs[$scope.svc.activeLegIndex].seatModel.seats[name],
						popoverId = (seatObj.isSelected) ? '#seatPopoverSelected' : '#seatPopover';
                    $scope.seat = seatObj;

                    if (($scope.isTouchDevice || $rootScope.isMobile) && seatObj.isSelected) { // deselecting on tablet/mobile
                        $scope.$emit('seatWasSelectedOrDeselected', seatObj);
                        return;
                    }
                    if ($rootScope.isMobile && !seatObj.isSelected) {
                        $scope.$emit('mobileTooltipWasDisplayed');
                    }
                    $scope.$apply();
                    calculateTooltipPositionAndDisplay($seat, popoverId);
                }

                function refreshTooltip() {
                    $('#seatPopoverSelected, #seatPopover').hide();
                    // don't show tooltip after deselect on tablet or mobile
                    if (($scope.isTouchDevice || $rootScope.isMobile) && !$scope.seat.isSelected) { return; }
                    // refresh
                    var $seat = $('#' + $scope.svc.activeLegIndex + '-seat-' + $scope.seat.name),
						popoverId = ($scope.seat.isSelected) ? '#seatPopoverSelected' : '#seatPopover';
                    calculateTooltipPositionAndDisplay($seat, popoverId);
                }

                function hideTooltip() {
                    mouseInPopover = false;
                    $timeout.cancel(showTooltipTimeout);
                    $('#seatPopover').hide();
                    $('#seatPopoverSelected').hide();
                    $('#seatPopoverArrow').hide();
                }
                // hideTooltip() responds to events much faster than the scope equivalent.
                // $scope fn is accessed through ng-click. therefore, 2 references to hideTooltip fn.
                $scope.hideTooltip = function () {
                    hideTooltip();
                };

                function calculateTooltipPositionAndDisplay($seat, popoverId) {
                    $timeout.cancel(showTooltipTimeout);
                    showTooltipTimeout = $timeout(function () {

                        var $rect = $seat.find('rect'),
                            $svg = $seat.parents('svg'),
							$container = $seat.parents('.seatmap'),
							containerWidth = $container.width(),
                            svgAttrWidth = $svg[0].getAttribute('viewBox').split(' ')[2],
                            svgWidth = $svg.width(),
                            svgScaleFactor = svgWidth / svgAttrWidth,
							seatWidth = Number($rect.attr('width')) * svgScaleFactor,
							tooltipWidth = $(popoverId).outerWidth(),
							maxTooltipLeft = containerWidth - tooltipWidth,
							rectTop = Number($rect.attr('y')) * svgScaleFactor,
							rectLeft = Number($rect.attr('x')) * svgScaleFactor,
							svgLeft = (containerWidth - svgWidth) / 2,
							seatmapLeft = $container.position().left,
							seatLeft = seatmapLeft + svgLeft + rectLeft,
							position = {
							    tooltipTop: rectTop + seatWidth,
							    tooltipLeft: seatLeft + seatWidth / 2 - tooltipWidth / 2,
							};
                        if (position.tooltipLeft < 0) { position.tooltipLeft = 0; }
                        if (position.tooltipLeft > maxTooltipLeft) { position.tooltipLeft = maxTooltipLeft; }
                        if ($rootScope.isMobile && popoverId === "#seatPopover") {
                            $(popoverId).slideDown(200).css({
                                top: position.tooltipTop,
                                left: position.tooltipLeft
                            });
                        } else {
                            $(popoverId).show().css({
                                top: position.tooltipTop,
                                left: position.tooltipLeft
                            });
                        }

                        if (popoverId === "#seatPopoverSelected") {
                            $timeout(function() {
                                $(popoverId).fadeOut();
                            }, 3000);
                        }
                    }, 250);

                    // Reset the seat description truncation on mobile
                    if ($rootScope.isMobile) {
                        $scope.tooltipExpanded = false;
                    }
                }

                // Helper function for positioning
                function calculateRectCenter($rect) {
                    // var $rect = $seat.find('rect'),
                    var rectWidth = Number($rect.attr('width')),
                        rectHeight = Number($rect.attr('height')),
                        rectTop = Number($rect.attr('y')),
                        rectLeft = Number($rect.attr('x'));
                    return {
                        x: rectLeft + (rectWidth / 2),
                        y: rectTop + (rectHeight / 2)
                    };
                }

                // for updating seat against selection
                function updateSeat(name, seatObj, legIndex) {
                    var $seat = $('#' + legIndex + '-seat-' + name);
                    if (!$seat[0]) { return; }

                    var $rect = $seat.find('rect').first(),
                        $bar = $seat.find('[id^=Bar]'),
						origFill = $rect.attr('orig-fill'),
                        origBarFill = $bar.attr('orig-fill'),
                        origRx = $rect.attr('orig-rx');

                    // Update price if needed (disable for preview)
                    var $price = $seat.find('text[id^="Price-Web-Placeholder"] tspan')
                    if (seatObj.isStandard) {
                        // We don't want to show prices for standard seats.
                        $price.text('');
                    }
                    if ($price.length && !seatObj.isStandard) {
                        var oldPrice = $seat.attr('old-price');
                        var newPrice = $scope.calculateSeatDisplayCost(seatObj);
                        if (!isNaN(newPrice) && oldPrice != newPrice) {
                            $seat.attr('old-price', newPrice);
                            var currencyCode = $scope.$root.$currency;
                            var symbol = CURRENCY_SYMBOLS[currencyCode];
                            var priceRoundedUp = Math.ceil(newPrice);
                            var priceText;

                            if (priceRoundedUp.toString().length > MAX_PRICE_DIGITS || TRUNCATED_CURRENCIES.indexOf(currencyCode) != -1) {
                                // Price is too long to display on seat
                                priceText = symbol;
                                seatObj.isPriceTruncated = true;
                                updateSeatBindings($seat, true);
                            } else {
                                priceText = $filter('currency')(priceRoundedUp, symbol, 0);
                                seatObj.isPriceTruncated = false;
                                updateSeatBindings($seat);
                            }
                            $price.text(priceText);
                        }
                    }

                    // select
                    if (seatObj.isSelected && !origFill) {
                        $seat[0].classList.add('hide-price');
                        var oldFill, oldBarFill, oldRx;
                        if (seatObj.isLieFlat) {
                            oldFill = $seat.find('path').attr('fill');
                            $seat.find('path').attr('fill', colors.darkViolet);
                            $seat.find('line').attr('stroke', 'none');
                        } else {
                            oldFill = $rect.attr('fill');
                            oldBarFill = $bar.attr('fill');
                            oldRx = $rect.attr('rx');
                            $rect.attr('fill', colors.darkViolet);
                            var rectWidth = Number($rect.attr('width'));
                            $rect.attr('rx', rectWidth/2);
                            $bar.attr('fill', 'none');
                        }
                        $rect.attr('orig-fill', oldFill);
                        $rect.attr('orig-rx', oldRx);
                        $bar.attr('orig-fill', oldBarFill);
                        drawInitialsForSeat($seat, name, legIndex, seatObj);
                    }

                    // deselect
                    if (!seatObj.isSelected && origFill) {
                        $seat.children('[id^=initials]').remove();
                        $seat[0].classList.remove('hide-price');
                        if (seatObj.isLieFlat) {
                            $seat.find('path').attr('fill', origFill);
                            $seat.find('line').attr('stroke', colors.white);
                        } else {
                            $rect.attr('fill', origFill);
                            $bar.attr('fill', origBarFill);
                            $rect.attr('rx', origRx);
                        }
                        $rect.removeAttr('orig-fill');
                        $rect.removeAttr('orig-rx');
                        $bar.removeAttr('orig-fill');
                    }
                }

                var cols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J']; // for assigning tabindex
                //Update each seat name attribute with Seat Type
                function updateSeatNameAttribute(seat, attributeValue) {
                    if (seat.attr('name')) {
                        seat.attr('name', seat.attr('name') + ',' + attributeValue);
                    }
                    else {
                        seat.attr('name', attributeValue);
                    }
                }

                var mouseInPopover = false;
                function mouseEnteredPopover() {
                    mouseInPopover = true;
                }

                var tooltipDebounce = 0; // Prevents delayed tooltip shows/hides from interacting
                                         // by keeping track of tooltip show attempts
                // Updates interaction event bindings for a set of seats
                function updateSeatBindings($seats, truncated) {
                    $seats.off(); // Clear existing bindings
                    if ($scope.svc.isForSelection) { // are we in seat selection?
                        if (!$rootScope.isMobile) {
                            if ($scope.isTouchDevice) {
                                // Just show the tooltip for touch devices
                                $seats.on('click keyup', showTooltip);
                            } else if (truncated) {
                                // If the seat price has been truncated,
                                // we need to allow users to confirm the price
                                // and selection from within the tooltip.
                                // This requires some event and timing voodoo.
                                $seats.mouseenter(function(e) {
                                    // On mouseenter, show the tooltip
                                    tooltipDebounce++;
                                    showTooltip.apply(e.currentTarget);
                                });
                                $seats.mouseleave(function() {
                                    // On mouseleave, wait a moment and only hide the tooltip
                                    // if we're not hovering in the popover or another seat
                                    $timeout(function() {
                                        tooltipDebounce--;
                                        if (tooltipDebounce > 0) {
                                            return;
                                        }
                                        if (!mouseInPopover && showTooltipTimeout.$$state.status !== 0) {
                                            hideTooltip();
                                        }
                                    }, 100);
                                });

                                // Keeps track of whether or not the mouse is in the popover
                                $('[id^="seatPopover"]').off();
                                $('[id^="seatPopover"]').mouseenter(mouseEnteredPopover);
                                $('[id^="seatPopover"]').mouseleave(hideTooltip);
                                $seats.on('click keyup', selectSeat);
                            } else {
                                // Otherwise, simply show and hide the tooltip on hover
                                // and click to select a seat.
                                $seats.on('click keyup', selectSeat);
                                $seats.hover(showTooltip, hideTooltip);
                            }
                        } else {
                            // Mobile is simple, hide the old tooltip and show the new.
                            // Selection is handled within the tooltip itself.
                            $seats.on('click', hideTooltip);
                            $seats.on('click', showTooltip);
                        }
                    }
                }

                // show unavailable seats, prices, and build out seat model based on svg
                function renderSeatmapAtIndex(legIndex) {
                    // already rendered?
                    if ($('#svgSeatmap' + legIndex).find('svg').attr('rendered')) {
                        updateSeatmapAtIndex(legIndex);
                        return;
                    }

                    // Make sure svg is centered horizontally and pushed to top
                    var $svgEl = $('#svgSeatmap' + legIndex).find('svg');
                    var svgEl = $svgEl[0];
                    if (svgEl) {
                        svgEl.setAttribute('preserveAspectRatio', 'xMidYMin');
                        if (!window.document.documentMode) {
                            svgEl.removeAttribute('height');
                            svgEl.removeAttribute('width');
                        }
                    }

                    var renderStartTime = new Date().getTime();

                    updateSeatBindings($('g[id^=seat]'));

                    var model = $scope.svc.legs[legIndex].seatModel, highestPrice = 0;
                    model.availableFirstClassSeats = 0;
                    model.availableExtraComfortSeats = 0;
                    model.availablePreferredSeats = 0;
                    model.availableMainCabinPreferredSeats = 0;
                    model.availableStandardSeats = 0;
                    if (!model.seatsUnavailable) {
                        // build out model from seat properties and render price text & tabindex or unavailable
                        $('g[id^=seat]').each(function () {

                            var $seat = $(this),
								$rect = $seat.find('rect'),
								//$text = $seat.find('text'),
								id = $seat.attr('id').split('-'),
								name = id[1],
								rowNumber = parseInt(name, 10),
								props = (id[2]) ? id[2] : '';

                            $seat.attr('id', legIndex + '-seat-' + name);
                            $seat[0].classList.add('seat');

                            if (!model.seats[name] || model.seats[name].available === false) {
                                model.seats[name] = {
                                    isUnavailable: true
                                };
                            }
                            if ($scope.svc.isForSelection && !model.seats[name].isUnavailable && !$seat.attr('tabindex')) {
                                // assign tabindex
                                var row = parseInt(name),
									col = name.match(/[A-Z]/)[0],
									colIndex = cols.indexOf(col),
									tabindex = 100 + (row * 10) + colIndex;
                                $seat.attr({
                                    'tabindex': tabindex,
                                    'aria-label': row + ' ' + col
                                });
                            }

                            /*	assign properties from SVG:
								e = extra comfort
								p = preferred (old preferred, DEPRECATED)
								w = window
								a = aisle
								x = exit row
								l = limited recline
								r = rear facing
								f = first class
								i = lie flat
                                m = main cabin preferred
							*/
                            model.seats[name].name = name;
                            model.seats[name].Type = 0;
                            if (props.indexOf('f') >= 0) {
                                updateSeatNameAttribute($seat, "firstclass");
                                model.seats[name].isFirstClass = true;
                            }
                            if (props.indexOf('i') >= 0) {
                                updateSeatNameAttribute($seat, "lieflat");
                                model.seats[name].isLieFlat = true;
                            }
                            if (props.indexOf('x') >= 0) {
                                updateSeatNameAttribute($seat, "exitrow");
                                model.seats[name].isExitRow = true;
                            }
                            if (props.indexOf('a') >= 0) {
                                updateSeatNameAttribute($seat, "aisle");
                                model.seats[name].isAisle = true;
                            }
                            if (props.indexOf('w') >= 0) {
                                updateSeatNameAttribute($seat, "window");
                                model.seats[name].isWindow = true;
                            }
                            if (props.indexOf('e') >= 0) {
                                updateSeatNameAttribute($seat, "extracomfort");
                                model.seats[name].isExtraComfort = true;
                            }
                            if (props.indexOf('p') >= 0) {
                                updateSeatNameAttribute($seat, "preferred");
                                model.seats[name].isPreferred = true;
                            }
                            if (props.indexOf('r') >= 0) {
                                updateSeatNameAttribute($seat, "rearfacing");
                                model.seats[name].isRearFacing = true;
                            }
                            if (props.indexOf('l') >= 0) {
                                updateSeatNameAttribute($seat, "limitedrecline");
                                model.seats[name].isLimitedRecline = true; // not used
                            }
                            if (model.seats[name].seatType === 1) {
                                updateSeatNameAttribute($seat, "maincabinpreferred");
                                model.seats[name].isMainCabinPreferred = true;
                            }
                            if (!model.seats[name].isExtraComfort && !model.seats[name].isPreferred && !model.seats[name].isFirstClass && !model.seats[name].isMainCabinPreferred) {
                                updateSeatNameAttribute($seat, "standard");
                                model.seats[name].isStandard = true;
                            }

                            if (model.seats[name].price && model.seats[name].price > highestPrice) {
                                highestPrice = model.seats[name].price;
                            }

                            // disabling cabins
                            if ($scope.svc.legs[legIndex].disableFirstClass && model.seats[name].isFirstClass) {
                                model.seats[name].isUnavailable = true;
                            }
                            if ($scope.svc.legs[legIndex].disableMainCabin && !model.seats[name].isFirstClass) {
                                model.seats[name].isUnavailable = true;
                            }

                            //disabling row 4 for  infant travelers on 717
                            if (rowNumber === 4 && $scope.svc.legs[legIndex].EquipmentName === 'Boeing 717' && $scope.svc.disableRow4) {
                                model.seats[name].isUnavailable = true;
                            }
                            // disabling row 4 on change seats
                            if (rowNumber === 4 && $scope.svc.legs[legIndex].EquipmentName === 'Boeing 717' && $scope.svc.BlockRowFour) {
                                model.seats[name].isUnavailable = true;
                            }

                            // disabling exit rows
                            if ($scope.svc.disableExitRows && model.seats[name].isExitRow) {
                                model.seats[name].isUnavailable = true;
                            }

                            // disabling upgrades
                            if (model.disableSeatUpgradeFlx) {
                                $scope.svc.disableSeatUpgrades = model.disableSeatUpgradeFlx
                            }
                            if ($scope.svc.disableSeatUpgrades && (model.seats[name].isExtraComfort || model.seats[name].isPreferred)) {
                                model.seats[name].isUnavailable = true;
                            }

                            // assign properties of entire seatmap
                            if (model.seats[name].isFirstClass) {
                                $seat[0].classList.add('firstClass');
                                model.hasFirstClass = true;
                            }
                            if (model.seats[name].isLieFlat) {
                                $seat[0].classList.add('lieFlat');
                                model.hasLieFlats = true;
                            }
                            if (model.seats[name].isRearFacing) {
                                model.hasRearFacing = true;
                            }
                            if (model.seats[name].isExtraComfort) {
                                $seat[0].classList.add('extraComfort');
                                model.hasExtraComfort = true;
                                if (model.seats[name].price) {
                                    model.extraComfortPrice = model.seats[name].price;
                                }
                            }
                            if (model.seats[name].isPreferred) {
                                $seat[0].classList.add('preferred');
                                model.hasPreferred = true;
                                if (model.seats[name].price) {
                                    model.preferredSeatPrice = model.seats[name].price;
                                }
                            }
                            if (model.seats[name].isMainCabinPreferred) {
                                $seat[0].classList.add('main-cabin-preferred');
                                model.hasMainCabinPreferred = true;
                                if (model.seats[name].price) {
                                    model.mainCabinPreferredSeatPrice = model.seats[name].price;
                                }
                            }
                            if (model.seats[name].isStandard) {
                                $seat[0].classList.add('standard');
                            }

                            if (model.seats[name].isUnavailable) {
                                if (model.seats[name].isLieFlat) {
                                    $seat.find('line').attr('stroke', 'none');
                                    $seat.find('path').attr('stroke', colors.gray);
                                    $seat.find('path').attr('fill', colors.unavailableBG);
                                    drawXForSeat($seat);
                                } else {
                                    $rect.attr('fill', 'none');
                                    drawXForSeat($seat);
                                }
                            }
                            else {
                                $seat[0].classList.add('available');

                                if (model.seats[name].isFirstClass) {
                                    model.availableFirstClassSeats++;
                                }
                                if (model.seats[name].isExtraComfort) {
                                    model.availableExtraComfortSeats++;
                                    model.seats[name].Type = 1;
                                }
                                if (model.seats[name].isPreferred) {
                                    model.availablePreferredSeats++;
                                    model.seats[name].Type = 2;
                                }
                                if (model.seats[name].isMainCabinPreferred) {
                                    model.availableMainCabinPreferredSeats++;
                                    model.seats[name].Type = 2;
                                }
                                if (model.seats[name].isStandard) {
                                    model.availableStandardSeats++;
                                }
                            }

                            updateSeat(name, model.seats[name], legIndex);
                        }); // end $('g[id^=seat]').each

                        // set text and style cabin headings
                        var firstClassLabel = ($scope.svc.legs[legIndex].IsInternational) ? $scope.svc.strings['businessclass'] : $scope.svc.strings['firstclass'];
                        var $headingFirstClass = $('#heading-firstClass');
                        styleAndPositionHeading($headingFirstClass, legIndex, firstClassLabel);
                        var $headingmainCabin = $('#heading-mainCabin');
                        var mainCabinLabel = $scope.svc.strings.maincabin;

                        // Determine if we should augment the main cabin label with an upgrade type.
                        var upgradeLabel;
                        if ($scope.svc.legs[legIndex].seatModel.hasExtraComfort) {
                            upgradeLabel = $scope.svc.strings.extracomforttext;
                        }
                        if ($scope.svc.legs[legIndex].seatModel.hasPreferred) {
                            upgradeLabel = $scope.svc.strings.preferredseat;
                        }
                        styleAndPositionHeading($headingmainCabin, legIndex, mainCabinLabel, upgradeLabel);

                        if (legIndex > 0) {
                            // Change def ids to be leg-specific (otherwise they don't work!)
                            $svgEl.find('defs [id]').each(function(i,e) {
                                $(e).attr('id', legIndex + '-' + $(e).attr('id'));
                            });

                            // Update referencing fills
                            $svgEl.find('[fill^="url"]').each(function(i,e) {
                                var fill = $(e).attr('fill').split('#');
                                $(e).attr('fill', fill[0] + '#' + legIndex + '-' + fill[1]);
                            });
                            $svgEl.find('[orig-fill^="url"]').each(function(i,e) {
                                var fill = $(e).attr('orig-fill').split('#');
                                $(e).attr('orig-fill', fill[0] + '#' + legIndex + '-' + fill[1]);
                            });

                            // Update referencing clip-paths
                            $svgEl.find('[clip-path^="url"]').each(function(i,e) {
                                var clip = $(e).attr('clip-path').split('#');
                                $(e).attr('clip-path', clip[0] + '#' + legIndex + '-' + clip[1]);
                            });
                        }

                        // Render time clock
                        var renderEndTime = new Date().getTime();
                        var time = renderEndTime - renderStartTime;
                        $log.debug('rendered seatmap in ' + time + 'ms');

                        $('#svgSeatmap' + legIndex).find('svg').attr({
                            rendered: true,
                            id: 'svg' + legIndex
                        }).removeAttr('data-name').children('title').remove();

                        if ($rootScope.IsChangeFlightBooking) {
                            $scope.svc.applyAnyAvailableSeatCredits(legIndex);
                            updateSeatmapAtIndex(legIndex);
                        }
                    }
                    else {
                        $('#svgSeatmap' + legIndex).find('svg').remove();
                    }
                    $timeout(function () {
                        $scope.$emit('haSeatmapChanged', legIndex);
                        $scope.$emit('haStickyResize');
                    });
                }

                // update seatmap for selection
                function updateSeatmapAtIndex(legIndex) {
                    var renderStartTime = new Date().getTime();
                    for (var name in $scope.svc.legs[legIndex].seatModel.seats) {
                        var seatObj = $scope.svc.legs[legIndex].seatModel.seats[name];
                        updateSeat(name, seatObj, legIndex);
                    }
                    var renderEndTime = new Date().getTime();
                    var time = renderEndTime - renderStartTime;
                    $log.debug('updated seatmap at index ' + legIndex + ' in ' + time + 'ms');

                    $timeout(function () {
                        $scope.$emit('haSeatmapChanged', legIndex);
                    });
                }

                // Adjusts cabin headers in the svg
                function styleAndPositionHeading($heading, legIndex, mainString, upgradeString) {
                    if ($heading[0]) {
                        var $text = $heading.find('tspan');
                        var $text1 = $text.first();
                        $text.attr({
                            'text-anchor': 'middle',
                            'x': '50%'
                        });
                        if ($text.length === 1) {
                            $text1.text(upgradeString ? upgradeString + ' / ' + mainString : mainString);
                        } else if ($text.length === 2) {
                            var $text2 = $text.last();
                            $text1.text(upgradeString ? upgradeString + ' /' : '');
                            $text2.text(mainString);
                        }
                        var name = $heading.attr('id');
                        $heading.attr('id', legIndex + '-' + name);
                    }
                }

                function getInitials(seatObj) {
                    return $scope.svc.getInitialsForSeat(seatObj);
                }

                // set vertical alignment of seat class touts
                function getTopOffset(jqEl) {
                    return jqEl[0].getBoundingClientRect().top + $(window)['scrollTop']();
                }
                function getBottomOffset(jqEl) {
                    return jqEl[0].getBoundingClientRect().bottom + $(window)['scrollTop']();
                }

                $scope.$on('haSeatmapChanged', function (event, legIndex) {
                    setToutDisplayPrices(legIndex);

                    $scope.$watch(
                        function () {
                            return $('.seat-class-touts-inner').length && $('.leg-info-container').length;
                        },
                        function (nv, ov) {
                        	 if (nv) setToutPositions(legIndex);
                        }
                    );
                });

                // set tout positions
                function setToutPositions(legIndex) {
                    if ($rootScope.isMobile) { return; }

                    var toutContainer = $('.seat-class-touts-inner');
                    var toutContainerOffset = getTopOffset(toutContainer);
                    var allTouts = $('.seat-class-touts .tout');
                    var markers = $('#svgSeatmap' + legIndex + ' [id^=class-marker-]');
                    var lastTout;
                    var lowestOffset = 0;

                    // if no markers are found, set basic (static) positions
                    if (markers.length === 0) {
                        allTouts.css({
                            'position': 'static',
                            'margin-top': toutContainer.css('margin-top'),
                            'top': 0
                        });
                        return;
                    }

                    var legInfoBottom = getBottomOffset($('.leg-info-container')) + 30;

                    // if markers are found, set absolute positions
                    markers.each(function(i, el) {
                        var $el = $(el);
                        var markerClass = $el.attr('id').split('class-marker-')[1];

                        var tout = $('.tout-' + markerClass);
                        if (!tout.length) {
                        	return;
                        }
                        var markerOffset = getTopOffset($el);
                        var toutOffset;
                        if (markerOffset < legInfoBottom) {
                            // Don't let the top tout overlap the leg info
                            toutOffset = legInfoBottom - toutContainerOffset;
                        } else {
                            toutOffset = markerOffset - toutContainerOffset;
                        }

                        tout.css({
                            'position': 'absolute',
                            'margin-top': 0,
                            'top': toutOffset
                        });

                        if (toutOffset > lowestOffset) {
                            lowestOffset = toutOffset;
                            lastTout = tout;
                        }
                    });

                    // set container height to ensure positioning for legend element
                    toutContainer.height(lowestOffset + lastTout.height());
                }

                // find min/max display prices of available EC/Preferred seats
                function setToutDisplayPrices(legIndex) {
                    var seatsObj = haVariableSeatmapSvc.legs[haVariableSeatmapSvc.activeLegIndex].seatModel.seats;
                    var minMaxEC = getPricesMinMax(getPriceRangeForSeatType(seatsObj, 'isExtraComfort'));
                    haVariableSeatmapSvc.legs[legIndex].upgradePriceRange = minMaxEC;
                    haVariableSeatmapSvc.legs[legIndex].showUpgradePriceMin = minMaxEC.min !== undefined;
                    haVariableSeatmapSvc.legs[legIndex].showUpgradePriceMax = minMaxEC.max !== undefined && minMaxEC.max != minMaxEC.min;

                    var minMaxMCP = getPricesMinMax(getPriceRangeForSeatType(seatsObj, 'isMainCabinPreferred'));
                    haVariableSeatmapSvc.legs[legIndex].upgradePriceRangeMCP = minMaxMCP;
                    haVariableSeatmapSvc.legs[legIndex].showUpgradePriceMinMCP = minMaxMCP.min !== undefined;
                    haVariableSeatmapSvc.legs[legIndex].showUpgradePriceMaxMCP = minMaxMCP.max !== undefined && minMaxMCP.max != minMaxMCP.min;
                }

                function getPriceRangeForSeatType(seats, typeAttr) {
                    var priceRange = [];
                    angular.forEach(seats, function (seat) {
                        if (typeof seat.SeatAmountDisplay !== 'undefined' && seat[typeAttr]) priceRange.push(seat.SeatAmountDisplay);
                    });
                    return priceRange;
                }

                function getPricesMinMax(prices) {
                    prices = prices.filter(function (v, i, s) {
                        return s.indexOf(v) === i;
                    });

                    prices.sort(function (a, b) {
                        return a-b;
                    });

                    return {
                        'min': prices[0],
                        'max': prices[prices.length - 1]
                    }
                }

                // add initials to selected seat
                function drawInitialsForSeat($seat, name, legIndex, seatObj) {
                    if ($('#initials' + legIndex + '-' + name)[0]) { return; }

                    var textNode = document.createElementNS('http://www.w3.org/2000/svg', 'text'),
						$rect = $seat.find('rect'),
						center = calculateRectCenter($rect);

                    // Position, style, and populate the initials svg node
                    textNode.setAttribute('fill', '#fff');
                    textNode.setAttribute('x', center.x);
                    textNode.setAttribute('y', center.y);
                    textNode.setAttribute('text-anchor', 'middle');
                    textNode.setAttribute('dy', '0.3em');
                    textNode.setAttribute('font-family', 'Slate Pro');
                    textNode.setAttribute('font-size', '20');
                    textNode.setAttribute('id', 'initials' + legIndex + '-' + name);
                    $(textNode).text(getInitials(seatObj));
                    $seat[0].appendChild(textNode);
                }

                // add unavailable "x"
                function drawXForSeat($seat) {
                    var line1 = document.createElementNS('http://www.w3.org/2000/svg', 'line'),
						line2 = document.createElementNS('http://www.w3.org/2000/svg', 'line'),
						rectX = Number($seat.find('rect').attr('x')),
						rectY = Number($seat.find('rect').attr('y')),
						rectWidth = Number($seat.find('rect').attr('width')),
                        inset = rectWidth / 4,
						farInset = rectWidth - inset;
                    line1.setAttribute('x1', rectX + inset);
                    line1.setAttribute('y1', rectY + inset);
                    line1.setAttribute('x2', rectX + farInset);
                    line1.setAttribute('y2', rectY + farInset);
                    line2.setAttribute('x1', rectX + farInset);
                    line2.setAttribute('y1', rectY + inset);
                    line2.setAttribute('x2', rectX + inset);
                    line2.setAttribute('y2', rectY + farInset);
                    line1.setAttribute('stroke', colors.unavailableLine);
                    line2.setAttribute('stroke', colors.unavailableLine);
                    line1.setAttribute('stroke-width', 5);
                    line2.setAttribute('stroke-width', 5);
                    line1.setAttribute('stroke-linecap', 'round');
                    line2.setAttribute('stroke-linecap', 'round');
                    $seat[0].appendChild(line1);
                    $seat[0].appendChild(line2);
                }
            }
        };
    }]);

})(angular);

/*
 * classList.js: Cross-browser full element.classList implementation.
 * 1.1.20150312
 *
 * By Eli Grey, http://eligrey.com
 * License: Dedicated to the public domain.
 *   See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
 */

/* jshint ignore:start */

if ("document" in self) {

    // Full polyfill for browsers with no classList support
    // Including IE < Edge missing SVGElement.classList
    if (!("classList" in document.createElement("_")) || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg", "g"))) {

        (function (view) {

            "use strict";

            if (!('Element' in view)) { return; }

            var classListProp = "classList",
				protoProp = "prototype",
				elemCtrProto = view.Element[protoProp],
				objCtr = Object,
				strTrim = String[protoProp].trim || function () {
				    return this.replace(/^\s+|\s+$/g, "");
				},
				arrIndexOf = Array[protoProp].indexOf || function (item) {
				    var i = 0,
						len = this.length;
				    for (; i < len; i++) {
				        if (i in this && this[i] === item) {
				            return i;
				        }
				    }
				    return -1;
				},
				// Vendors: please allow content code to instantiate DOMExceptions
				DOMEx = function (type, message) {
				    this.name = type;
				    this.code = DOMException[type];
				    this.message = message;
				},
				checkTokenAndGetIndex = function (classList, token) {
				    if (token === "") {
				        throw new DOMEx(
							"SYNTAX_ERR",
							"An invalid or illegal string was specified"
						);
				    }
				    if (/\s/.test(token)) {
				        throw new DOMEx(
							"INVALID_CHARACTER_ERR",
							"String contains an invalid character"
						);
				    }
				    return arrIndexOf.call(classList, token);
				},
				ClassList = function (elem) {
				    var trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
						classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
						i = 0,
						len = classes.length;
				    for (; i < len; i++) {
				        this.push(classes[i]);
				    }
				    this._updateClassName = function () {
				        elem.setAttribute("class", this.toString());
				    };
				},
				classListProto = ClassList[protoProp] = [],
				classListGetter = function () {
				    return new ClassList(this);
				};
            // Most DOMException implementations don't allow calling DOMException's toString()
            // on non-DOMExceptions. Error's toString() is sufficient here.
            DOMEx[protoProp] = Error[protoProp];
            classListProto.item = function (i) {
                return this[i] || null;
            };
            classListProto.contains = function (token) {
                token += "";
                return checkTokenAndGetIndex(this, token) !== -1;
            };
            classListProto.add = function () {
                var tokens = arguments,
					i = 0,
					l = tokens.length,
					token,
					updated = false;
                do {
                    token = tokens[i] + "";
                    if (checkTokenAndGetIndex(this, token) === -1) {
                        this.push(token);
                        updated = true;
                    }
                }
                while (++i < l);

                if (updated) {
                    this._updateClassName();
                }
            };
            classListProto.remove = function () {
                var tokens = arguments,
					i = 0,
					l = tokens.length,
					token,
					updated = false,
					index;
                do {
                    token = tokens[i] + "";
                    index = checkTokenAndGetIndex(this, token);
                    while (index !== -1) {
                        this.splice(index, 1);
                        updated = true;
                        index = checkTokenAndGetIndex(this, token);
                    }
                }
                while (++i < l);

                if (updated) {
                    this._updateClassName();
                }
            };
            classListProto.toggle = function (token, force) {
                token += "";

                var result = this.contains(token),
					method = result ? force !== true && "remove" : force !== false && "add";

                if (method) {
                    this[method](token);
                }

                if (force === true || force === false) {
                    return force;
                } else {
                    return !result;
                }
            };
            classListProto.toString = function () {
                return this.join(" ");
            };

            if (objCtr.defineProperty) {
                var classListPropDesc = {
                    get: classListGetter,
                    enumerable: true,
                    configurable: true
                };
                try {
                    objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
                } catch (ex) { // IE 8 doesn't support enumerable:true
                    if (ex.number === -0x7FF5EC54) {
                        classListPropDesc.enumerable = false;
                        objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
                    }
                }
            } else if (objCtr[protoProp].__defineGetter__) {
                elemCtrProto.__defineGetter__(classListProp, classListGetter);
            }

        }(self));

    } else {
        // There is full or partial native classList support, so just check if we need
        // to normalize the add/remove and toggle APIs.

        (function () {
            "use strict";

            var testElement = document.createElement("_");

            testElement.classList.add("c1", "c2");

            // Polyfill for IE 10/11 and Firefox <26, where classList.add and
            // classList.remove exist but support only one argument at a time.
            if (!testElement.classList.contains("c2")) {
                var createMethod = function (method) {
                    var original = DOMTokenList.prototype[method];

                    DOMTokenList.prototype[method] = function (token) {
                        var i, len = arguments.length;

                        for (i = 0; i < len; i++) {
                            token = arguments[i];
                            original.call(this, token);
                        }
                    };
                };
                createMethod('add');
                createMethod('remove');
            }

            testElement.classList.toggle("c3", false);

            // Polyfill for IE 10 and Firefox <24, where classList.toggle does not
            // support the second argument.
            if (testElement.classList.contains("c3")) {
                var _toggle = DOMTokenList.prototype.toggle;

                DOMTokenList.prototype.toggle = function (token, force) {
                    if (1 in arguments && !this.contains(token) === !force) {
                        return force;
                    } else {
                        return _toggle.call(this, token);
                    }
                };

            }

            testElement = null;
        }());

    }
}
/* jshint ignore:end */
;
(function (angular) {

	'use strict';

	var module = angular.module('haVariableSeatSelectionModule', ['ui.router', 'ui.bootstrap', 'ngSanitize', 'haSeatMapAPI']);
    var upgraded = location.hash.toLowerCase().indexOf('upgraded') > -1;
	if ((location.pathname.toLowerCase().indexOf('/book/inflightoptions') > -1) ||
		(location.pathname.toLowerCase().indexOf('/uidocs/demo/variableseatmap') > -1) ||
		(location.pathname.toLowerCase().indexOf('/my-account/my-trips/select-or-upgrade-seats') > -1)) {


		module.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
			try {
				$stateProvider.state('index', {
					url: '/flight/:flightId'
				});
				$urlRouterProvider.otherwise('/flight/1');
			} catch (error) {
				console.error('Tried to establish "index" state');
			}
		}]);
	}

	// Filter to strip off the end of leg cities.
	module.filter('removeState', [function() {
		return function(city) {
			return city.split(',')[0];
		}
	}]);

	// Author: Jamie Perkins (Updated for VSP by Michael Toymil and Travis Sisti)
	// the entire seat selection package - pax, tabs, legend, seatmap, etc
	module.directive('haVariableSeatSelection', ['$rootScope', 'haGlobals', 'haVariableSeatmapService', '$timeout', 'haConfig', '$state', '$stateParams', 'haSeatMapAPI', '$log', 'haModal', '$window', 'haSitecoreStrings', '$interval', '$filter', function ($rootScope, haGlobals, haVariableSeatmapSvc, $timeout, haConfig, $state, $stateParams, haSeatMapAPI, $log, haModal, $window, $scs, $interval, $filter) {
		return {
			restrict: 'A',
			scope: {},
			templateUrl: haConfig.getTemplateUrl('VariableSeatmap/ha-variable-seat-selection.html'),
			link: function ($scope, element, attrs) {

				$scope.upgradedToMainCabin = upgraded && !$rootScope.IsMainCabinUpgradeAvailable;
				$scope.standalone = typeof attrs.standalone === "string";

				$rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off

				// In some cases we need to use the VSM in existing mini SPAS
				// so we mock the ui-router features and hook straight into
				// the VSM state change handlers
				var disableRouting = typeof attrs.disableRouting === "string";
				var mcbSeatMapModal = "mcbSeatMapModalVSP";
				if (disableRouting) {
					// We can catch any ui-sref directives here and pipe them through our phony $state.
					$scope.$on('$stateNotFound', function (event, unfoundState, fromState, fromParams) {
						event.preventDefault();
						$state.go(unfoundState.to, unfoundState.toParams)
					});

					// Override $state and $stateParams
					$state = {
						go: function (route, params) {
							if (route !== 'index' || !params) {
								throw 'Invalid call to mock $state.go with disabledRouting set.';
							}
							$stateParams = params;
							$scope.$broadcast('$stateChangeSuccess');
						}
					};

					// TODO: hardcoded for now
					$stateParams = {
						flightId: 1
					}
				}

                $scope.svc = haVariableSeatmapSvc;
                $scope.entertaimentTooltipVisible = false;
                $scope.inFlightEntertainmentInfo = null;
                $scope.inFlightEntertainmentAvailable = null;
                $rootScope.adultPresent = { PaxType: 0 };
                $scope.upgradeSeatAvailable = false;
                $scope.MainCabinPriceDifference = $rootScope.MainCabinPriceDifference;
                $scope.upgradeToMainCabin = function () {
	                $scope.svc.upgradeToMainCabin()
		                .then(function () {
			                document.body.dispatchEvent(new CustomEvent('UpgradeToMainCabin', {
				                'detail': {
				                    'pageName': window.digitalData.page.pageInfo.name + ":" +  mcbSeatMapModal
				                }
			                }));
			                $window.location.href = $window.location.pathname + '?#upgraded';
		                });
                };

				$scope.dateFormatString;
                haGlobals('HA', function (ha) {
                    if (ha && ha.SCStrings) {
						if (ha.SCStrings.entertainment_data) {
							$scope.inFlightEntertainmentInfo = ha.SCStrings.entertainment_data;
						}
						if (ha.SCStrings.inflightoptionsinfo && ha.SCStrings.inflightoptionsinfo.dateformatstring) {
							$scope.dateFormatString = ha.SCStrings.inflightoptionsinfo.dateformatstring;
						}
                    }
					if ($rootScope.IsMainCabinUpgradeAvailable) {
						haModal({
						    id: mcbSeatMapModal,
							templateUrl: '/Templates/VariableSeatmap/ha-seatmap-MCB-upgrade.html',
							backdrop: 'true',
							modalLock: true,
							size: 'modal-md',
							scope: $scope
						});
					}
				});

				if (getUrlVars()['user']) {
					$scope.svc.selectedPassengerIndex = +getUrlVars()['user'].replace(/\/#\/flight\/./, '');
				}

				if ($rootScope.PassengerTripSummary) {
					$rootScope.adultPresent = $filter('filter')($rootScope.PassengerTripSummary.Passengers, { PaxType: 0 })[0];
				}
				if (window.sessionStorage.getItem('haMinor') && window.sessionStorage.getItem('haMinor') !== 'false') {
					$rootScope.adultPresent = { PaxType: 1 };
				}
				$scope.fareRulesLink = $window.fareRulesLink;
				$scope.showSeatingFilters = false;

				// get VM with segments, equipment type, passengers, etc.
				$scope.svc.getFlightVM();

				$scope.checkIFE = function (aircraft) {
					if (!aircraft) {
						return false;
					}
					var ifeOffered = false;
					if ($scope.inFlightEntertainmentInfo !== undefined && $scope.inFlightEntertainmentInfo.airplanes.length) {
						for (var i = 0; i < $scope.inFlightEntertainmentInfo.airplanes.length; i++) {
							var planes = $scope.inFlightEntertainmentInfo.airplanes[i];
							if (planes['airplane name'].toLowerCase() === aircraft.toLowerCase()) {
								ifeOffered = true;
							}
						}
					}
					//Check if In Flight Entertainment is available on this trip
					if (ifeOffered) {
						$scope.inFlightEntertainmentAvailable = ifeOffered;
					}
					return ifeOffered;
				}

				$scope.$watch('svc.passengers', function(nv,ov) {
					$scope.seatsChosenForLeg = false;
					for (var i = 0; i < $scope.svc.passengers.length; i++) {
						if ($scope.svc.passengers[i].Seats[$scope.svc.activeLegIndex].SeatLocation) {
							$scope.seatsChosenForLeg = true;
							break;
						}
					}
				}, true);

				$scope.$watch('svc.pageLoading', function () {

					if ($scope.svc.pageLoading === false) {
						$scope.svc.activeLegIndex = $stateParams.flightId - 1;
						if ($scope.svc.legs && $scope.svc.legs.length) {
							if (!$scope.svc.legs[$scope.svc.activeLegIndex]) {
								// leg entered in url which does not exist. use first leg
								$state.go('index', { flightId: 1 });
								return;
							}
							if ($scope.inFlightEntertainmentInfo === null) {
								$scope.svc.loadIFE().then(function (data) {
									$scope.inFlightEntertainmentInfo = data[0];
									renderSeatmapForLegIndex($scope.svc.activeLegIndex);
								});
							} else {
								renderSeatmapForLegIndex($scope.svc.activeLegIndex);
							}


							// if initial leg is codeshare, trigger bkgd load of other leg
							if ($scope.svc.legs[$scope.svc.activeLegIndex].IsCodeShare) {
								$scope.svc.getNextLeg();
							}
						}
						else {
							$scope.svc.handleError('Missing leg data for flights.');
						}
					}
				});

				$scope.tripSummaryDetailsModal = function () {
					if ($scope.svc.isChangeSeats || $scope.standalone) {
						return;
					}
					$rootScope.summaryModel = angular.copy($rootScope.PassengerTripSummary);
					haModal({
						backdrop: 'true',
						id: 'trip-details-modal',
						templateUrl: '/tripSummaryDetailsModal.html',
						scope: $rootScope
					});
				};

				$scope.$on('$stateChangeSuccess', function () {
					if (!$scope.svc.pageLoading && $stateParams.flightId - 1 !== $scope.svc.activeLegIndex && $scope.svc.legs[$stateParams.flightId - 1]) {
						$scope.$broadcast('cancelTooltip');
						$scope.svc.activeLegIndex = $stateParams.flightId - 1;
						$rootScope.$broadcast('seatSelectionLegChanged');
						renderSeatmapForLegIndex($scope.svc.activeLegIndex);
						$scope.showCTABar = false;
						document.documentElement.scrollTop = document.body.scrollTop = 0; // scroll to top
					}
				});

				function getUrlVars() {
					var vars = [], hash;
					var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
					for (var i = 0; i < hashes.length; i++) {
						hash = hashes[i].split('=');
						vars.push(hash[0]);
						vars[hash[0]] = hash[1];
					}
					return vars;
				}

				function renderSeatmapForLegIndex(legIndex) {
					if (haVariableSeatmapSvc.disallowAdvanceSeatSelection($scope.svc.legs[legIndex], $scope.$switch('InFlightOptionsInfo:EnableFAARestriction'), 'ppg', $scope.svc)) {
						return haVariableSeatmapSvc.setFAARestriction($scope.svc.legs[$scope.svc.activeLegIndex], $scope.svc);
					}

					if (!$scope.svc.legs[legIndex].IsCodeShare) {

						$scope.svc.legs[legIndex].error = null;

						if (!$scope.svc.legs[legIndex].promise) {
							$scope.svc.legs[legIndex].promise = $scope.svc.getAvailableSeatsForLeg(legIndex);
						}

						$scope.svc.legs[legIndex].promise.then(function () {
							// needs to be sequential because loading svg depends on equip type from seats call
							$scope.svc.loadSvgSeatmapForLeg(legIndex).then(function () {

								// conditional filtering/unavailability of cabins
								// resetFilters();
								checkForUpgradableSeats();

								var fareClass = '';
								if ($scope.svc.legs[legIndex].selectedClassForDisplay !== undefined && $scope.svc.legs[legIndex].selectedClassForDisplay !== null) {
									fareClass = $scope.svc.legs[legIndex].selectedClassForDisplay.toLowerCase();
								}
								else
									fareClass = $scope.svc.legs[legIndex].SelectedFareClass;
								if ((fareClass.includes('first') || fareClass.includes('business')) && $scope.svc.legs[legIndex].EquipmentCode !== 'AT5') {
									// filterMainCabin();
									// only in reshop can user downgrade from first
									if (!$scope.IsChangeFlightBooking) {
										$scope.svc.legs[legIndex].disableMainCabin = true;
									}
								}
								// make FC cabin unavailable:
								if (!fareClass.includes('first') && !fareClass.includes('business')) {
									// if changing seats
									if ($scope.svc.isChangeSeats) {
										$scope.svc.legs[legIndex].disableFirstClass = true;
										// filterFirstClass();
									}
									// under certain booking conditions, disable first
									else if ($rootScope.PassengerTripSummary) {
										if (// E-cert
											($rootScope.PassengerTripSummary.SelectedPromo !== null) ||
											// Miles Redemption
											($rootScope.PassengerTripSummary.BookingType > 0) ||
											// Purchased miles
											($rootScope.PassengerTripSummary.MilesPurchaseDetails !== null) ||
											// Orbitz APH Path
											($rootScope.PassengerTripSummary.SelectedHotel !== null)) {

											$scope.svc.legs[legIndex].disableFirstClass = true;
											// filterFirstClass();
										}
									}
								}

								// pre-assign seat selections if they exist
								angular.forEach($scope.svc.passengers, function (pax) {

									if (pax.Seats[legIndex].SeatLocation) {
										var modelSeat = $scope.svc.legs[legIndex].seatModel.seats[pax.Seats[legIndex].SeatLocation];
										$.extend(modelSeat,{
											// price: pax.Seats[legIndex].SeatAmount,
											isSelected: true,
											pax: pax,
											available: true
										});
										if (modelSeat !== undefined && modelSeat !== null && modelSeat.seatType === 1 && modelSeat.originalPrice) {
											pax.Seats[legIndex].PreferredSeatOriginalPrice = modelSeat.originalPrice;
										}
										if (pax.Seats[legIndex].SeatAmount > 0) {
											pax.Seats[legIndex].SurchargeOk = true;
										}
										$scope.svc.includeSeatNumber = true;
									}
								});

								if (!$scope.svc.legs[legIndex].resolved) {
									$scope.svc.legs[legIndex].resolved = true;
									$rootScope.$broadcast('legResolvedAtIndex', legIndex);
								}

								$scope.$broadcast('renderSeatmapAtIndex', legIndex);
								calculateSeatsTotal();
								$scope.$emit('haStickyResize');

								// for mobile, adjust height for svg scaling
								if ($rootScope.isMobile) {
									var $svg = $('#svgSeatmap' + legIndex).find('svg');
									if (!$svg.attr('scaled')) {
										var waitForRender = $interval(function () {
											if ($('#svgSeatmap' + legIndex).outerWidth() > 100) {
												$interval.cancel(waitForRender);
												var initialWidth = parseFloat($svg.attr('width')),
													initialHeight = parseFloat($svg.attr('height')),
													scaledWidth = $('#svgSeatmap' + legIndex).outerWidth(),
													scaleFactor = scaledWidth / initialWidth,
													scaledHeight = scaleFactor * initialHeight;
												$log.debug('svg scale factor:', scaleFactor);
												$scope.svc.svgScaleFactor = scaleFactor;
												$svg.attr({
													height: scaledHeight,
													scaled: true
												});
											}
										}, 50);
									}
								}

							}, handlePromiseError);

						}, handlePromiseError);
					}
				}

				function handlePromiseError(error) {
					$scope.svc.legs[$scope.svc.activeLegIndex].resolved = true;
					$scope.svc.legs[$scope.svc.activeLegIndex].error = error;
					$log.error(error);
				}

				function checkForUpgradableSeats() {
					if ($scope.svc.legs && $scope.svc.legs.length && !$scope.upgradeSeatAvailable) {
						$scope.svc.legs.forEach(function (leg) {
							if (typeof leg.seatModel !== 'undefined' && typeof leg.seatModel.seats !== 'undefined') {
								for (var seat in leg.seatModel.seats) {
									if (leg.seatModel.seats[seat].price > 0) {
										$scope.upgradeSeatAvailable = true;
										break;
									}
								}
							}
						});
					}
				}

				function getSelectedFareClass(seat) {
					if (seat.isStandard) {
						return 'coach';
					}
					if (seat.isPreferred) {
						return 'preferred';
					}
					if (seat.isExtraComfort) {
						return 'extracomfort';
					}
					if (seat.isFirstClass) {
						return 'first';
					}
					if (seat.isMainCabinPreferred) {
						return 'maincabinpreferred';
					}

				}

				// SELECTING SEATS

				$scope.$on('seatWasSelectedOrDeselected', function (event, data) {
					$scope.selectSeat(data);
					$scope.svc.includeSeatNumber = true;
				});

				var seatConfirmed = false; // for eliminating double-confirms on mobile

				$scope.assignSeat = function () {
					$scope.$emit('closeModal');
					seatConfirmed = false;
					var seatObj = $scope.seat;
					var pax = $scope.svc.passengers[$scope.svc.selectedPassengerIndex],
						paxSeat = pax.Seats[$scope.svc.activeLegIndex],
						fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;
					// if some other seat was selected, deselect it
					if (paxSeat.SeatLocation) {
						$scope.svc.legs[$scope.svc.activeLegIndex].seatModel.seats[paxSeat.SeatLocation].isSelected = false;
					}
					// finally, select seat
					seatObj.isSelected = true;
					paxSeat.SeatLocation = seatObj.name;
					paxSeat.SeatAmount = seatObj.price;
					paxSeat.Type = seatObj.Type;
					paxSeat.PreferredSeatOriginalPrice = seatObj.originalPrice;
					paxSeat.isMainCabinPreferred = seatObj.isMainCabinPreferred;

					if (paxSeat.originalSeat) {
						paxSeat.originalSeat.isChanged = false;
					}
					seatObj.pax = pax; // assign pax for tooltip

					// IsUpgrade used in TripSummary.html
					paxSeat.IsUpgrade = seatObj.Type > 0 && /coach/gi.test(fareClass);

					// isSurcharge used for showing seat charges on seat selection
					if ((seatObj.Type === 1 && fareClass !== 'extracomfort') || (seatObj.Type === 2 && fareClass !== 'preferred')) {
						paxSeat.isSurcharge = true;
					} else {
						paxSeat.isSurcharge = false;
					}

					$rootScope.$broadcast('ancelaryStateChange');
					$rootScope.$broadcast('refreshSeatmapTooltip');

					nextPassenger();
					$scope.$broadcast('updateSeatmapAtIndex', $scope.svc.activeLegIndex);
					calculateSeatsTotal();

				}

				$scope.selectSeat = function (seatObj) {

					var pax = $scope.svc.passengers[$scope.svc.selectedPassengerIndex],
						paxSeat = pax.Seats[$scope.svc.activeLegIndex];

					if (typeof paxSeat.SeatLocation === 'undefined' || paxSeat.SeatLocation === null || paxSeat.SeatLocation.length === 0) {

						var fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;
					} else {
						var fareClass = getSelectedFareClass($scope.svc.legs[$scope.svc.activeLegIndex].seatModel.seats[paxSeat.SeatLocation]);
					}

					// deselect seat
					if (seatObj.isSelected) {
						delete seatObj.pax;
						seatConfirmed = false;
						angular.forEach($scope.svc.passengers, function (passenger, i) {
							if (passenger.Seats[$scope.svc.activeLegIndex].SeatLocation &&
								passenger.Seats[$scope.svc.activeLegIndex].SeatLocation === seatObj.name) {
								seatObj.isSelected = false;
								passenger.Seats[$scope.svc.activeLegIndex].SeatLocation = null;
								if (!passenger.Seats[$scope.svc.activeLegIndex].DowngradeOk && $scope.svc.originalPassengerFares) {
									passenger.Seats[$scope.svc.activeLegIndex].SeatAmount = $scope.svc.originalPassengerFares[i][$scope.svc.activeLegIndex].SeatAmount;
									passenger.Seats[$scope.svc.activeLegIndex].Type = $scope.svc.originalPassengerFares[i][$scope.svc.activeLegIndex].Type;
								} else {
									passenger.Seats[$scope.svc.activeLegIndex].SeatAmount = null;
									passenger.Seats[$scope.svc.activeLegIndex].Type = 0;
								}
								$scope.svc.selectedPassengerIndex = i;
								$rootScope.$broadcast('ancelaryStateChange'); // ugh spelling
								$rootScope.$broadcast('refreshSeatmapTooltip');
								$scope.$broadcast('updateSeatmapAtIndex', $scope.svc.activeLegIndex);
								calculateSeatsTotal();
								return;
							}
						});
						$scope.showCTABar = true;
					}
					// select seat
					else {

						// confirm First Class upgrade
						if (seatObj.isFirstClass && fareClass && fareClass.toLowerCase && fareClass.toLowerCase() !== 'first' && fareClass.toLowerCase() !== 'business') {
							$timeout(function () {
								haModal({
									id: 'ConfirmUpgradeModal',
									backdrop: 'true',
									templateUrl: '/upgradeToFirstModal.html',
									scope: $scope
								});
							});
							return;
						}

						// confirm downgrade / difference
						if (!paxSeat.DowngradeOk && fareClass !== 'first' &&
						// seat credit
						(paxSeat.originalSeat && !paxSeat.originalSeat.isPrereservedSeat && paxSeat.originalSeat.price > seatObj.price && !paxSeat.originalSeat.isChanged))
						{
							$scope.seat = seatObj;
							$scope.originalSeat = paxSeat.originalSeat;

							if (!seatObj.isExtraComfort && (!seatObj.isMainCabinPreferred || (seatObj.isMainCabinPreferred && paxSeat.originalSeat.isExtraComfort))) {
								$timeout(function () {
									haModal({
										id: 'ConfirmDowngradeModalVSP',
										backdrop: 'true',
										templateUrl: '/downgradeSeatModal.html',
										scope: $scope,
										extendScope: {
											fareClass: fareClass
										}
									});
								});
								return;
							}
						}

						// confirm exit row compliance
						if (seatObj.isExitRow && !paxSeat.ExitOk) {
							$scope.seat = seatObj;
							$timeout(function () {
								haModal({
									id: 'ConfirmExitRowModalVSP',
									backdrop: 'true',
									template: angular.element('.confirmExitRowModal'),
									scope: $scope
								});
							});
							return;
						}
						$scope.seat = seatObj;
						$scope.assignSeat();
						$scope.showCTABar = true;
					}
				};

				$scope.$on('mobileTooltipWasDisplayed', function() {
					$scope.showCTABar = false;
				});

				$scope.seatsChanged = function () {
					var changed = false;
					angular.forEach($scope.svc.passengers, function (passenger, i) {
						angular.forEach(passenger.Seats, function (seat, j) {
							if (seat.SeatLocation && seat.SeatLocation !== $scope.svc.passengersInitial[i].Seats[j].SeatLocation) {
								changed = true;
							}
						});
					});

					return changed;
				};

				$scope.confirmUpgrade = function () {
					$scope.$emit('closeModal');
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].DowngradeOk = false;
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].SurchargeOk = true;
					if ($scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat) {
						$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat.isChanged = true;
					}
					seatConfirmed = true;
					$scope.selectSeat($scope.seat);

					var offerType = '';
					if ($scope.seat.isExtraComfort) {
						offerType = 'extracomfort';
					} else if ($scope.seat.isPreferred) {
						offerType = 'preferred';
					}
					document.body.dispatchEvent(new CustomEvent('UpgradeSeatSelected', { 'detail': {
						'seatType': offerType
					}}));
				};

				$scope.confirmDowngrade = function () {
					$scope.$emit('closeModal');
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].DowngradeOk = true;
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].SurchargeOk = false;
					if ($scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat) {
						$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat.isChanged = true;
					}
					seatConfirmed = true;
					$scope.selectSeat($scope.seat);
				};

				// if a user has a higher valued EC seat, and selects a new flight with a lower valued EC seat
				$scope.confirmDifference = function () {
					// if EC/PC seat is selected and pax previously downgraded, add back the PremiumSeatAmount
					if ($rootScope.PassengerTripSummary && $scope.seat.Type > 0 &&
						$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].DowngradeOk) {
						$rootScope.PassengerTripSummary.Passengers[$scope.svc.selectedPassengerIndex].PremiumSeatAmount += $scope.seat.price;
					}
					var legSeatAmount = $scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].SeatAmount;
					if ($rootScope.PassengerTripSummary) {
						$rootScope.PassengerTripSummary.Passengers[$scope.svc.selectedPassengerIndex].PremiumSeatAmount -= legSeatAmount;
					}
					$scope.$emit('closeModal');
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].SurchargeOk = true;
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].DifferenceOk = true;
					if ($scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat) {
						$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].originalSeat.isChanged = true;
					}
					seatConfirmed = true;
					$scope.selectSeat($scope.seat);
				};


				$scope.confirmExitRow = function () {
					$scope.$emit('closeModal');
					$scope.svc.passengers[$scope.svc.selectedPassengerIndex].Seats[$scope.svc.activeLegIndex].ExitOk = true;
					$scope.selectSeat($scope.seat);
				};

				$scope.scrollToTop = function () {
					var offsetElem = ($('#SeatSelection').length) ? '#SeatSelection' : 'body';
					var offset = $(offsetElem).offset().top;
					$('body, html').animate({ scrollTop: offset }, 'fast');
				};

				function calculateSeatsTotal() {
					$timeout(function () { // ensures digest cycle is triggered
						$scope.svc.seatChargeSubtotals[$scope.svc.activeLegIndex] = 0;
						var legIndex = $scope.svc.activeLegIndex;
						angular.forEach($scope.svc.passengers, function (passenger) {

							var passengerSeat = passenger.Seats[legIndex],
								cost = passengerSeat.SeatAmount != undefined ? passengerSeat.SeatAmount : 0,
								fareClass = $scope.svc.legs[legIndex].SelectedFareClass;

		                    if ($scope.svc.isGroupBooking() && !passenger.IsMemberEligibleForDiscount && passengerSeat.isMainCabinPreferred && passengerSeat.PreferredSeatOriginalPrice) {
		                        cost = passengerSeat.SeatLocation != undefined ? passengerSeat.PreferredSeatOriginalPrice : 0;
		                    }

							// do they have a seat credit? (change seats/reshop)
							if (passengerSeat.originalSeat && passengerSeat.originalSeat.credit > 0) {
								cost -= passengerSeat.originalSeat.credit;
							}
							// did they buy an EC/PS fare
							else if (!passengerSeat.DowngradeOk && passengerSeat.IsUpgrade != null && !passengerSeat.IsUpgrade) {
								cost -= passengerSeat.SeatAmount;
							}

							cost = Math.max(cost, 0);
							passengerSeat.SeatAmountDisplay = cost;
							$scope.svc.seatChargeSubtotals[$scope.svc.activeLegIndex] += cost;
						});
						$scope.svc.seatChargeTotal = $scope.svc.seatChargeSubtotals.reduce(function(a,b) { return a + b}, 0);
					}, 0);
				}

				function nextPassenger() {
					if ($scope.svc.selectedPassengerIndex === $scope.svc.passengers.length - 1) {
						$scope.svc.selectedPassengerIndex = 0;
					} else {
						$scope.svc.selectedPassengerIndex++;
					}
				}

				$scope.selectPax = function(index) {
					$scope.svc.selectedPassengerIndex = index;
					$scope.$broadcast('updateSeatmapAtIndex', $scope.svc.activeLegIndex);
				}

				$scope.paxHaveSeatsForLegCount = function () {
					var count = 0;
					for (var i = 0, len = $scope.svc.passengers.length; i < len; i++) {
						if ($scope.svc.passengers[i].Seats[$scope.svc.activeLegIndex].SeatLocation) {
							count++;
						}
					}
					return count;
				};

				function seatTypeToSeatTypeString(type) {
					switch (type) {
						case 2:
							return 'Preferred';
						case 1:
							return 'ExtraComfort';
						default:
							return 'Standard';
					}
				}

				// STANDALONE BUTTON CONTROLS
				$scope.submitStandalone = function () {
					if (!$scope.seatsChanged()) {
						$scope.cancelStandalone();
						return;
					}
					var seatSelectionData = {};
					angular.forEach($scope.svc.legs, function (leg, i) {
						var legData = [];
						angular.forEach($scope.svc.passengers, function (passenger, j) {
							legData.push({
								id: passenger.TravelerID || passenger.ID,
								seat: passenger.Seats[i].SeatLocation,
								amount: passenger.Seats[i].SeatAmount,
								type: seatTypeToSeatTypeString(passenger.Seats[i].Type)
							})
						});
						seatSelectionData[leg.id] = legData;
					});
					$rootScope.$broadcast('$standaloneVerticalSeatmapSubmit', seatSelectionData);
				};
				$scope.cancelStandalone = function () {
					$rootScope.$broadcast('$standaloneVerticalSeatmapCancel');
				};

				// CANCEL SEAT CHANGE
				$scope.cancelSeatChange = function() {
					$window.location.href='/my-account/my-trips/itinerary-details';
				};

				// SUBMITTING SEATS
				$scope.submitSeatSelection = function (skipSeatSelection) {
					$scope.svc.setIFE($scope.inFlightEntertainmentAvailable).then($.noop, $.noop);

					// next flight button
					if (!skipSeatSelection && $scope.svc.activeLegIndex !== $scope.svc.legs.length - 1) {
						$scope.svc.selectedPassengerIndex = 0;
						$state.go('index', { flightId: $scope.svc.activeLegIndex + 2 });
						return;
					}

					if ($scope.standalone) {
						// If this is a standalone VSM we don't want to interact with the APIs here,
						// but rather leave that up to the context in which VSM is situated via
						// broadcasting events.
						$scope.submitStandalone();
						return;
					}


					if (!skipSeatSelection) {
						$scope.submitting = true;
					}

					var postObject = $scope.svc.createSeatsPostObject();
					$scope.svc.setUpgradableSeat($scope.upgradeSeatAvailable).finally(function () {
						// for change seats?
						if ($scope.svc.isChangeSeats) {
							delete postObject.InFlightOptions;
							delete postObject.TotalPremiumSeatAmount;
							//$log.debug('post object for change seats', postObject);
							// legacy endpoints first validate then submit
							haSeatMapAPI.ValidateEditSeatFareDifference(postObject).success(function (result) {
								$log.debug('validate seat fare diff result', result);
								switch (result) {
									case 1: // seats have been upgraded
										angular.element('#seatMapForm').submit();
										break;
									case 2: // seats have been downgraded
									case 3: // seats were changed with new seats of equal value
										haSeatMapAPI.ConfirmEditSeatsService(postObject).success(function (response) {
											$log.debug('confirm edit seats service response', response);
											angular.element('#seatMapForm').submit();
										}).error(function (error) {
											$scope.svc.handleError(error);
											$scope.submitting = false;
										});
										break;
									case 4: // no seats were changed
										$scope.svc.handleError(noSeatChangeErrorText);
										$scope.submitting = false;
										break;
									default: // should not happen
										$scope.svc.handleError('Unhandled result for ValidateEditSeatFareDifference request: ' + result);
										$scope.submitting = false;
										break;
								}

                            }).error(function (error) {
                                $scope.svc.handleError(error);
                                $scope.submitting = false;
                            });
                        }
                        // regular booking or reshop
                        else {
                            if ($rootScope.IsChangeFlightBooking) {
                                haSeatMapAPI.UpdateChangeFlightInflightoptions(postObject, JSON.stringify(postObject)).success(function () {
                                    angular.element('#seatMapForm').submit();
                                }).error(function () {
                                    angular.element('#seatMapForm').submit();
                                });
                            }
                            else {
                                haSeatMapAPI.UpdateInflightOptionsService(postObject, JSON.stringify(postObject)).success(function () {

									// Mpulse tracking - trip insurance quote request.
	                                if (window.BOOMR && BOOMR.version) {
		                                window.BOOMR.sendMetric("Alnz_Quote_Rq", 1);
									}

	                                angular.element('#seatMapForm').submit();
                                }).error(function (error) {
                                    $scope.svc.handleError(error);
									$scope.submitting = false;
                                });
                            }
						}
					});
				};

				// guest info modal
                $scope.showGuestModal = function(e) {
					haModal({
						id: "guest-information-modal-contents",
						templateUrl: '/Templates/VariableSeatmap/ha-guest-info-modal.html',
						backdrop: 'true',
						modalLock: false,
						size: 'modal-md',
						scope: $scope,
						mobileVerticalCenter: true
					});
                };

				// leg info modal
				$scope.showLegInfoModal= function(e) {
					haModal({
						id: "leg-information-modal-contents",
						templateUrl: '/Templates/VariableSeatmap/ha-leg-info-modal.html',
						backdrop: 'true',
						modalLock: false,
						// size: 'modal-md',
						scope: $scope,
						mobileVerticalCenter: true
					});
				};

				var $body = $('body');
				$scope.upgradeTracking = function (type, leg) {
					var seatUpgradeOffer = '';
					if (type == "EXTRACOMFORT" && (!$body.data('UpgradeSeatOffered') || $body.data('UpgradeSeatOffered') !== leg)) {
						seatUpgradeOffer = 'extracomfort';
					}

					if (type == "PREFERREDSEAT" && (!$body.data('UpgradeSeatOffered') || $body.data('UpgradeSeatOffered') !== leg)) {
						seatUpgradeOffer = 'preferred';
					}

					if (seatUpgradeOffer) {
						$body.data('UpgradeSeatOffered', leg);
						document.body.dispatchEvent(new CustomEvent('UpgradeSeatOffered', { 'detail': {
							'seatType': seatUpgradeOffer
						}}));
					}

					return true;
				}
			}
		};
	}]);

	// NOTE: Duplicating due to time constraints. If we need to add more messages, please consider refactoring these into a message system.
	module.directive('haEliteCompanionsTooltip', ['$rootScope', 'haConfig', 'haVariableSeatmapService', function($rootScope, haConfig, haVariableSeatmapService) {
		return {
			restrict: 'A',
			templateUrl: haConfig.getTemplateUrl('VariableSeatmap/ha-elite-companions-tooltip.html'),
			scope: true,
			link: function ($scope, $el) {
				$scope.svc = haVariableSeatmapService;
				var $tooltip = $el.children('#elite-companions-tooltip-wrapper');
				var containsElite;
				var containsNonElite;
				$scope.tooltipShouldDisplay = function() {
					if ($scope.svc.isPreview || !$scope.svc.isForSelection) {
						return false;
					}
					containsElite = containsElite === undefined ? $scope.svc.passengers.filter(function(el){ return el.IsMemberEligibleForDiscount }).length : containsElite;
					containsNonElite = containsNonElite === undefined ? $scope.svc.passengers.filter(function(el){ return !el.IsMemberEligibleForDiscount }).length : containsNonElite;
					return $scope.svc.isGroupBooking() && containsElite && containsNonElite;
				}

				function evaluateTooltip() {
					if ($scope.tooltipShouldDisplay()) {
						$tooltip.addClass('animate-show-bar');
					}
				}

				$scope.hideTooltip = function() {
					$tooltip.removeClass('animate-show-bar').addClass('animate-hide-bar');
				}

				$scope.$on('renderSeatmapAtIndex', function(event, data) {
					if ($scope.svc.legs[$scope.svc.activeLegIndex] && $scope.svc.legs[$scope.svc.activeLegIndex].resolved) {
						evaluateTooltip();
					}
				});

				// Evaluate on link in case the directive links after seatmap render.
				evaluateTooltip();
			}
		}
	}]);
	module.directive('haComplimentarySeatsTooltip', ['haConfig', 'haVariableSeatmapService', function(haConfig, haVariableSeatmapService) {
		return {
			restrict: 'A',
			templateUrl: haConfig.getTemplateUrl('VariableSeatmap/ha-complimentary-seats-tooltip.html'),
			scope: true,
			link: function ($scope, $el) {
				$scope.svc = haVariableSeatmapService;
				var $tooltip = $el.children('#complimentary-seats-tooltip-wrapper');

				$scope.tooltipShouldDisplay = function() {
					// There are fewer free seats than passengers and there are available seats to upgrade to
					return ($scope.svc.legs[$scope.svc.activeLegIndex].seatModel &&
						$scope.svc.passengers &&
						$scope.svc.legs[$scope.svc.activeLegIndex].seatModel.availableStandardSeats - $scope.svc.passengers.length < 0 &&
						($scope.svc.legs[$scope.svc.activeLegIndex].seatModel.availablePreferredSeats > 0 || $scope.svc.legs[$scope.svc.activeLegIndex].seatModel.availableMainCabinPreferredSeats > 0)
					);
				}

				function evaluateTooltip() {
					if ($scope.tooltipShouldDisplay()) {
						$tooltip.addClass('animate-show-bar');
					}
				}

				$scope.hideTooltip = function() {
					$tooltip.removeClass('animate-show-bar').addClass('animate-hide-bar');
				}

				$scope.$on('renderSeatmapAtIndex', function(event, data) {
					if ($scope.svc.legs[$scope.svc.activeLegIndex] && $scope.svc.legs[$scope.svc.activeLegIndex].resolved) {
						evaluateTooltip();
					}
				});

				// Evaluate on link in case the directive links after seatmap render.
				evaluateTooltip();
			}
		}
	}]);
	// seat credit tooltip
	module.directive('haSeatCreditTooltip', [ 'haConfig', '$log', '$window', '$timeout', 'haVariableSeatmapService', '$rootScope', function (haConfig, $log, $window, $timeout, haVariableSeatmapService, $rootScope) {

		return {
			restrict: 'A',
			templateUrl: haConfig.getTemplateUrl('VariableSeatmap/ha-seat-credit-tooltip.html'),
			scope: true,
			link: function ($scope, $el) {

				$scope.svc = haVariableSeatmapService;
				var $tooltip = $el.children('#seat-credit-tooltip-wrapper');

				$scope.$on('legResolvedAtIndex', function(event, data) {
					if (data === $scope.svc.activeLegIndex) {
						$log.debug('received legResolvedAtIndex');
						queueShowHideAnimation();
					}
				});
				$scope.$on('seatSelectionLegChanged', function() {
					$log.debug('recieved seatSelectionLegChanged');
					if ($scope.svc.legs[$scope.svc.activeLegIndex] && $scope.svc.legs[$scope.svc.activeLegIndex].resolved) {
						queueShowHideAnimation();
					}
				});

				// on pax change
				$scope.$watch('svc.selectedPassengerIndex', function(newVal, oldVal) {
					if (newVal !== undefined && $scope.svc.legs[$scope.svc.activeLegIndex] && $scope.svc.legs[$scope.svc.activeLegIndex].resolved) {
						queueShowHideAnimation();
					}
				});

				$scope.$on('reshopCreditAppliedForLeg', function(event, data) {
					if (data === $scope.svc.activeLegIndex) {
						$log.debug('received reshopCreditAppliedForLeg');
						queueShowHideAnimation();
					}
				});

				// force close
				$scope.closeTooltip = function($event) {
					animations.hideBar();
				}

				// animation queue
				var animationQueue = [],
					animating = false,
					barIsVisible = false,
					animations = {
						hideBar: function() {
							if (barIsVisible) {
								$tooltip.removeClass('animate-show-bar').addClass('animate-hide-bar');
								$timeout(function() {
									barIsVisible = false;
								}, 300);
							}
						},
						showBar: function() {
							if (!barIsVisible) {
							    var pax = $scope.svc.passengers[$scope.svc.selectedPassengerIndex],
									paxSeat = pax.Seats[$scope.svc.activeLegIndex],
									fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;

								if (!$scope.ready) {
									$scope.ready = true;
								}
								$scope.selectingPassenger = pax;
								$scope.currency = $rootScope.$currency;

								// only show bar if there is a seat credit
								if (fareClass !== 'first' && paxSeat.originalSeat && paxSeat.originalSeat.credit > 0) {
									$tooltip.removeClass('animate-hide-bar').addClass('animate-show-bar');

									$timeout(function() {
										barIsVisible = true;
									}, 300);
								}
							}
						}
					};

				function queueShowHideAnimation() {
					queueAnimation('hideBar', 300);
					queueAnimation('showBar', 300);
				}

				function queueAnimation(name, duration, callback) {
					var okToAdd = true;
					for (var i = 0, len = animationQueue.length; i < len; i++) {
						if (animationQueue[i].name === name) {
							okToAdd = false;
							break;
						}
					};
					if (okToAdd) {
						animationQueue.push({
							name: name,
							duration: duration,
							callback: callback
						});
					}
					if (!animating) {
						fireNextAnimationInQueue();
					}
				}
				function fireNextAnimationInQueue() {
					if (!animationQueue[0]) {
						animating = false;
						return;
					}
					if (!animating) {
						animating = true;
					}
					animations[animationQueue[0].name]();
					$timeout(function() {
						if (animationQueue[0].callback) {
							animationQueue[0].callback();
						}
						animationQueue.shift();
						fireNextAnimationInQueue();
					}, animationQueue[0].duration);
				}

				// if sticky message bar loads after listened for events fire
				if ($scope.svc.legs[$scope.svc.activeLegIndex] && $scope.svc.legs[$scope.svc.activeLegIndex].resolved) {
					queueShowHideAnimation();
				}

			}
		}
	}]);

	// for attaching InFlightVM to scope, for child directive's scope inheritance
	module.controller('haVariableInFlightOptionsController', ['$scope', 'haGlobals', '$rootScope', 'haPassengersService', '$timeout', 'haVariableSeatmapService', function ($scope, haGlobals, $rootScope, $pax, $timeout, haVariableSeatmapSvc) {

		haGlobals('InFlightVM', function (InFlightVM) {
			$.extend($rootScope, InFlightVM);
		});

		haGlobals('infantlap', function (infantlap) {
			$rootScope.infantLapList = infantlap;
		});

		haGlobals('groupPNRSelectedPax', function (groupPNRSelectedPax) {
			$rootScope.groupPNRSelectedPax = groupPNRSelectedPax;
		});

		// update haPassengersService for sticky progress bar
		// picked up from $scope.updatePassengerList() in ha-seat-selection.js
		var firstUser = true;

		if ($rootScope.TripSummary) {
			angular.forEach($rootScope.TripSummary.Passengers, function (passenger) {

				var paxType = 'Adult';
				if (passenger.ActualPaxTypeSelected) {
					paxType = passenger.ActualPaxTypeSelected;
				} else if (passenger.Type) {
					paxType = passenger.Type;
				}

				if ($rootScope.IsChangeFlightBooking) {
					switch (passenger.PaxFareCode) {
						case 'ADT':
							paxType = 'Adult';
							break;
						case 'CHD':
							paxType = 'Child';
							break;
						default:
							paxType = 'Adult';
							break;
					}
				}

				$pax.add({
					type: paxType,
					isUser: firstUser,
					FirstName: passenger.FirstName,
					LastName: passenger.LastName,
					AvatarUrl: passenger.AvatarImage,
					Id: passenger.TravelerAssociationID
				});
				if (firstUser === true) {
					firstUser = false;
				}
			});
			// for ha-receipt
			//update selected segments from trip summary
			$scope.selectedSegments = [];
			angular.forEach($rootScope.TripSummary.Trips, function (trip) {
				var segment = trip.Flights[0];
				segment.IsMileagePricing = trip.IsMileagePricing;

				for (var i = 0; i < segment.AvailBookingFares.length; i++) {
					segment.selectedSeatClass = segment.AvailBookingFares[i].Name;
					segment[segment.selectedSeatClass] = segment.AvailBookingFares[i];
				}
				$scope.selectedSegments.push(segment);
			});

			$scope.$on('ancelaryStateChange', function () {
				// need to set $scope.seatSelection so that seat choices can be
				// reflected in ha-receipt for reshop
				$scope.seatSelection = haVariableSeatmapSvc.createSeatsPostObject();
				$rootScope.TripSummary.TotalPremiumSeatAmount = $scope.seatSelection.TotalPremiumSeatAmount;
			});
		}

	}]);

})(angular);
;
(function (angular) {

	// haAdvanceToNextInputService
	// --------------------------------------------
	//
	// * **Class:** HaAdvanceToNextInputService
	// * **Author:** Nathan Probst
	//
	// Service for advancing to the next input on valid selection.

	'use strict';

	var mod = angular.module('haAdvanceToNextInputService', []);

	mod.service('haAdvanceToNextInput', [function () {
		return {
			next: function ($element) {
				var selectable = $('input[ha-advance-to-next-input]');
				var currentIndex = selectable.index($element.find('input[ha-advance-to-next-input]'));
				var nextElement = selectable.eq(currentIndex + 1);

				if (nextElement.length > 0 && currentIndex >= 0) {
					setTimeout(function () {
						nextElement.focus();
					}, 0);
				}
			}
		};
	}]);

	// Ensure the service is initialized
	mod.run(['$rootScope', 'haAdvanceToNextInput', function ($rootScope, svc) {
		$rootScope.$on('haAdvanceToNextInput:next', function (event, $element) {
			svc.next($element);
		});
	}]);

})(angular);
;
(function (angular) {

	// Ha Help And Tips Service
	// --------------------------------------------
	//
	// * **Class:** HaCalendarEventsService
	// * **Author:** George Pantazis
	//
	// Service for asynchronously retrieving different types of events for the calendar,
	// or other date-specific components.

	'use strict';

	var mod = angular.module('haCalendarEventsService', []);

	mod.service('haCalendarEventsService', function () {

		// Mock model. Replace this with an $http call to retrieve API data
		// during integration with the HA API infrastructure.

		var model = {
			// A random ID.
			'123': {
				// Returns an array of days under the type `promoDays`.
				'promoDays': [{
					'start': {
						'year': 2013,
						'month': 10,
						'day': 20
					},
					'end': {
						'year': 2013,
						'month': 11,
						'day': 1
					}
				}, {
					'start': {
						'year': 2013,
						'month': 11,
						'day': 16
					},
					'end': {
						'year': 2013,
						'month': 11,
						'day': 18
					}
				}, {
					'start': {
						'year': 2013,
						'month': 11,
						'day': 30
					},
					'end': {
						'year': 2013,
						'month': 12,
						'day': 1
					}
				}]
			},

			'456': {
				'unavailableDays': [{
					'start': {
						'year': 2013,
						'month': 11,
						'day': 8
					},
					'end': {
						'year': 2013,
						'month': 11,
						'day': 10
					}
				}]
			},

			// Can return multiple kinds of days.
			'789': {
				'unavailableDays': [{
					'start': {
						'year': 2013,
						'month': 11,
						'day': 8
					},
					'end': {
						'year': 2013,
						'month': 11,
						'day': 10
					}
				}],
				'promoDays': [{
					'start': {
						'year': 2013,
						'month': 10,
						'day': 20
					},
					'end': {
						'year': 2013,
						'month': 11,
						'day': 1
					}
				}, {
					'start': {
						'year': 2013,
						'month': 11,
						'day': 16
					},
					'end': {
						'year': 2013,
						'month': 11,
						'day': 18
					}
				}, {
					'start': {
						'year': 2013,
						'month': 11,
						'day': 30
					},
					'end': {
						'year': 2013,
						'month': 12,
						'day': 1
					}
				}]
			}
		};

		var service = {

			// $http request would occur here; dependencies on this service should utilize
			// the `cb` to pass a function, and expect results asynchronously.

			// Using a timeout to simulate async response.

			getEvents: function (id, cb) {
				setTimeout(function () {
					cb(model[id]);
				}, 1);
			}

		};

		return service;
	});

})(angular);
;
(function (angular) {

	// Hawaiian Cities Service
	// =========================
	//
	// * **Class:** haCities
	// * **Author:** Nathan Probst
	//
	// A service for returning data about cities HA services

	'use strict';

	function repromise(fn, times) {
		if (typeof times === 'undefined') {
			times = 3; // default
		}
		return fn().then(
			undefined, // pass through success
			function (err) {
				// only on timeout or specific error codes
				if ((times > 0) && ([0, 408, 500, 502, 503, 504].indexOf(err.status) >= 0)) {
					return repromise(fn, times - 1); // retry
				}
				throw err; // rethrow
			}
		);
	}

	var module = angular.module('haCitiesModule', ['haCitiesAPI']);

	module.factory('haCitiesSvc', [
		'$q',
		'$log',
		'$filter',
		'haCitiesAPI',

		function ($q, $log, $filter, api) {

			var svc = {};
			var isExpertBooking = false;

			var filter = function (list, fn) {
				if (fn != null) {
					// Allow named, registered filter functions...
					if (typeof fn === 'string') {
						fn = $filter(fn);
					}
					if (typeof fn !== 'function') {
						return list;
					}
					var fList = [];
					for (var i = 0; i < list.length; i++) {
						if (list[i] != null && fn(list[i])) {
							fList.push(list[i]);
						}
					}
					return fList;
				}
				return list;
			};

			var getCityListPromiseExpertBooking = function (filterFn) {
				isExpertBooking = true;
				return getCityListPromise(filterFn);
			};

			var getCityListPromise = function (filterFn) {
				if (isExpertBooking && !svc.CITY_LIST_EXPERT_BOOKING) {
					svc.CITY_LIST = null; // bust any old cities if we're in ExpertBooking
				}
				if (svc.CITY_LIST) {
					var deferred = $q.defer();
					deferred.resolve(filter(svc.CITY_LIST, filterFn));

					return deferred.promise;
				} else {
					var cityListFn = isExpertBooking ? api.getCityListExpertBooking : api.getCityList;
					return repromise(cityListFn).then(function (data) {
						svc.CITY_LIST = data;
						if (isExpertBooking) {
							svc.CITY_LIST_EXPERT_BOOKING = data;
						}
						return filter(svc.CITY_LIST, filterFn);
					});
				}
			};

			var getFilteredCityListPromise = function (legIndex, isOrigin) {
				// create a re-callable closure
				var fn = function () {
					return api.getFilteredCityList(legIndex, isOrigin);
				};
				return repromise(fn).then(function (cityList) {
					return cityList;
				});
			};


			var getCityMapPromise = function () {
				if (svc.CITY_MAP != null) {
					var deferred = $q.defer();
					deferred.resolve(svc.CITY_MAP);

					return deferred.promise;
				} else {
					return getCityListPromise().then(function (cities) {
						svc.CITY_MAP = {};
						for (var i = 0; i < cities.length; i++) {
							svc.CITY_MAP[cities[i].Code] = cities[i];
						}

						return svc.CITY_MAP;
					});
				}
			};

			var getCityPairsPromise = function () {
				if (isExpertBooking && !svc.CITY_PAIRS_EXPERT_BOOKING) {
					svc.CITY_PAIRS = null; // bust any old pairs if we're in ExpertBooking
				}
				if (svc.CITY_PAIRS) {
					var deferred = $q.defer();
					deferred.resolve(svc.CITY_PAIRS);

					return deferred.promise;
				} else {
					var cityPairsFn = isExpertBooking ? api.getCityPairsExpertBooking : api.getCityPairs;
					return repromise(cityPairsFn).then(function (data) {
						svc.CITY_PAIRS = data;
						if (isExpertBooking) {
							svc.CITY_PAIRS_EXPERT_BOOKING = data;
						}
						return svc.CITY_PAIRS;
					});
				}
			};

			var getNitpMapPromise = function () {
				if (svc.NITP_MAP != null) {
					var deferred = $q.defer();
					deferred.resolve(svc.NITP_MAP);

					return deferred.promise;
				} else {
					return repromise(api.getNitpMap).then(function (data) {
						svc.NITP_MAP = data;

						return svc.NITP_MAP;
					});
				}
			};

			var getCityByCodePromise = function (code) {
				return getCityMapPromise().then(function (map) {
					return map[code];
				});
			};

			var getCityByCodeSync = function (code) {
				return svc.CITY_MAP[code];
			};

			// returns an array of codes that are valid pairs with origin
			var getValidPairs = function (originCode, maxHops) {
				var result = [];
				maxHops = maxHops || 1;
				if (!svc.CITY_PAIRS || !svc.CITY_MAP) {
					return [];
				}
				var originPairs = svc.CITY_PAIRS[originCode]
				for (var p = 0; p < originPairs.length; p++) {
						result.push(svc.CITY_MAP[originPairs[p]])
				}
				return result;
			};

			var getValidPairsPromise = function (originCode, maxHops) {

				maxHops = maxHops || 1;

				return $q.all([getCityPairsPromise(), getCityMapPromise()]).then(function (resolved) {
					return getValidPairs(originCode, maxHops);
				});
			};

			var getReachableCitiesPromise = function (originCode, maxHops, filterFn) {
				// All cities are considered reachable from undefined origin.
				if (originCode == null) {
					return getCityListPromise(filterFn);
				}

				maxHops = maxHops || 1;

				// var then;
				// if (typeof performance !== 'undefined') {
				//     then = performance.now();
				// }

				return getValidPairsPromise(originCode, maxHops).then(function (result) {
					return filter(result, filterFn);
				});
			};

			// Make token pairs to handle searches like "San Fr"
			var makeTokenPairs = function (tokens) {
				var pairs = [];

				for (var i = 0; i < tokens.length - 1; i++) {
					pairs.push(tokens[i] + ' ' + tokens[i + 1]);  // Pair with space
					pairs.push(tokens[i] + tokens[i + 1]);        // Pair without space
				}

				return tokens.concat(pairs);
			};

			var weightedMatch = function (city, nameOrCode) {
				var sum = 0;

				if (nameOrCode == null) {
					return sum;
				}

				// Perfect match
				if (city.DisplayName === nameOrCode) {
					sum = 100;
					// $log.debug('Perfect', sum, city.DisplayName);
				}

				var concatValue = nameOrCode.split(' ').join('');

				if ((city.Code != null) && (city.Code.toUpperCase() === nameOrCode.toUpperCase())) {
					sum += 1;
					// $log.debug('Code', sum, city.DisplayName);
				}

				if (city.DisplayName != null) {
					var displayNameTokens = makeTokenPairs(city.DisplayName.split(' '));

					angular.forEach(displayNameTokens, function (token) {
						if (token.toLowerCase().indexOf(nameOrCode.toLowerCase()) === 0) {
							sum += 0.3;
							// $log.debug('Token', sum, city.DisplayName);
						} else if (token.toLowerCase().indexOf(concatValue.toLowerCase()) === 0) {
							sum += 0.15;
							// $log.debug('Token', sum, city.DisplayName);
						}
					});
				}

				if (city.LinkedAirportCodes != null) {
					var linkedAirportCodes = city.LinkedAirportCodes.split(',');

					angular.forEach(linkedAirportCodes, function (code) {
						if (code.toUpperCase() === nameOrCode.toUpperCase()) {
							sum += 0.2;
							// $log.debug('Linked', sum, city.DisplayName);
						}
					});
				}

				if (city.SearchTags != null) {
					var searchTagTokens = makeTokenPairs(city.SearchTags.split(' '));

					angular.forEach(searchTagTokens, function (token) {
						if ((token.toLowerCase().indexOf(nameOrCode.toLowerCase()) === 0) || (token.toLowerCase().indexOf(concatValue.toLowerCase()) === 0)) {
							sum += 0.1;
							// $log.debug('Search', sum, city.DisplayName);
						}
					});
				}

				// Only boost weight for HA Cities that aleady match!
				if ((sum > 0) && (city.IsHACity)) {
					sum += 0.5;
					// $log.debug('HA', sum, city.DisplayName);
				}

				city._weight = sum; // jscs:ignore disallowDanglingUnderscores
				// if (sum > 0) {
				//     $log.debug('FINAL', sum, city.DisplayName);
				// }

				return city._weight; // jscs:ignore disallowDanglingUnderscores
			};

			var filterWeightedMatches = function (cityList, nameOrCode) {
				var result = [];

				// var then;
				// if (typeof performance !== 'undefined') {
				//     then = performance.now();
				// }

				for (var i = 0; i < cityList.length; i++) {
					var city = cityList[i];
					if (city != null && weightedMatch(city, nameOrCode) > 0) {
						result.push(city);
					}
				}

				// if (typeof performance !== 'undefined') {
				//     $log.debug('_filterWeightedMatches ['+cityList.length+']:'+
				//         Math.round(1000*(performance.now()-then))+'μs');
				// }

				return result;
			};

			var getMatchingCitiesPromiseExpertBooking = function(nameOrCode, pairCode, filterFn) {
				isExpertBooking = true;
				return getMatchingCitiesPromise(nameOrCode, pairCode, filterFn);
			};

			var getMatchingCitiesPromise = function (nameOrCode, pairCode, filterFn) {
				return getReachableCitiesPromise(pairCode, 1, filterFn).then(function (cityList) {
					return $filter('orderBy')(filterWeightedMatches(cityList, nameOrCode), '-_weight');
				});
			};

			var getMatchingFilteredCities = function (nameOrCode, legIndex, isOrigin) {
				// create a re-callable closure
				var fn = function () {
					return api.getFilteredCityList(legIndex, isOrigin);
				};
				return repromise(fn).then(function (cityList) {
					return $filter('orderBy')(filterWeightedMatches(cityList, nameOrCode), '-_weight');
				});
			};

			var getNitpOriginsPromise = function () {
				return $q.all([getCityListPromise(), getNitpMapPromise()]).then(function (resolved) {
					var list = resolved[0];
					var map = resolved[1];

					var result = [];

					for (var i = 0; i < list.length; i++) {
						var city = list[i];
						if (map.hasOwnProperty(city.Code)) {
							result.push(city);
						}
					}

					return result;
				});
			};

			var getNitpDestinationsPromise = function (cityCode) {
				return $q.all([getCityListPromise(), getNitpMapPromise()]).then(function (resolved) {
					var list = resolved[0];
					var map = resolved[1];

					var result = [];
					var codes = map[cityCode];
					for (var i = 0; i < codes.length; i++) {
						var code = codes[i];
						for (var j = 0; j < list.length; j++) {
							var city = list[j];
							if (city.Code === code) {
								result.push(city);
							}
						}
					}

					return result;
				});
			};

			svc.preloadCitiesExpertBooking = function () {
				isExpertBooking = true;
				svc.preloadCities();
			};

			svc.preloadCities = function() {
				getCityMapPromise();
				getCityPairsPromise();
			};

			svc.isInternational = function (city) {
				return (city != null) && (city.Market === 3);
			};

			svc.pairIsInvalid = function (originCode, destCode) {

				if (!originCode || !destCode) {
					return true;
				}
				var validPairs = getValidPairs(originCode);
				var validPairCodes = [];
				for (var i = 0, len = validPairs.length; i < len; i++) {
					if (validPairs[i] && validPairs[i].Code) {
						validPairCodes.push(validPairs[i].Code);
					}
				}
				if (validPairCodes.indexOf(destCode) < 0) {
					return true;
				}
				return false;
			};

			svc.getAllCities = getCityListPromise;
			svc.getAllCitiesExpertBooking = getCityListPromiseExpertBooking;
			svc.getFilteredCities = getFilteredCityListPromise;
			svc.getReachableCities = getReachableCitiesPromise;
			svc.getCityByCode = getCityByCodePromise;
			svc.getCityByCodeSync = getCityByCodeSync;
			svc.getMatchingCities = getMatchingCitiesPromise;
			svc.getNitpOrigins = getNitpOriginsPromise;
			svc.getNitpDestinations = getNitpDestinationsPromise;
			svc.getMatchingFilteredCities = getMatchingFilteredCities;
			svc.getCityMap = getCityMapPromise;
			svc.getValidPairs = getValidPairs;
			svc.getMatchingCitiesExpertBooking = getMatchingCitiesPromiseExpertBooking;
			svc.getHotelCities = api.getExpediaCityList;
			svc.searchCarLocations = api.searchCarLocations;
			svc.getAllCarLocations = api.getAllCarLocations;
			svc.getCarLocationById = api.getCarLocationById;

			return svc;
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Customer Selections Service
	// --------------------------------------------
	//
	// * **Class:** HaCustomerSelectionService
	// * **Author:** George Pantazis
	//
	// Service for maintaining customer selections model.

	'use strict';

	var mod = angular.module('haCustomerSelectionsService', []);

	mod.service('haCustomerSelections', ['$rootScope', 'haUtils',
		function ($root, haUtils) {

			// Here, you would either restore the $root from localstorage, or
			// sync it with the server via AJAX. For the sake
			// of the front-end demo, we're simply declaring it here.

			var model = {
				'selections': {
					'tripName': 'Default Trip Name',
					'legs': [],
					'passengers': []
				}
			};

			$.extend($root, model);

			$root.$watch('selections', function () {
				service.updateTotal();
			}, true);

			var service = {

				clearLegs: function () {
					$root.selections.legs = [];
				},

				// Prepopulate legs with dates spaced one week apart.
				createLegs: function (howMany, overwrite) {
					for (var i = 0; i < howMany; i++) {
						if (overwrite || (!overwrite && !$root.selections.legs[i])) {

							if ($root.selections.legs[i] === undefined) {
								$root.selections.legs[i] = {};
							}
						}
					}
				},

				getLeg: function (which) {

					if (!haUtils.isNumber(which) || !$root.selections.legs[which]) {
						return false;
					}

					return $root.selections.legs[which];
				},

				setLegDate: function (which, dateObj) {

					var newDateObj = null;
					var newDate = dateObj ? new Date(dateObj.year, dateObj.month - 1, dateObj.day) : new Date(new Date() * 1 + 1000 * 60 * 60 * 24 * 7 * which);
					var now = new Date();
					var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());

					if (dateObj === undefined) {
						if (which > 0) {
							newDateObj = $root.selections.legs[which - 1 * 1].date;
						}

						if (newDateObj === undefined) {
							newDateObj = {
								year: newDate.getFullYear(),
								month: newDate.getMonth() + 1,
								day: newDate.getDate()
							};
						}

					}
					else {

						newDateObj = dateObj;
						var index = 0;
						angular.forEach($root.selections.legs, function (leg) {
							if (leg.date != null) {
								var legDate = new Date(leg.date.year, leg.date.month - 1, leg.date.day);
								if (index < which) {
									if (legDate > newDate) {
										leg.date = newDateObj;
									}
								}
								else if (index > which) {
									if (legDate < newDate) {
										leg.date = newDateObj;
									}
								}
							}
							index++;
						});
					}


					//  nextLeg = $root.selections.legs[which * 1 + 1],
					//  prevLeg = $root.selections.legs[which * 1 - 1];

					//if (nextLeg) {

					//  var nextDate = new Date(nextLeg.date.year, nextLeg.date.month - 1, nextLeg.date.day);

					//  if (newDate > nextDate) {
					//    nextLeg.date = dateObj;
					//  }

					//}

					//if (prevLeg) {

					//  var prevDate = new Date(prevLeg.date.year, prevLeg.date.month - 1, prevLeg.date.day);

					//  if (newDate > prevDate) {
					//    prevLeg.date = dateObj;
					//  }

					//}

					$root.selections.legs[which].date = newDateObj;

					// If the user manually sets the date to before today, go ahead and reset it to today.
					// Remove this conditional if time-travel becomes a possibility in the future.
					if (newDate < today) {
						setTimeout(function () {
							service.setLegDate(which, {
								year: today.getFullYear(),
								month: today.getMonth() + 1,
								day: today.getDate()
							});
						}, 0);
					}

				},

				// As we don't currently have pricing data, this is mocked to a fixed
				// price. It should update the total dynamically as $root.selections changes.
				updateTotal: function () {
					$root.selections.currentTotal = 1234.56;
				},

				updateLegDates: function () {

					if ($root.selections.legs && $root.selections.legs.length > 1) {
						var index = $root.selections.legs.length;
						for (var i = 0; i <= index - 2; i++) {
							var currentLeg = $root.selections.legs[i];
							var nextLeg = $root.selections.legs[i + 1];

							if (currentLeg.date != null && nextLeg.date != null) {
								var currentLegDate = new Date(currentLeg.date.year, currentLeg.date.month - 1, currentLeg.date.day);
								var nextLegDate = new Date(nextLeg.date.year, nextLeg.date.month - 1, nextLeg.date.day);
								if (nextLegDate < currentLegDate) {
									nextLeg.date = currentLeg.date;
								}
							}
						}
					}
				}


			};

			return service;
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haDataCacheService', ['haHttpService'])

	.service('haDataCacheService', [
		'haHttpService',
		'$cacheFactory',

		function (http, $cacheFactory) {
			var haDataCache = $cacheFactory.get('haDataCache');

			return {
				get: function (url) {
					return http.GET(url, {
						cache: haDataCache
					});
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Favorites Service
	// --------------------------------------------
	//
	// * **Class:** haFavoritesService
	// * **Author:** George Pantazis
	//
	// Service for loading the user's favorites. This is distinct from the user
	// object (see: ha-user-service), as favorites may exist for unauthenticated
	// users.

	'use strict';

	var mod = angular.module('haFavoritesService', []);

	mod.service('haFavorites', ['$rootScope',
		function ($rootScope) {

			// Mock model. Replace this with an $http call to retrieve user data
			// during integration with the HA API infrastructure.

			var model = [{
				'title': 'Waimoku Falls Trail',
				'photo': '/Content/assets/common/images/demo-nav-wishlist1.jpg',
				'location': 'Haleakala National Park'
			}];

			var service = {

				// For demonstration, this simply restores a mock set of favorites.
				// In integration, this would write to localstorage and sync with the server
				// via API services (Angular $http).

				updateFavorites: function () {
					$rootScope.favorites = model;
					return service;
				}

			};

			return service;
		}
	]);

})(angular);
;
(function (angular) {

	// Hawaiian Global Utilities
	// =========================
	//
	// * **Class:** haGlobals
	// * **Author:** Nathan Probst
	//
	// A utility class to access global page scope without
	// declaring globals in code.

	'use strict';

	var module = angular.module('haGlobalsModule', []);

	module.factory('haGlobals', ['$window', function ($window) {

		return function (names, cb, requireAll) {
			// NEP: FIXME: Really, we should be stuffing data into a _namespace_.
			names = [].concat(names);
			requireAll = !!requireAll;

			if ((names.length === 0) || (cb == null)) {
				return;
			}

			var globals = [];
			var foundSome = false;
			var foundAll = true;
			angular.forEach(names, function (name) {
				var found = ((typeof $window[name] !== 'undefined') && ($window[name] !== null));
				foundSome = foundSome || found;
				foundAll = foundAll && found;
				globals.push($window[name]);
			});

			// Only callback if global found
			if ((requireAll && foundAll) || (!requireAll && foundSome)) {
				cb.apply(null, globals);
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Help And Tips Service
	// --------------------------------------------
	//
	// * **Class:** HaHelpAndTipsService
	// * **Author:** George Pantazis
	//
	// Service for asynchronously retrieving help and tips data for a given city.

	'use strict';

	var mod = angular.module('haHelpAndTipsService', []);

	mod.service('haHelpAndTips', function () {

		// Mock model. Replace this with an $http call to retrieve API data
		// during integration with the HA API infrastructure.

		var model = {
			'cityName': 'Kahului, Maui',
			'months': [{
				'sunnyPct': '80',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}, {
				'sunnyPct': '90',
				'tempLow': '70F',
				'tempHigh': '84F'
			}]
		};

		var service = {

			// $http request would occur here; dependencies on this service should utilize
			// the `cb` to pass a function, and expect results asynchronously.

			// I would reccommend that some manner of cacheing be employed if the same city is requested;
			// this will enable the FE modules using this service to be highly modular without having
			// to worry about overpinging the server.

			getData: function (city, cb) {

				if (city === 'OGG') {
					model.cityName = 'Kahului, Maui';
				} else if (city === 'HNL') {
					model.cityName = 'Honolulu';
				} else {
					model.cityName = 'Default Data';
				}

				cb(model);
			}

		};

		return service;
	});

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haHttpService', [])
	.factory('requestedWithInterceptor', [
		'$location', '$rootScope', function ($location, $rootScope) {
			return {
				request: function (config) {

					if (!/\/\//.test(config.url)) {
						config.headers['X-Requested-With'] = 'XMLHttpRequest';
						config.headers.csrf = $rootScope.csrf;
					}

					return config;
				},
				response: function (response) {
					if (response && response.data && response.data.ErrorType) {
						if (response.data.ErrorType === 'TabError') {
							//$location.path and $lcoation.url methods are not working in few pages so used native window.lcoation.href
							window.location.href = '/book/error?ErrorType=TabError';
							return;
						} else if (response.data.ErrorType === 'SessionTimeOut') {
							window.location.href = '/book/error?ErrorType=SessionTimeOut';
							return;
						}
					}
					return response;
				}
			};
		}
	])
	.factory('sessionLostInterceptor', ['$q', '$rootScope', function($q, $rootScope){
		return {
			responseError: function(response) {
				if (response && response.status === 401) {
					$rootScope.$broadcast('$sessionLostEvent');
				}
				return $q.reject(response);
			}
		}
	}])
	.config([
		'$httpProvider', function ($httpProvider) {
			$httpProvider.interceptors.push('requestedWithInterceptor');
			$httpProvider.interceptors.push('sessionLostInterceptor');
		}
	])
	.factory('haHttpService', [
		'$log', '$http', '$q', function ($log, $http, $q) {
			return {
				GET: function (url, config) {
					config = config || {};
					if (typeof $log.debug === 'function') {
						$log.debug('GET ' + url);
					}
					return $http.get(url, config)
					.error(function (data, status /*, headers, config*/) {
						$log.warn('[' + status + '] GET ' + url, data);
					});
				},

				GETPARALLEL: function (urls) {
					var deferred = $q.defer();
					var promises = [];

					angular.forEach(urls, function (url) {
						promises.push($http.get(url));
					});

					$q.all(promises).then(
						function (responses) {
							deferred.resolve(responses);
						},
						function () {
							deferred.reject();
						}
					);

					return deferred.promise;
				},

				POST: function (url, data, config) {
					config = config || {};
					if (typeof $log.debug === 'function') {
						$log.debug('POST ' + url);
					}
					return $http.post(url, data, config)
					.error(function (data, status /*, headers, config*/) {
						$log.warn('[' + status + '] POST ' + url, data);
					});
				},

				PUT: function (url, data, config) {
					config = config || {};
					if (typeof $log.debug === 'function') {
						$log.debug('PUT ' + url);
					}
					return $http.put(url, data, config)
					.error(function (data, status /*, headers, config*/) {
						$log.warn('[' + status + '] PUT ' + url, data);
					});
				},

				DELETE: function (url, config) {
					config = config || {};
					if (typeof $log.debug === 'function') {
						$log.debug('DELETE ' + url);
					}
					return $http['delete'](url, config)
					.error(function (data, status /*, headers, config*/) {
						$log.warn('[' + status + '] DELETE ' + url);
					});
				}
			};
		}
	])
	.directive('form', [
		'$rootScope', function ($rootScope) {
			return {
				restrict: 'E',
				link: function ($scope, $el) {
					if ($el.length && $rootScope.csrf) {
						$el.append($('<input type="hidden" name="__RequestVerificationToken" value="' + $('body > [name="__RequestVerificationToken"]').val() + '" />'));
					}
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var mod = angular.module('haModalService', []);

	mod.factory('haModal', ['$document', '$compile', '$rootScope', 'haConfig', '$controller', '$timeout',
		function ($document, $compile, $rootScope, haConfig, $controller, $timeout) {
			var defaults = {
				id: null,
				template: null,
				templateUrl: null,
				title: 'Default Title',
				heading:'Default Heading',
				backdrop: true,
				success: { label: 'OK', fn: null },
				cancel: { label: 'Close', fn: null },
				controller: null, //just like route controller declaration
				backdropClass: 'modal-backdrop',
				defaultContent: null,
				modalClass: 'modal',
				modalLock: false,
				size: '',
				verticalCenter: false,
				mobileVerticalCenter: false
			};

			var body = $document.find('body');

			function dialog(/*optional*/templateUrl, options, passedInLocals) {
				var loadingSpinner = angular.element('<div class="ha-loading-spinner fixed"><div><div></div></div></div>');
				body.append(loadingSpinner);

				var target = $(document.activeElement),
					scrollPos = window.pageYOffset,
					subsequentModalOpened = false,
					closed = false;

				// Handle arguments if optional template isn't provided.
				if (angular.isObject(templateUrl)) {
					passedInLocals = options;
					options = templateUrl;
				} else {
					options.templateUrl = templateUrl;
				}

				options = angular.extend({}, defaults, options); //options defined in constructor

				var idAttr = options.id ? ' id="' + options.id + '" ' : '';

				// Custom Event for Analytics
				document.body.dispatchEvent(new CustomEvent('ModalOpened', { detail: options.id }));
				$rootScope.$broadcast('cancelTooltip');

				var modalBody = (function () {
					if (options.template) {
						if (angular.isString(options.template)) {
							// Simple string template
							return options.template;
						} else {
							// jQuery/JQlite wrapped object
							return options.template.html();
						}
					} else {
						// Template url
						return '<div ng-include="\'' + options.templateUrl + '\'"></div>';
					}
				})();
				//We don't have the scope we're gonna use yet, so just get a compile function for modal

				var modalEl = '';
				var backdropEl = '';
				var modalLock = options.modalLock;

				if (modalLock) {
					modalEl = angular.element(
						'<div class="ha-modal ' + options.modalClass + ' fade"' + idAttr + ' tabindex="0">' +
						'<div class="modalContainer modalLock ' + options.size + '">' +
						modalBody +
						'</div>' +
						'</div>');
					backdropEl = angular.element('<div class="modal-backdrop">');
				} else {
					modalEl = angular.element(
						'<div class="ha-modal ' + options.modalClass + ' fade"' + idAttr + ' tabindex="0">' +
						'<div class="modalContainer ' + options.size + '">' +
						'<a class="close-modal-icon" ng-click="$modalCancel()" href="" role="button"><i class="close ha-icon icon-close"></i><span class="sr-only" scs-text="forms.close"></span></a>' +
						modalBody +
						'</div>' +
						'</div>');
					backdropEl = angular.element('<div class="modal-backdrop" ng-click="$modalCancel($event)">');
				}

				var focusables, inputs;
				var collectFocusables = function () {
					focusables = $(modalEl[0]).find(':focusable');
					inputs = $(modalEl[0]).find('input[type=text], input[type=email], textarea, select');
					if (inputs.length > 0) {
						// do not focus inputs for IE9, bc it suppresses placeholder shim
						if (!$('html').hasClass('lte-ie9')) {
							$(inputs[0]).focus();
						}
					}
					else if (!$rootScope.isMobile) {
						$(focusables[0]).focus();
					}
				};

				var setVerticalCenter = function () {
					var listner = scope.$watch(
						function () {
							return modalEl.find('.modalContainer').height();
						},
						function (newValue, oldValue) {
							if (newValue !== 0) {
								var modalContainer = modalEl.find('.modalContainer');
								var marginTop = (modalContainer.outerHeight() / 2) * -1;
								modalContainer.css({ 'top': '50%', 'margin-top': marginTop });
								listner(); // remove watcher
							}
						}
					);
				};

				var handleTabPressed = function (event) {
					if (event.keyCode === 9) {
						// refresh the focusable list as it might have changed
						focusables = $(modalEl[0]).find(':focusable').get();

						// trap tab key within modal
						var isfirstfocusable = document.activeElement === focusables[0];
						var islastfocusable = document.activeElement === focusables[focusables.length - 1];

						if (event.shiftKey && isfirstfocusable) {
							// if focused on first element and pressing -shifttab, cycle around backwards
							event.preventDefault();
							focusables[focusables.length - 1].focus();
						} else if (!event.shiftKey && islastfocusable) {
							// if focused on last element and pressing tab, cycle around
							event.preventDefault();
							focusables[0].focus();
						}
					}
				};

				var handleEscPressed = function (event) {
					if (event.keyCode === 27 && !modalLock) {
						$(document.activeElement).blur();
						scope.$modalCancel(event);
					}
				};

				var closeFn = function () {
					// debounce closeFn
					if (closed) {
						return;
					} else {
						closed = true;
					}
					body.unbind('keydown', handleEscPressed);
					body.unbind('keydown', handleTabPressed);
					backdropEl.removeClass('fade in');

					//loadingSpinner.remove();
					modalEl.removeClass('in');
					$timeout(function () {
						modalEl.remove();
						body.removeClass('modal-active');

						// if we have multiple modals, ensure modalactive remains true until they are all closed
						$rootScope.modalsopen--;
						if ($rootScope.modalsopen == 0) {
							$rootScope.modalactive = false;
						}

						if ($rootScope.isMobile && !subsequentModalOpened) {
							var bodyMargin = parseFloat($('body').css('margin-top'));
							scrollPos = bodyMargin * -1;
							// reset margin
							$('body').css({ 'margin-top': 0 });
							window.scrollTo(0, scrollPos);
						}
						if (options.backdrop) {
							backdropEl.remove();
						}
					}, 300);
				};

				body.bind('keydown', handleEscPressed);
				body.bind('keydown', handleTabPressed);

				var ctrl;
				var locals;
				var scope = options.scope || $rootScope.$new();

				if (options.extendScope) {
					angular.extend(scope, options.extendScope);
				}

				if (!$rootScope.modalsopen) {
					$rootScope.modalsopen = 1;
				} else {
					$rootScope.modalsopen++;
				}
				$rootScope.modalactive = true;
				scope.$defaultContent = options.defaultContent;
				scope.$title = options.title;
				scope.$heading = options.heading;
				scope.$modalClose = closeFn;				

				// listen for broadcasted close event
				scope.$on('$modalCancel', function (event, idString) {
					if (idString && idString.length) {
						if (idString === options.id) {
							scope.$modalClose();
						}
					}
					else {
						scope.$modalClose();
					}
				});
				// for handling subsequent modals on mobile
				scope.$on('$modalOpened', function (event, idString) {
					if (idString && idString.length && idString !== options.id) {
						subsequentModalOpened = true;
					}
				});

				scope.$modalCancel = function ($event) {
					var modalcontainer = modalEl.find('.modalContainer');
					if ($event && $event.type === 'click' && (!$.contains(modalcontainer[0], $event.target) || modalcontainer.is($event.target) || !$.contains(document, $event.target))) {
						return;
					}
					var callFn = options.cancel.fn || closeFn;

					scope.$modalClose();
					$timeout(function () {
						scope.$emit('haModalClosed', options.id);
						if (!$rootScope.isMobile && target && target[0] && target[0].tagName !== 'g') {
							// set back to the element which triggered the modal
							target.focus();
							//document.body.scrollTop = scrollPos;
						}
					}, 300);

					callFn.call(this);
				};				
				scope.$modalSuccess = function () {
					var callFn = options.success.fn || closeFn;
					callFn.call(this);
					scope.$modalClose();
				};
				scope.$modalSuccessLabel = options.success.label;
				scope.$modalCancelLabel = options.cancel.label;

				if (options.controller) {
					locals = angular.extend({ $scope: scope }, passedInLocals);
					ctrl = $controller(options.controller, locals);
					// Yes, ngControllerController is not a typo
					modalEl.contents().data('$ngControllerController', ctrl);
				}

				$rootScope.$on('closeModal', function () {
					scope.$modalCancel();
				});

				$compile(modalEl)(scope);
				$compile(backdropEl)(scope);
				body.append(modalEl);
				body.addClass('modal-active');

				if ($rootScope.isMobile) {
					// allows body to remain fixed to prevent scrolling of content underneath modal,
					// while maintaining scroll position on page.
					var bodyMargin = parseFloat($('body').css('margin-top'));
					if (bodyMargin >= 0) {
						$('body').css({ 'margin-top': scrollPos * -1 });
					}
					// auto-pad .modal-main depending on height of .modal-header
					if (modalEl.find('.modal-header').length) {
						var headerHeight = modalEl.find('.modal-header').outerHeight();
						modalEl.find('.modal-main').css({ paddingTop: headerHeight });
					}
				}
				$rootScope.$broadcast('$modalOpened', options.id);

				if (options.backdrop) {
					body.append(backdropEl);
					$timeout(function () {
						backdropEl.addClass('fade in');
					}, 0);
				}

				// click outside to close
				// do not use ng-click on .ha-modal because it breaks modals on iPad
				$('.ha-modal').on('click', function (e) {
					e.stopPropagation();
					if ($(e.target).hasClass('ha-modal') && !options.modalLock) {
						scope.$modalCancel();
					}
				});

				$timeout(function () {
					modalEl.addClass('in');
					loadingSpinner.addClass('ng-hide');

					collectFocusables();
					if (options.callback) {
						options.callback(modalEl);
					}

					if (!$rootScope.isMobile && options.verticalCenter) {
						setVerticalCenter();
					}
					if ($rootScope.isMobile && options.mobileVerticalCenter) {
						modalEl.addClass('mobile-vertical-center');
					}
				}, 100);

				return scope;
			}

			dialog.openPriceCalendar = function (legs, adults, children) {
				var options = {
					id: 'FlexiblePriceView',
					backdrop: 'true',
					extendScope: {
						legs: legs,
						adults: adults || 1,
						children: children || 0
					}
				};
				dialog(haConfig.getTemplateUrl('/Book/FlightSearch/ha-flexible-price-modal.html'), options);
			};

			window.haModal = dialog;
			return dialog;
		}]);
})(angular);
;
(function (angular) {

	// Ha Seat Map Service
	// --------------------------------------------
	//
	// * **Class:** haPassengersService
	// * **Author:** Josh Nielsen & Nathan Probst
	//
	// Service for managing passengers


	'use strict';

	var mod = angular.module('haPassengersService', ['haPassengersAPI']);

	mod.service('haPassengersService', ['haPassengersAPI', function (haPassengersAPI) {

		var service = {

			passengers: [],

			types: [
				'Adult',
				'Child',
                'Infant' //adding
			],

			// Delegate to haPassengersAPI...
			addeditpax: haPassengersAPI.addeditpax,
			editPaxLink: haPassengersAPI.editPaxLink,
			editLoggedInPax: haPassengersAPI.editLoggedInPax,
			addPaxToPNR: haPassengersAPI.addPaxToPNR,
			addExtraLoggedTravellerInfo: haPassengersAPI.addExtraLoggedTravellerInfo,
			associateinfants: haPassengersAPI.associateinfants,
			updatecontactinfo: haPassengersAPI.updatecontactinfo,

			isAdultInFlight: function (travelers, departDate) {
				if (travelers && travelers.length > 0) {
					var adults = $.grep(travelers, function (t) {
						return moment(departDate, 'MM-DD-YYYY').diff(moment(t.DOBMonth + '/' + t.DOBDay + '/' + t.DOBYear, 'MM-DD-YYYY'), 'years') >= 15;
					});

					var minorAdults = $.grep(travelers, function (t) {
						var age = moment(departDate, 'MM-DD-YYYY').diff(moment(t.DOBMonth + '/' + t.DOBDay + '/' + t.DOBYear, 'MM-DD-YYYY'), 'years');
						return age >= 12 && age < 15;
					});

					var children = $.grep(travelers, function (t) {
						var age = moment(departDate, 'MM-DD-YYYY').diff(moment(t.DOBMonth + '/' + t.DOBDay + '/' + t.DOBYear, 'MM-DD-YYYY'), 'years');
						return age >= 2 && age < 12;
					});

					//If you are less than 15 but still a minor adult, you can't fly with a child
					if (adults.length === 0 && children.length === 0) {
						return minorAdults.length > 0;
					}
					return adults.length > 0;
				}
				return false;
			},

			num: function (type) {
				if (type) {
					var num = 0;
					angular.forEach(this.passengers, function (pass) {
						if (pass.type === type) {
							num++;
						}
					});

					return num;
				}

				return this.passengers.length;
			},

			add: function (passengers) {
				if (typeof passengers === 'object' && passengers.length) { // if is array
					this.passengers = this.passengers.concat(passengers);
					return this.passengers;
				}

				return this.passengers.push(passengers);
			},

			uniquePaxTypes: function () {

				var uniqueType = [];
				angular.forEach(this.passengers, function (pax) {
					uniqueType.push(pax.type);
				});
				return $.unique(uniqueType);
			},

			remove: function (query) {
				var self = this;
				angular.forEach(this.passengers, function (pass, i) {
					for (var key in query) {
						if (query.hasOwnProperty(key)) {
							if (pass[key] === query[key]) {
								self.passengers.splice(i, 1);
							}
						}
					}
				});
				return this.passengers;
			},

			get: function (query) {
				angular.forEach(this.passengers, function (pass) {
					for (var key in query) {
						if (query.hasOwnProperty(key)) {
							if (pass[key] === query[key]) {
								return pass;
							}
						}
					}
				});
			}
		};

		return service;
	}]);

})(angular);
;
(function (angular) {

	// Ha Search Cache Service
	// --------------------------------------------
	//
	// * **Class:** HaSearchCacheService
	// * **Author:** George Pantazis
	//
	// Service for maintaining customer recent search model.

	'use strict';

	var mod = angular.module('haSearchCacheService', []);

	mod.service('haSearchCache', ['$rootScope',
		function ($rootScope) {

			// Here, you would either restore from localstorage, or
			// sync it with the server via AJAX. For the sake
			// of the front-end demo, we're simply declaring it here.

			// Once I've assembled the components into a booking module, I'll
			// hook this up to localStorage and remove this fake model.

			var model = {
				'searchCache': [{
					'tripName': 'Trip To Honolulu',
					'legs': [{
						'origin': 'SFO',
						'destination': 'HNL',
						'date': {
							'year': 2013,
							'month': 9,
							'day': 10
						},
						'time': {
							'start': 900,
							'end': 2100
						}
					}]
				}, {
					'tripName': 'Trip To Honolulu',
					'legs': [{
						'origin': 'SFO',
						'destination': 'HNL',
						'date': {
							'year': 2013,
							'month': 9,
							'day': 10
						},
						'time': {
							'start': 900,
							'end': 2100
						}
					}, {
						'origin': 'HNL',
						'destination': 'SFO',
						'date': {
							'year': 2013,
							'month': 9,
							'day': 20
						},
						'time': {
							'start': 900,
							'end': 2100
						}
					}]
				}, {
					'tripName': 'Trip To Hawaii',
					'legs': [{
						'origin': 'SFO',
						'destination': 'HNL',
						'date': {
							'year': 2013,
							'month': 9,
							'day': 10
						},
						'time': {
							'start': 900,
							'end': 2100
						}
					}, {
						'origin': 'HNL',
						'destination': 'OGG',
						'date': {
							'year': 2013,
							'month': 9,
							'day': 15
						},
						'time': {
							'start': 900,
							'end': 2100
						}
					}, {
						'origin': 'OGG',
						'destination': 'SFO',
						'date': {
							'year': 2013,
							'month': 9,
							'day': 20
						},
						'time': {
							'start': 900,
							'end': 2100
						}
					}]
				}]
			};

			$.extend($rootScope, model);

			var service = {

				cacheCurrentSelections: function () {
					$rootScope.searchCache.unshift($rootScope.selections);
				},

				restoreSearch: function (which) {
					if ($rootScope.searchCache[which]) {
						$rootScope.selections = $.extend(true, {}, $rootScope.searchCache[which]);
					}
				}

			};

			return service;
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Segment Service
	// --------------------------------------------
	//
	// * **Class:** haSeatClassService
	// * **Author:** Josh Nielsen
	//
	// Service for flight segment functions


	'use strict';

	var mod = angular.module('haSegmentService', []);

	mod.service('haSegmentService', ['haPassengersService', '$log',
		function ($pax, $log) {

			var service = {

				selectedSegments: [],

				legs: [],

				numFares: function (segment, paxType) {
					var fares = {
						Adult: 0,
						Child: 0,
						Infant: 0
					};

					var relevantTypes = this.getRelevantPaxTypes();
					if ($pax.passengers != null) {
						angular.forEach($pax.passengers, function (pax) {
							if ((pax.Type === 'Child') && (relevantTypes.indexOf(pax.Type) < 0)) {
								fares.Adult++;
							} else {
								fares[pax.Type]++;
							}
						});
					}

					return fares[paxType];
				},

				getRelevantPaxTypes: function (segment) {
					var relevantTypes = [];
					var self = this;

					// return relevants types for a single segment if passes as argument, otherwise return for all segments
					var segments = segment ? [segment] : this.selectedSegments;

					// for each passenger type check to see if there are corresponding fares available in segments
					angular.forEach($pax.getNonInfants(), function (pax) {
						angular.forEach(segments, function (segment) {
							if (self.getFare(segment, pax.Type, true)) {
								if (relevantTypes.indexOf(pax.Type) === -1) {
									relevantTypes.push(pax.Type);
								}
							}
						});
					});

					return relevantTypes;
				},

				getFare: function (segment, passengerType, noCoerce) {
					var fare;
					var self = this;

					// use selectedSeatClass if set otherwise default to first seat class in list
					if (segment != null) {
						var fareClass = this.getFareClass(segment);
						if (segment.BookingFares != null) {
							angular.forEach(segment.BookingFares, function (bookingFare) {
								if (bookingFare.Name.toLowerCase() === fareClass) {
									// try for fareType based on passengerType but fall back to default (Adult) if undefined
									var origFareType = bookingFare.FareTypes[passengerType];

									if (noCoerce) {
										fare = origFareType;
									} else {
										fare = self.getClosestFare(passengerType, bookingFare.FareTypes);
									}
								}
							});
						}
					} else {
						$log.warn('getFare() called with null "segment"');
					}
					return fare;
				},

				getClosestFare: function (passengerType, fares) {
					var paxTypeIdx = $pax.types.indexOf(passengerType);
					for (var i = paxTypeIdx; i >= 0; i--) {
						var fare = fares[$pax.types[i]];
						if (fare) {
							return fare;
						}
					}
				},

				getFareClass: function (segment) {
					// use selected seat class if selection has been made, otherwise default to first (0) seat class
					var fareClass;
					if (segment != null) {
						if ((segment.BookingFares != null) &&
							(segment.BookingFares[0] != null) &&
							(typeof segment.BookingFares[0].Name === 'string')) {
							fareClass = segment.BookingFares[0].Name.toLowerCase();
						}
					} else {
						$log.warn('getFareClass() called with null "segment"');
					}
					var seatClass = segment.selectedSeatClass || fareClass;
					return seatClass;
				},

				getExtraComfortPrice: function (segment) {
					var price = 0;
					if (this.Legs != null) {
						angular.forEach(segment.Legs, function (leg) {
							price += this.getLegExtraComfortPrice(leg);
						});
					}

					return price;
				},

				getLegExtraComfortPrice: function (leg) {
					var price = 0;
					if (leg.SeatSelections != null) {
						angular.forEach(leg.SeatSelections, function (selection) {
							price += selection.price;
						});
					}

					return price;
				}
			};

			return service;
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haTemplateCache', ['haHttpService'])

	.service('haTemplateCache', [
		'haHttpService',
		'$templateCache',

		function (http, $templateCache) {
			return {
				get: function (url) {
					return http.GET(url, {
						cache: $templateCache
					});
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Login Service
	// --------------------------------------------
	//
	// * **Class:** haUserService
	// * **Author:** George Pantazis
	//
	// Service for loading the logged-in user's state, or logging the user out.

	'use strict';

	var mod = angular.module('haUserService', []);

	mod.service('haUser', ['$rootScope',
		function ($rootScope) {

			// Mock model. Replace this with an $http call to retrieve user data
			// during integration with the HA API infrastructure.

			var model = {
				'firstName': 'John',
				'lastName': 'Doe',
				'photo': '/Content/assets/common/images/demo-nav-account-pic-icon.jpg',
				'haMiles': '14552',
				'upcomingTrips': [{
					'departTime': 1386375432426,
					'arriveTime': 1386375432426,
					'flights': [123, 456, 789],
					'onTime': true
				}, {
					'departTime': 1386375432426,
					'arriveTime': 1386375432426,
					'flights': [123, 456, 789],
					'onTime': true
				}],
				'savedTrips': [{
					'tripName': 'Trip to Maui',
					'tripType': 'Package',
					'tripLink': '#',
					// If flight price has updated since the last visit.
					'newPrice': false,
					'savedDate': 1386375432426,
					'departDate': 1386375432426,
					'returnDate': 1386375432426,
					'otherPassengers': [
						'Charlie Becket',
						'Mako Becket',
						'Brad Becket'
					]
				}, {
					'tripName': 'Trip to Honolulu',
					'tripType': 'Package',
					'tripLink': '#',
					// If flight price has updated since the last visit.
					'newPrice': true,
					'savedDate': 1386375432426,
					'departDate': 1386375432426,
					'returnDate': 1386375432426,
					'otherPassengers': [
						'Charlie Becket'
					]
				}, {
					'tripName': 'Trip to Big Island',
					'tripType': 'Package',
					'tripLink': '#',
					// If flight price has updated since the last visit.
					'newPrice': true,
					'savedDate': 1386375432426,
					'departDate': 1386375432426,
					'returnDate': 1386375432426,
					'otherPassengers': [
						'Charlie Becket',
						'Brad Becket'
					]
				}],
				'eCerts': [{
					'title': '30% Off',
					'subtitle': 'Mainland to Hawaii Islands',
					'travelStart': 1386375432426,
					'travelEnd': 1386375432426,
					'bookBy': 1386375432426
				}]
			};

			var service = {

				// For demonstration, this simply restores a mock user. In integration this would primarily
				// be used for updating the $root.user object after a successful login $http request.

				updateUser: function () {
					$rootScope.user = model;
					return service;
				},

				// For demonstration, this simply removes the $root.user object. In integration,
				// this should hit a logout REST service and only on logout success clear the user object.

				logout: function () {
					delete $rootScope.user;
					return service;
				}

			};

			// This should call on instantiation, in order to load the current user into,
			// Angular's $rootScope, if one exists.

			service.updateUser();

			return service;
		}
	]);

})(angular);
;
(function (angular) {

    // Hawaiian Global Utilities
    // =========================
    //
    // * **Class:** haUtils
    //
    // A global store for common, reusable JS methods.

    'use strict';

    //////////////////////////
    // PROTOTYPE AUGMENTATIONS
    //////////////////////////

    // STRING
    if (!String.prototype.format) {
        String.prototype.format = function () {
            var args = arguments;
            return this.replace(/{(\d+)}/g, function (match, number) {
                return typeof args[number] !== 'undefined' ? args[number] : match;
            });
        };
    }


    var module = angular.module('haUtilsModule', ['haViewModelModule']);

    module.factory('haUtils', [
		'$rootScope',
		'$log',
		'$window',
		'$locale',		
		'haViewModelSvc',
		'haConfig',
		function ($rootScope, $log, $window, $locale, haViewModelSvc, haConfig) {

		    var svc = {
		        // SEE: http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
		        debounce: function (func, threshold, execAsap) {
		            var timeout;

		            return function debounced() {
		                var self = this;
		                var args = arguments;

		                function delayed() {
		                    if (!execAsap) {
		                        func.apply(self, args);
		                    }
		                    timeout = null;
		                }

		                if (timeout) {
		                    clearTimeout(timeout);
		                } else if (execAsap) {
		                    func.apply(self, args);
		                }

		                timeout = setTimeout(delayed, threshold || 100);
		            };
				},

		        safeApply: function ($scope, fn) {
		            if ((typeof $scope.$id !== 'number') && (typeof fn === 'function')) {
		                throw new Error('safeApply expects a scope and a function.');
		            }

		            var phase = $rootScope.$$phase;
		            if (phase === '$apply' || phase === '$digest') {
		                fn();
		            } else {
		                $scope.$apply(fn);
		            }
		        },

		        closestInArray: function (arr, num) {

		            var closest;
		            var closestDiff = Infinity;

		            for (var i in arr) {

		                var diff = Math.abs(arr[i] - num);

		                if (Math.abs(arr[i] - num) < closestDiff) {
		                    closestDiff = diff;
		                    closest = arr[i];
		                }
		            }

		            return closest;
		        },

		        getChildrenPositions: function ($el, $children) {

		            var bounds = $el.get(0).getBoundingClientRect().left;
		            var positions = [];

		            $children.each(function () {
		                positions.push(this.getBoundingClientRect().left - bounds);
		            });

		            return positions;
		        },

		        indexOf: function (array, obj) {
		        	if (!array || !obj) {
		        		return -1;
		        	}

		        	for (var i = 0; i < array.length; i++) {
		        		if (array[i] === obj) {
		        			return i;
		        		}
		        	}
		        	return -1;
		        },

		        inIframe: function () {
					try {
						return $window.self !== $window.top;
					} catch (e) {
						return true;
					}
				},

		        isNumber: function (n) {
					return !isNaN(parseFloat(n)) && isFinite(n);
		        },

		        leftPad: function (n, targetLength) {
		            var output = n + '';
		            while (output.length < targetLength) {
		                output = '0' + output;
		            }
		            return output;
		        },

		        objRef: function (obj, str) {
		            str = str.split('.');
		            for (var i = 0; i < str.length; i++) {
		                if (!obj || obj[str[i]] === undefined) {
		                    return undefined;
		                }
		                obj = obj[str[i]];
		            }
		            return obj;
		        },

		        objSet: function (obj, str, val) {
		            str = str.split('.');
		            while (str.length > 1) {
		                obj = obj[str.shift()];
		            }
		            obj[str.shift()] = val;
		            return val;
		        },

		        readCookie: function (name) {
		            name += '=';
		            for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--) {
		                if (!ca[i].indexOf(name)) {
		                    return ca[i].replace(name, '');
		                }
		            }
		        },

		        getFlightQueryModelCookie: function () {
		            var checkCookie = this.readCookie('FlightQueryModelCookie2');
		            if (checkCookie !== undefined) {
		                $log.debug('FlightQueryModelCookie2: Found');
		                checkCookie = decodeURIComponent(checkCookie);
		                var fsModel = parseFSModel(checkCookie);
		                if (fsModel) {
		                    return fsModel;
		                }
		            }
		            $log.debug('FlightQueryModelCookie2: null');
		            return null;
		        },

		        getFlightQueryModelRecentCookie: function () {
		            var checkCookie = this.readCookie('FlightQueryModelRecentCookie2');
		            if (checkCookie !== undefined) {
		                $log.debug('getFlightQueryModelCookie2: Found');
		                checkCookie = decodeURIComponent(checkCookie);
		                var cookieList = [];
		                var recentList = checkCookie.split(',');
		                angular.forEach(recentList, function (item) {
		                    var fsModel = parseFSModel(item);
		                    if (fsModel) {
		                        cookieList.push(fsModel);
		                    }
		                });
		                if (cookieList.length > 0) {
		                    return cookieList;
		                }
		            }
		            $log.debug('getFlightQueryModelCookie2: null');
		            return null;
		        },

		        getReshopFlightQueryModelCookie: function () {
		            var checkCookie = this.readCookie('ReshopFlightQueryModelCookie');
		            if (checkCookie !== undefined) {
		                checkCookie = decodeURIComponent(checkCookie);
		                return JSON.parse(checkCookie);
		            }
		            return null;
		        },

		        isEN: function () {
		            var $body = angular.element('body');

		            return $body.hasClass('en') || $body.hasClass('en-au') || $body.hasClass('en-nz') || $body.hasClass('en-us');
		        },

		        isJP: function () {
		            return angular.element('body').hasClass('ja-jp');
		        },

		        isKR: function () {
		            return angular.element('body').hasClass('ko-kr');
		        },

		        isCN: function () {
		            return angular.element('body').hasClass('zh-cn');
		        },

		        isTW: function () {
		            return angular.element('body').hasClass('zh-tw');
		        },

		        ensureRootScope: function () {
		            return $rootScope.HA || ($rootScope.HA = {});
		        },

		        ensureScope: function (memberName, scope) {
		            return scope[memberName] || (scope[memberName] = {});
		        },

		        isLocalDev: function () {
		            return ($window.location.hostname.indexOf('local') === 0);
		        },

		        // Use ng-model="scope.path" on a partial to attach it to the containing scope correctly.
		        attachNgModelAttrToScopeAsVM: function ($scope, $attrs, initFn) {
		            if ($attrs.ngModel != null) {
		                $attrs.$observe('ngModel', function (value) {
		                    if (value != null) {
		                        $scope.$watch(value, function (model) {
		                            if (model != null) {
		                                $scope.VM = model;
		                                if (initFn != null) {
		                                    initFn($scope.VM);
		                                }
		                            }
		                        });
		                    }
		                });
		            }
		        },

		        // Use ha-view-model="vmName" to inject the server's ViewModel into scope as 'VM' (by convention).
		        // Respect and use any inherited $scope.VM
		        attachViewModelToScopeAsVM: function ($scope, vmName, initFn) {
		            return haViewModelSvc.get(vmName).then(function (VM) {
		                if ($scope.VM == null) {
		                    $scope.VM = VM;
		                }
		                if ($scope.VM != null && initFn != null) {
		                    initFn($scope.VM);
		                }
		            });
		        },

		        attachPartialVM: function ($scope, $attrs, vmName, defaultModel, initFn) {
		            // If we have ngModel="VM.Something", then we should use that as the local VM.
		            if ($attrs.ngModel != null) {
		                svc.attachNgModelAttrToScopeAsVM($scope, $attrs, initFn);
		            }
		                // If we have a specified vmName, the we should use the data from ha-view-model="vmName".
		            else if (vmName != null) {
		                svc.attachViewModelToScopeAsVM($scope, vmName, initFn);
		            }
		                // Else we should use the controller-provided defaultModel to avoid runtime error.
		            else {
		                $scope.VM = defaultModel || {};
		                if (initFn != null) {
		                    initFn($scope.VM);
		                }
		            }
		        },

		        // SC Strings look like this:
		        // "<image mediapath="/Images/Brand/Airplanes/Airbus-A330/Interior/Front Cabin/a330-frontcabin-fltattendants-300x170" mediaid="{469511C8-454F-4188-B645-FD5BF91E4E61}" src="~/media/469511C8454F4188B645FD5BF91E4E61.ashx?20140731T0203257678" />"
		        // And are resolvable like this:
		        // /~/media/Images/Brand/Airplanes/Airbus-A330/Interior/Front Cabin/a330-frontcabin-fltattendants-300x170.ashx
		        // If mediapath is unavailble, use src
		        getImageFromSiteCoreString: function (scString) {
		            var $image = $(scString);
		            var mp = $image.attr('mediapath');
		            if (mp) {
		            	return [HA.cdn, '/~/media', mp, '.ashx'].join('');
		            }

		            var src = $image.attr('src');
		            if (src) {
		            	return [HA.cdn, '/', src].join('');
		            }
		        },

		        getImageObjectFromSiteCoreString: function (scString) {
		            var $image = $(scString);
		            var mp = $image.attr('mediapath');
		            var alt = $image.attr('alt');
		            var width = $image.attr('width');
		            var height = $image.attr('height');

		            return {
		                src: [HA.cdn, '/~/media', mp, '.ashx'].join(''),
		                alt: alt,
		                width: width,
		                height: height
		            };
		        },

		        webtrends: {
		            token: function (context) {
		                var pageName = $window.$pageName.replace(' ', '');
		                var country = $window.$langCode.substr(-2);
		                return ['HAWAIIANAIR', country, context, pageName].join('_');
		            },
		            tokenV2: function(context) {
		                var country = $window.$langCode.substr(-2);
		                return ['HAWAIIAN-', country, '.TPS.BRAND.',context].join('');

		            }
		        },

		        parseLegs: function (legs) {
		            return legs && legs.split(',').map(function (leg) {
		                var m = /([A-Za-z]{3})-?([A-Za-z]{3})?[ +-]?(\d\d\d\d-\d\d-\d\d)?/.exec(leg);
		                if (!m) {
		                    return;
		                }

		                return {
		                    origin: { code: m[1].toUpperCase() },
		                    destination: { code: m[2].toUpperCase() },
		                    departDate: m[3] && moment(m[3], 'YYYY-MM-DD').toDate()
		                };
		            });
				},

				splitUrl: function (urlString) {
					var urlInfo = {
						url: urlString,
						params: []
					};

					if (urlString) {
						var urlSplit = urlString.split('?');
						if (urlSplit.length > 1) {
							urlInfo = {
								url: urlSplit[0],
								params: urlSplit[1].split('&')
							};
						}
					}

					return urlInfo;
				},

		        querystring: function (name) {
		            if (!urlParams) {
		                parseQS();
		            }
		            return urlParams[name];
		        },

		        createQueryString: function (object, carRentalObj, urlParams) {
		            var params = [];
		            angular.forEach(object, function (val, key) {
		                params.push(key + '=' + encodeURI(val));
					});

					if (carRentalObj) {
						var discountQuery = encodeURI(this.createCarRentalQuery(carRentalObj));

						if (discountQuery) {
							params.push('supplierBenefits=' + discountQuery);
						}
					}

					if (urlParams) {
						for (var i = 0; i < urlParams.length; i++) {
							params.push(urlParams[i]);
						}
					}
		            return '?' + params.join('&');
				},

				createCarRentalQuery: function (carRentalObj) {
					var carRentalParams = [];
					angular.forEach(carRentalObj, function (discountCodes, companyCode) {
						angular.forEach(discountCodes, function (discountValue, discountType) {
							if (discountValue) {
								carRentalParams.push(companyCode + "|" + discountType + "|" + discountValue);
							}
						});
					});
					return carRentalParams.join(",");
				},

				// Use language to get locale for ABG query string
				getLocale: function(language) {
					var locale = '';
					// language = en, en-au, en-nz, ja-jp, ko-kr, zh-cn, zh-tw
					switch (language) {
						case 'ja-jp':
							locale = 'jp';
							break;
						case 'zh-cn':
							locale = 'zh';
							break;
						case 'zh-tw':
							locale = 'zh_hk';
							break;
						case 'ko-kr':
							locale = 'ko';
							break;
						default:
							locale = 'en';
							break;
					}
					return locale;
				},


		        localStorageSet: function (key, object) {
		            if ($window.localStorage) {
		                $window.localStorage.setItem(key, object);
		            }
		        },

		        localStorageGet: function (key) {
		            var saved;
		            if ($window.localStorage) {
		                saved = $window.localStorage[key];
		            }

		            if (saved) {
		                return saved;
		            } else {
		                return null;
		            }
		        },

		        injectScriptDependency: function (parentElem, scriptLink) {
		            var scriptTag = angular.element(document.createElement('script'));
		            scriptTag.attr('charset', 'utf-8');
		            scriptTag.attr('src', scriptLink);
		            parentElem.prepend(scriptTag);
		        },

		        /* changes "{0} membership will start on {1} and expire on {2}."
						 to "Jamie's membership will start on 11/27/14 and expire on 11/27/15"
						 */
		        formatDynamicString: function (string, replacementArray) {
		            if (string) {
		                return string.replace(/({\d})/g, function (j) {
		                    if (j) {
		                        return replacementArray[j.replace(/{/, '').replace(/}/, '')];
		                    }
		                });
		            }
		        },

		        checkForInfant: function (vm) {
					var hasInfant = false;
					for (var x = 0; x < vm.Travellers.length; x++) {
						if (vm.Travellers[x].Type == 'INF') {
							hasInfant = true;
						}
					}
					return hasInfant;
				},

				checkIfTicketedInfant: function (vm) {
					var isTicketed = false;
					for (var x = 0; x < vm.Travellers.length; x++) {
						for (var y = 0; y < vm.Tickets.length; y++) {
							if (vm.Travellers[x].Type == 'INF' && (vm.Travellers[x].DisplayName === vm.Tickets[y].TravellerName) && vm.Tickets[y].ETicket.length > 0) {
								isTicketed = true;
							}
						}
					}
					return isTicketed;
				},

				removeDuplicatesFromArray: function (arrayToCheck, stringPropertyToCompare) {
					return arrayToCheck.filter(
						function (a) { return !this[a[stringPropertyToCompare]] ? this[a[stringPropertyToCompare]] = true : false; }, {}
					);
				},

				getStandardLocale: function () {
					var localeSplit = $locale.id.split('-');
					localeSplit[1] = !!localeSplit[1] ? localeSplit[1].toUpperCase() : '';
					return localeSplit.join('_');
				},

				bccCapture: function (campaignId, cellNumber, referrerId) {
					try {
						$rootScope.isTargetBcusEligible();
						var url = window.location.href;
						var email = "";
						var firstName = "";
						var lastName = "";
						var dayPhone = "";
						var evePhone = "";
						var haMilesNumber = "";
						var paymentFormIPS = 1;//if user is logged-in and if it's payment page then don't trigger post message "barclaysRequest"
console.log('returl', url);
						if (url.toLowerCase().indexOf("/book/pax/index#/passenger/1") !== -1) {
							if ($("input[name=firstname]").length) firstName = $("input[name=firstname]").val();
							if ($("input[name=lastname]").length) lastName = $("input[name=lastname]").val();

							if ($("input[name='Member1']:checked").val()) {
								if ($("input[name=partnerairline]").length) {
									var partner = $("input[name=partnerairline]").val();
									if (partner.indexOf("HA") !== -1) {
										if ($("input[name=hmnumber]").length) {
											haMilesNumber = $("input[name=hmnumber]").val();
										}
									}
								}
							} else {
								if ($('input[name=email]').length) email = $("input[name=email]").val();
							}
						} else if (url.toLowerCase().indexOf("/book/pax/index#/contact") !== -1) {
							if ($("input[name=email]").length) email = $("input[name=email]").val();
							if ($("input[name=PhoneNum0]").length) dayPhone = $("input[name=PhoneNum0]").val();
							if ($("input[name=PhoneNum1]").length) evePhone = $("input[name=PhoneNum1]").val();
						}

						var json = {
							"cellNumber": cellNumber,
							"campaignId": campaignId,
							"referrerId": referrerId,
							"returl": url,
							"first": firstName,
							"last": lastName,
							"email": email,
							"dayPhone": dayPhone,
							"evePhone": evePhone,
							"HaMilesNumber": haMilesNumber
						};
						if (window.location.hostname.indexOf('webview') !== -1) {
							Promise.all([
								$scs.get('BarclaysPopupContent.Heading'),
								$scs.get('BarclaysPopupContent.Content'),
								$scs.get('BarclaysPopupContent.Title'),
								$scs.get('BarclaysPopupContent.OkayButtonText'),
								$scs.get('BarclaysPopupContent.CancelButtonText')
							])
								.then(function (strings) {
									haModal(haConfig.getTemplateUrl('ha-nma-modal.html'), {
										id: "barclays-modal",
										backdrop: 'true',
										heading: strings[0],
										title: strings[2],
										success: {
											label: strings[3], fn:
												function () {
													$.when(bccTargetApply()).done(function (cc) {
														// if on payment page, form data is in an iframe, so we'll utilize existing code
														if (url.toLowerCase().indexOf("/book/payment") !== -1 && paymentFormIPS == 1) {
															sessionStorage.setItem("referrerId", referrerId);
															var iframe = document.getElementById("formIFrame");
															iframe.contentWindow.postMessage("barclaysRequest", iframeOrigin);
															$rootScope.isTargetBcusEligible();
														}
													});
												}
										},
										cancel: {
											label: strings[4]
										},
										size: 'modal-md',
										defaultContent: strings[1]
									});
								});
						}
						else
						{
						    $.when(bccTargetApply()).done(function (cc) {
						        // if on payment page, form data is in an iframe, so we'll utilize existing code
								if (url.toLowerCase().indexOf("/book/payment") !== -1 && paymentFormIPS == 1) {
						            sessionStorage.setItem("referrerId", referrerId);
						            var iframe = document.getElementById("formIFrame");
						            iframe.contentWindow.postMessage("barclaysRequest", iframeOrigin);
						            $rootScope.isTargetBcusEligible();
						        }
						    });
						}

						function bccTargetApply() {
							return $.ajax({
								cache: false,
								async: false,
								type: "POST",
								dataType: "json",
								contentType: "application/json; charset=utf-8",
								url: window.location.origin + "/api/v2/target/BccApply/",
								data: JSON.stringify(json),
								success: function (data) {
									if (data.length) { window.location.href = data; paymentFormIPS = 0 }
								},
								error: function (abc, error) {
									$rootScope.isTargetBcusEligible();
									console.log(JSON.stringify(abc));
									return;
								}
							});
						}
					} catch (e) {
						console.log(e.message);
					}
				},

				isTargetBcusEligible: function () {
					return $.ajax({
						cache: false,
						async: false,
						type: "GET",
						timeout: 500,
						url: window.location.origin + "/api/v2/target/BccTargetEligibility/",
						success: function (data) {
							if (data === true) {
								$rootScope.IsBCusEligible = true;
							} else {
								$rootScope.IsBCusEligible = false;
							}
							return;
						},
						error: function (abc, error) {
							console.log(JSON.stringify(abc));
							console.log(error);
							$rootScope.IsBCusEligible = false;
						}
					});
				},

				isUpliftConfirmed: function () {
					return $.ajax({
						cache: false,
						async: false,
						type: "POST",
						timeout: 500,
						url: window.location.origin + "/Book/Payment/UpliftConfirm/",
						success: function (data) {
							if (data.OrderId !== "empty") {
								$rootScope.UpliftOrderId = data.OrderId;
								$rootScope.UpliftReservationCode = data.PNR;
								$rootScope.isUpliftConfirmed = true;
							} else {
								$rootScope.isUpliftConfirmed = false;
							}
							return;
						},
						error: function (abc, error) {
							console.log(JSON.stringify(abc));
							console.log(error);
							$rootScope.isUpliftConfirmed = false;
						}
					});
				}

		    };

		    var urlParams;

		    function parseQS() {
		        var match;
		        var amp = /([^&=]+)=?([^&]*)/g;
		        var query = window.location.search.substring(1);
		        urlParams = {};

		        while ((match = amp.exec(query))) {
		            urlParams[decode(match[1])] = decode(match[2]);
		        }
		    }

		    function decode(s) {
		        return decodeURIComponent(s.replace(/\+/g, ' '));// replace addition symbol with a space
		    }

		    function parseFSModel(item) {
		        var fsModel = null;
		        var valueList = item.split('|');
		        if (valueList.length === 6) {
		            fsModel = {
		                FlightSearchSegmentList: [],
		                FlightQueryTypeId: parseInt(valueList[1]),
		                AdultCount: parseInt(valueList[2]),
		                ChildCount: parseInt(valueList[3]),
		                InfantCount: parseInt(valueList[4]),
		                IsRefundable: (valueList[5] === 'true') ? true : false
		            };

		            var segmentList = valueList[0].split('+');

		            angular.forEach(segmentList, function (segment) {
		                var partList = segment.split('=');
		                if (partList.length === 4) {
		                    fsModel.FlightSearchSegmentList.push({
		                        DepartureDate: partList[0],
		                        OriginCityCode: partList[1],
		                        DestinationCityCode: partList[2],
		                        IsMiles: (partList[3] === 'true') ? true : false
		                    });
		                }
		            });
		        }

		        return fsModel;
		    }

		    //$(window).on('onpopstate', parseQS); maybe needed someday?

		    // Inject language checks into $rootScope...
		    $rootScope.constructor.prototype.isEN = svc.isEN;
		    $rootScope.constructor.prototype.isJP = svc.isJP;
		    $rootScope.constructor.prototype.isKR = svc.isKR;
		    $rootScope.constructor.prototype.isCN = svc.isCN;
		    $rootScope.constructor.prototype.isTW = svc.isTW;

		    return svc;
		}
    ]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haConfigModule', []);

	module.factory('haConfig', [function () {

		var templateRoot = '/templates/';
		var sitecoreRoot = '/sitecoreresources/';
		var languageCode = 'en-US';
		var tlds = [{'com': 'en'}, {'co.jp': 'ja-jp'}, {'co.kr': 'ko-kr'}, {'com.cn': 'zh-cn'}, {'com.tw': 'zh-tw'}, {'com.au': 'en-au'}, {'co.nz': 'en-nz'}];
		var hostArr = location.host.split('.');
		var tld = hostArr.length < 4 || !isNaN(parseInt(hostArr[0], 10)) ? 'com' : hostArr[2] + '.' + hostArr[3];
		var cultureIdx = tlds.map(function (i) { return typeof i[tld] !== 'undefined'; }).indexOf(true);
		var langString = (HA.cdn.html && HA.cdn.js && HA.cdn.css && HA.cdn.img) ? '?sc_lang=' + tlds[cultureIdx][tld] : '';
		var cacheVer = window.ver;

		if (HA.cdn.isOldIE) {
			HA.cdn.html = HA.cdnDynamic = '';	// JS, CSS, IMG are ok cross domain
		}

		return {

			/*
				The following helper methods are to facilitate getting the CDN prefix added to urls.
				For now they all assume you're passing in a path and query and not a fully qualified url
				as passing in a full url defeats the point of helper methods to prepend a CDN prefix.

				Should you pass in a full url, these methods will echo back your parameter as there is no way
				for them to discern your intentions.
			*/

			// TEMPLATES
			getTemplateUrl: function (url) {
				return url ? [HA.cdn.html, templateRoot, url.replace(/^\/|\/$/g, ''), '?ver=', cacheVer].join('') : '';
			},
			getTemplateUrlWithInclude: function (url) {
				return '<div ng-include="\'' + this.getTemplateUrl(url) + '\'"></div>';
			},
			getRazorTemplateUrl: function (url) {
				url += langString;
				var urlAppend = (~url.indexOf('?') ? '&' : '?') + 'ver=' + cacheVer;
				return url ? [HA.cdn.html, templateRoot, url.replace(/^\/|\/$/g, ''), urlAppend].join('') : '';
			},

			// IMAGES
			getImgUrl: function (pathAndQuery) {

				// If a full url was passed echo it back
				if (/^http/i.test(pathAndQuery) || /^\/\//i.test(pathAndQuery)) {
					return pathAndQuery;
				}

				// If there is a query string append sc_lang with an & instead of a ?
				var querySafeLangString = langString;
				if (/\?/gi.test(pathAndQuery) && langString) {
					querySafeLangString = '&' + langString.substring(1);
				}

				return pathAndQuery ? [HA.cdn.img, '/', pathAndQuery.replace(/^\/|\/$/g, ''), querySafeLangString].join('') : '';
			},

			// JSON ENDPOINTS
			getSitecoreResourceUrl: function (url) {
				return url ? [HA.cdnDynamic, sitecoreRoot, url.replace(/^\/|\/$/g, ''), langString].join('') : '';
			},
			getDynamicJsonUrl: function (pathAndQuery) {

				// If a full url was passed echo it back
				if (/^http/i.test(pathAndQuery) || /^\/\//i.test(pathAndQuery)) {
					return pathAndQuery;
				}

				// If there is a query string append sc_lang with an & instead of a ?
				var querySafeLangString = langString;
				if (/\?/gi.test(pathAndQuery) && langString) {
					querySafeLangString = '&' + langString.substring(1);
				}

				return pathAndQuery ? [HA.cdnDynamic, '/', pathAndQuery.replace(/^\/|\/$/g, ''), querySafeLangString].join('') : '';
			},



			// Language code helpers
			setLanguageCode: function (lc) {
				languageCode = lc;
				if (lc === 'en') {
					languageCode = 'en-US';
				}
			},
			getLanguageCode: function () {
				return languageCode;
			}
		};
	}
	]);

})(angular);
;
(function (ng) {

	// Hotel Common Service
	// --------------------------------------------
	//
	// * **Class:** haHotelRoomService
	// * **Author:** Nathan Probst
	//
	// Service providing room-related functions to Hotel controllers

	'use strict';

	var module;
	try {
		module = ng.module('haHotelPackagesModule');
	} catch (e) {
		module = ng.module('haHotelPackagesModule', ['haUtilsModule', 'haGlobalsModule', 'haFeatureFlagsModule', 'haCurrencyModule', 'haAncillariesModule', 'haModalService', 'haEqualHeightModule', 'haPassengersService', 'haRoundingFiltersModule', 'haSitecoreModule']);
	}

	var MAX_ROOMS = 4;
	var MAX_ADULTS_PER_ROOM = 3;
	var MAX_GUESTS_PER_ROOM = 4;
	/*
	 // Scheme for ROOMS_LOOKUP
	 // '#a#c':[ [adultsInRoom, childrenInRoom] ]
	 var ROOMS_LOOKUP = {
	 '1a0c': [[1,0]],
	 '1a1c': [[1,1]],
	 '1a2c': [[1,2]],
	 '1a3c': [[1,3]],
	 '2a0c': [[2,0]],
	 '2a1c': [[2,1]],
	 '2a2c': [[2,2]],
	 '2a3c': [[1,2],[1,1]],
	 '2a4c': [[1,2],[1,2]],
	 '2a5c': [[1,3],[1,2]],
	 '3a0c': [[3,0]],
	 '3a1c': [[3,1]],
	 '3a2c': [[2,2],[1,0]],
	 '3a3c': [[2,2],[1,1]],
	 '3a4c': [[2,2],[1,2]],
	 '4a0c': [[3,0],[1,0]],
	 '4a1c': [[3,1],[1,0]],
	 '4a2c': [[3,1],[1,1]],
	 '4a3c': [[3,1],[1,2]],
	 '5a0c': [[3,0],[2,0]],
	 '5a1c': [[3,1],[2,0]],
	 '5a2c': [[3,1],[2,1]],
	 '6a0c': [[3,0],[3,0]],
	 '6a1c': [[3,1],[3,0]],
	 '7a0c': [[3,0],[3,0],[1,0]]
	 };

	 var _initRoomsForGuests = function (numAdults, numChildren) {
	 var rooms = [];
	 var key = numAdults+'a'+numChildren+'c';
	 var defaults = ROOMS_LOOKUP[key];
	 if (defaults != null) {
	 // Array.map would be better...but IE.
	 for (var i=0; i<defaults.length; i++) {
	 rooms.push({
	 RoomIndex: i,
	 AdultCount: defaults[i][0],
	 ChildCount: defaults[i][1],
	 SeniorCount: 0,
	 InfantCount: 0
	 });
	 }
	 } else {
	 rooms.push({
	 RoomIndex: 0,
	 AdultCount: numAdults,
	 ChildCount: numChildren,
	 SeniorCount: 0,
	 InfantCount: 0
	 });
	 }
	 return rooms;
	 };
	 */
	var countType = function (arr, type) {
		return arr.reduce(function (num, t) {
			if (t.Type === type) {
				num++;
			}
			return num;
		}, 0);
	};
	var sumField = function (arr, field) {
		return arr.reduce(function (num, obj) {
			var val = obj[field];
			if (typeof val === 'string') {
				obj[field] = parseInt(val);
			}
			return num + obj[field];
		}, 0);
	};

	module.factory('haHotelRoomService', [
		'$log',
		'$rootScope',
		'$templateCache',
		'haModal',

		function ($log, $rootScope, $templateCache, haModal) {

			//jscs:disable disallowDanglingUnderscores
			var svc = {
				_data: null,
				_rooms: [],
				_travellers: [],
				_numAdults: 0,
				_numChildren: 0,
				init: function (data) {
					svc._data = data;
					svc._travelers = data.Travellers;
					svc._numSeniors = countType(data.Travellers, 'SRC');
					svc._numInfants = countType(data.Travellers, 'INF');
					svc._numChildren = countType(data.Travellers, 'WBC') +
						countType(data.Travellers, 'CHD') +
						countType(data.Travellers, 'INF');
					svc._numAdults = svc._travelers.length - svc._numChildren;
					if (data.UserRoomDistribution && data.UserRoomDistribution.Rooms) {
						svc._rooms = data.UserRoomDistribution.Rooms;
						// } else {
						// 	svc._rooms = _initRoomsForGuests(svc._numAdults, svc._numChildren);
					}
				},
				count: function () {
					return svc._rooms.length;
				},
				addRoom: function (rooms) {
					rooms = rooms || svc._rooms;
					rooms.push({
						RoomIndex: rooms.length + 1,
						AdultCount: 1,
						ChildCount: 0,
						SeniorCount: 0,
						InfantCount: 0
					});
				},
				canAddRoom: function (rooms) {
					rooms = rooms || svc._rooms;
					return (rooms.length < MAX_ROOMS) && (rooms.length < svc._numAdults);
				},
				validate: function (rooms) {
					// rooms = rooms || svc._rooms;
					var errors = [];

					if (rooms.length > svc._numAdults) {
						errors.push('Cannot have more rooms than adult guests.');
					}

					var i;
					var guests;
					for (i = 0; i < rooms.length; i++) {
						guests = parseInt(rooms[i].AdultCount) + parseInt(rooms[i].ChildCount);
						if (MAX_ADULTS_PER_ROOM < parseInt(rooms[i].AdultCount)) {
							errors.push('Too many adults in Room ' + (i + 1) + '.');
							break;
						}
					}

					for (i = 0; i < rooms.length; i++) {
						guests = parseInt(rooms[i].AdultCount) + parseInt(rooms[i].ChildCount);
						if (MAX_GUESTS_PER_ROOM < guests) {
							errors.push('Too many guests in Room ' + (i + 1) + '.');
							break;
						}
					}

					var numAdults = sumField(rooms, 'AdultCount');
					if (numAdults !== svc._numAdults) {
						errors.push('Must have ' + svc._numAdults + ' adult guest(s).');
					}
					var numChildren = sumField(rooms, 'ChildCount');
					if (numChildren !== svc._numChildren) {
						errors.push('Must have ' + svc._numChildren + ' child guest(s).');
					}
					sumField(rooms, 'SeniorCount');
					sumField(rooms, 'InfantCount');

					if (errors.length > 0) {
						return errors;
					}
					return null;
				},
				getMaxAdultsPerRoom: function() {
					return MAX_ADULTS_PER_ROOM;
				},
				getMaxGuestsPerRoom: function() {
					return MAX_GUESTS_PER_ROOM;
				},
				getMaxRooms: function() {
					return MAX_ROOMS;
				},
				getNumAdults: function() {
					return svc._numAdults;
				},
				getNumChildren: function() {
					return svc._numChildren;
				},
				openChangeRoomsModal: function () {
					var scope = $rootScope.$new();
					scope.rooms = svc._rooms.map(function (r) {
						return {
							RoomIndex: r.RoomIndex,
							AdultCount: r.AdultCount + r.SeniorCount,
							ChildCount: r.ChildCount + r.InfantCount,
							SeniorCount: 0,
							InfantCount: 0
						};
					});
					scope.addRoom = function () {
						svc.addRoom(scope.rooms);
					};
					scope.canAddRoom = function () {
						return svc.canAddRoom(scope.rooms);
					};
					scope.updateRooms = function () {
						var errors = svc.validate(scope.rooms);
						if (errors == null) {
							var seniors = svc._numSeniors;
							var infants = svc._numInfants;

							// I would use a map() here, but we want to process
							// the array from tail to head...
							angular.copy(scope.rooms, svc._rooms);
							for (var idx = scope.rooms.length - 1; idx >= 0; idx--) {
								var r = scope.rooms[idx];
								var s = 0;
								var a = r.AdultCount;
								var c = r.ChildCount;
								var i = 0;

								if (seniors > 0) {
									s = Math.min(a, seniors);
									a = a - s;
									seniors -= s;
								}

								if (infants > 0) {
									i = Math.min(c, infants);
									c = c - i;
									infants -= i;
								}

								svc._rooms[idx] = {
									RoomIndex: r.RoomIndex,
									SeniorCount: s,
									AdultCount: a,
									ChildCount: c,
									InfantCount: i
								};
							}

							scope.$modalCancel();
							$rootScope.$broadcast('haHotelRoomService:updateRooms', svc._rooms);
						} else {
							scope.errorMessage = errors.join('<br>');
						}
					};
					haModal({
						id: 'ChangeRoomsModal',
						backdrop: 'true',
						scope: scope,
						template: $templateCache.get('ChangeRoomsModal.html')
					});
				}


			};

			return svc;
		}
	]);

})(angular);
;
(function (ng) {

	// Hotel Common Service
	// --------------------------------------------
	//
	// * **Class:** haHotelCommonService
	// * **Author:** Nathan Probst
	//
	// Service providing common functions to Hotel controllers

	'use strict';

	var module;
	try {
		module = ng.module('haHotelPackagesModule');
	} catch (e) {
		module = ng.module('haHotelPackagesModule', ['haUtilsModule', 'haGlobalsModule', 'haFeatureFlagsModule', 'haCurrencyModule', 'haAncillariesModule', 'haModalService', 'haEqualHeightModule', 'haPassengersService', 'haRoundingFiltersModule', 'haSitecoreModule']);
	}

	module.factory('haHotelCommonService', [
		'$log',
		'$window',
		'haFeatureFlags',

		function ($log, $window, flags) {

			var mapPromotions = function (p) {
				return {
					amount: p.Amount,
					numNightsFree: p.NumNightsFree,
					numNightsRequired: p.NumNightsRequired,
					promoCode: p.PromoCode,
					text: p.ShortMarketingText,
					customerFulfillmentRequirements: p.CustomerFulfillmentRequirements,
					termsAndConditions: p.TermsAndConditions
				};
			};

			var mapRoomDetails = function (p) {
				return function (r) {
					if (r == null) {
						return null;
					}

					var result = {
						roomCode: r.RoomCode,
						rateCode: r.RateCode,
						rateAccessCode: r.RateAccessCode,
						promotions: r.Promotion.map(mapPromotions),
						stayAvailability: r.StayAvailability,
						description: r.RoomDescription,
						basePrice: parseFloat(p.BasePrice.Amount) || p.BasePrice.Amount,
						nightlyPriceDiff: 0,
						totalPriceDiff: 0,
						totalPrice: 0,
						resortFees: 0
					};

					// Sum up fees
					if ((r.HotelFees != null) && (r.HotelFees.SupplierFees != null)) {
						angular.forEach(r.HotelFees.SupplierFees, function (v) {
							if (typeof v.total === 'number') {
								result.resortFees += v.total;
							}
						});
					}
					return result;
				};
			};

			var mapRoomUpSells = function (p, baseRoom, nights) {
				return function (u) {
					var upsellRoom = mapRoomDetails(p)(u.RoomDetails.HotelRoomInfo);
					upsellRoom = angular.extend({}, baseRoom, upsellRoom);
					upsellRoom.nightlyPriceDiff = (u.PriceDifference.Value / nights);
					upsellRoom.totalPriceDiff = upsellRoom.nightlyPriceDiff * nights;

					return upsellRoom;
				};
			};

			var service = {

				data: null,
				hotels: [],

				mapOrbitzRepriceRooms: function (reprice, taxes) {
					var rooms = [];
					var roomTypes = reprice.PkgHotelSolution && reprice.PkgHotelSolution.HotelRoomType || [];
					var roomCount = roomTypes.length;
					var p = reprice.PackageSolution;
					var nights = p.HotelServicePrice.NumberOfNights || 1;
					for (var i = 0; i < roomCount; i++) {
						rooms[i] = [];
						var baseRoom = mapRoomDetails(p)(roomTypes[i].RoomDetails);
						if (baseRoom != null) {
							var upsellRooms = roomTypes[i].RoomUpSells.map(mapRoomUpSells(p, baseRoom, nights));
							rooms[i] = [baseRoom].concat(upsellRooms);
							for (var j = 0; j < rooms[i].length; j++) {
								var r = rooms[i][j];
								r.totalPrice = r.basePrice + r.totalPriceDiff + taxes;
							}
						}
					}
					return rooms;
				},

				mapOrbitzPackages: function (orbitzPackage) {
					if (orbitzPackage == null) {
						$log.debug('orbitzPackage == null');
						return [];
					}

					if (orbitzPackage.ErrorMessage != null) {
						$log.debug('orbitzPackage.ErrorMessage != null');
						return [];
					}
					// var then;
					// if (typeof performance !== 'undefined') {
					//   then = $window.performance.now();
					// }

					var result = orbitzPackage.HotelSolutions.map(function (h, idx) {
						var p = orbitzPackage.PackageSolutions[idx];

						var hotel = {
							id: h.HotelId,
							name: h.Name,
							description: '',
							hostCode: h.HotelHostCode,
							imageUrls: service.propertyImageUrls(h),
							propertyImageUrl: service.pickBestPropertyImage(h),
							starRating: h.StarRating,
							userScore: h.UserScoreRating,
							distanceValue: h.Distance.Value,
							distanceUnit: h.Distance.DistanceUnitField,
							coords: {
								latitude: h.Geocoded.Latitude.Value,
								longitude: h.Geocoded.Longitude.Value
							},
							rooms: h.HotelRoomDetail.map(mapRoomDetails(p)),
							totalCost: p.Total,
							morePerNight: p.ServicePricePerPerson,
							nights: p.HotelServicePrice.NumberOfNights,
							checkIn: p.HotelServicePrice.CheckInDate,
							checkOut: p.HotelServicePrice.CheckOutDate,
							savings: p.ServicePriceTotalSavings,
							streetAddress: h.Address.Street + ', ' + h.Address.City,
							address: h.Address,
							phoneNumber: h.PhoneNumber,
							isExclusive: h.IsHAExclusiveOffer,
							isLimited: h.IsLimited,
							resortFees: null
						};

						angular.forEach(h.Content.Description, function (d) {
							hotel.description += d.replace('\r\n', '<br>') + '<br>';
						});

						hotel.fullAddress = h.Address.Street + ', ' + h.Address.City + ', ' + h.Address.StateProvince + ' ' + h.Address.PostalCode;

						hotel.savingsPercent = 100 * (hotel.savings / (hotel.savings + hotel.cost));

						// Promote room promotions to hotel
						hotel.promotions = [];
						for (var i = 1; i <= hotel.rooms.length; i++) {
							var r = hotel.rooms[i - 1];
							if ((r.promotions != null) && (0 < r.promotions.length)) {
								var promos = r.promotions;
								if (hotel.rooms.length > 1) {
									for (var j = 0; j < promos.length; j++) {
										promos[j].text = 'Room ' + i + ': ' + promos[j].text;
									}
								}
								hotel.promotions = hotel.promotions.concat(promos);
							}
						}

						return hotel;
					});

					// if (typeof performance !== 'undefined') {
					//   $log.debug('mapOrbitzPackages['+orbitzPackage.HotelSolutions.length+']:',
					//     Math.round(1000*($window.performance.now()-then))+'μs');
					// }

					return result;
				},

				// calcSavings: function(hotels, packages) {
				//   ng.forEach(hotels, function(hotel, idx) {
				//     var savings = parseFloat(packages[idx].RateInformation.TotalSavings && packages[idx].RateInformation.TotalSavings.value);
				//     if (savings > 0) {
				//       var price = parseFloat(packages[idx].RateInformation.TotalCost.value);
				//       hotel.savings = savings;
				//       hotel.savingsPercent = (savings / (savings + price))*100;
				//     }
				//   });
				// },

				mapBestPropertyImage: function (hotels) {
					ng.forEach(hotels, service.pickBestPropertyImage);
				},

				pickBestPropertyImage: function (hotel) {
					for (var idx = 0; idx < hotel.Content.Visuals.length; idx++) {
						var url = hotel.Content.Visuals[idx].Url;
						if (url.indexOf('.jpg') === (url.length - 4)) {
							hotel.propertyImageUrl = url;
							return url;
						}
					}
				},

				propertyImageUrls: function (hotel, max) {
					max = max || flags.get('HotelMaxImages', 27);
					var trim = flags.get('HotelTrimLastImage', true) ? hotel.Content.Visuals.length - 2 : hotel.Content.Visuals.length - 1;
					var result = [];
					angular.forEach(hotel.Content.Visuals, function (v, i) {
						if ((result.length >= max) || ((i > 0) && (i >= trim))) {
							return;
						}

						var url = v.Url;
						if ((url.indexOf('.jpg') === (url.length - 4)) ||
							(url.indexOf('.png') === (url.length - 4)) ||
							(url.indexOf('.gif') === (url.length - 4)) ||
							(url.indexOf('.jpeg') === (url.length - 5))) {
							result.push(url);
						}
					});

					return result;
				}

			};

			return service;
		}
	]);

})(angular);
;
(function (angular) {

	// Hawaiian Feature Flags
	// ======================
	//
	// * **Class:** haFeatureFlags
	// * **Author:** Nathan Probst
	//
	// A utility class to access Feature Flags declared globally without declaring globals in code.

	'use strict';

	var module = angular.module('haFeatureFlagsModule', []);

	module.provider('haFeatureFlags', ['$windowProvider', function ($windowProvider) {
		var $window = $windowProvider.$get();
		var FLAGS = (($window.HA != null) && $window.HA.FLAGS) || {};
		var GLOBAL_DEFAULTS = {};

		var hasDefault = this.hasDefault = function (name) {
			return (typeof GLOBAL_DEFAULTS[name] !== 'undefined');
		};

		var has = this.has = function (name) {
			return (typeof FLAGS[name] !== 'undefined');
		};

		var get = this.get = function (name, localDefault) {
			if (has(name)) {
				return FLAGS[name];
			} else if (typeof localDefault !== 'undefined') {
				return localDefault;
			} else if (hasDefault(name)) {
				return GLOBAL_DEFAULTS[name];
			}
			return undefined;
		};

		this.setDefault = function (name, def) {
			GLOBAL_DEFAULTS[name] = def;
		};

		this.getDefault = function (name) {
			return GLOBAL_DEFAULTS[name];
		};

		this.$get = function () {
			return {
				hasDefault: hasDefault,
				has: has,
				get: get,
				setDefault: this.setDefault,
				getDefault: this.getDefault
			};
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haDateUtilsModule', []);

	module.factory('haDateUtils', ['$locale', function ($locale) {

		Date.prototype.dateAdd = function (size, value) {
			value = parseInt(value);
			var incr = 0;
			switch (size) {
				case 'day':
					incr = value * 24;
					this.dateAdd('hour', incr);
					break;
				case 'hour':
					incr = value * 60;
					this.dateAdd('minute', incr);
					break;
				case 'week':
					incr = value * 7;
					this.dateAdd('day', incr);
					break;
				case 'minute':
					incr = value * 60;
					this.dateAdd('second', incr);
					break;
				case 'second':
					incr = value * 1000;
					this.dateAdd('millisecond', incr);
					break;
				case 'month':
					value = value + this.getUTCMonth();
					if (value / 12 > 0) {
						this.dateAdd('year', value / 12);
						value = value % 12;
					}
					this.setUTCMonth(value);
					break;
				case 'millisecond':
					this.setTime(this.getTime() + value);
					break;
				case 'year':
					this.setFullYear(this.getUTCFullYear() + value);
					break;
				default:
					throw new Error('Invalid date increment passed');
			}

			return this;
		};
		Date.prototype.YYYY_MM_DD = function () {
			return this.getFullYear() + pad(this.getMonth() + 1) + pad(this.getDate());
		};
		function pad(n) {
			return n > 9 ? '-' + n : '-0' + n;
		}

		return {
			getVisibleMonths: function (monthsBack, monthsForward) {
				var startDate = (monthsBack > 0) ? (new Date()).dateAdd('month', monthsBack * -1) : new Date();
				var currentMonth = startDate.getMonth();
				var currentYear = startDate.getFullYear();
				var months = [];
				//monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
				var monthNames = $locale.DATETIME_FORMATS.MONTH;

				for (var i = 0; i < (monthsBack + monthsForward + 1); i++) {

					if (currentMonth === 12) {
						currentMonth = 0;
						currentYear++;
					}

					months.push({
						weeks: this.getVisibleWeeks(new Date(currentYear, currentMonth, 1)),
						name: monthNames[currentMonth] + ' ' + currentYear
					});

					currentYear = (currentMonth > 11) ? currentYear + 1 : currentYear;
					currentMonth = (currentMonth > 11) ? currentMonth - 12 : ++currentMonth;
				}

				return months;
			},
			getVisibleWeeks: function (date) {
				date = new Date(date || new Date());
				date.setDate(1);
				date.setHours(0);
				date.setMinutes(0);
				date.setSeconds(0);
				date.setMilliseconds(0);

				//fixes rare edge case of 1st day of week and 1st day of month
				if (!(date.getDay() === 0 && date.getDate() === 1)) {
					if (date.getDay() === 0) {
						date.setDate(-5);
					} else {
						date.setDate(date.getDate() - (date.getDay()));
					}
					if (date.getDate() === 1) {
						date.setDate(-6);
					}
				}

				var weeks = [];
				while (weeks.length < 6) {
					/*jshint -W116 */
					//if(date.getYear()=== startYear && date.getMonth() > startMonth) break;
					var week = [];
					for (var i = 0; i < 7; i++) {
						week.push(new Date(date));
						date.setDate(date.getDate() + 1);
					}
					weeks.push(week);
				}
				return weeks;
			},
			getDaysOfWeek: function (date) {
				date = new Date(date || new Date());
				date = new Date(date.getFullYear(), date.getMonth(), date.getDate());
				date.setDate(date.getDate() - (date.getDay()));
				var days = [];
				for (var i = 0; i < 7; i++) {
					days.push(new Date(date));
					date.setDate(date.getDate() + 1);
				}
				return days;
			},
			isValid: function (year, month, day) {
				var yearNum = parseInt(year, 10);
				var monthNum = parseInt(month, 10);
				var dayNum = parseInt(day, 10);

				if (!yearNum || isNaN(monthNum) || !dayNum) {
					return false;
				}
				var date = new Date(yearNum, monthNum - 1, dayNum);

				return date.getFullYear() === yearNum && date.getMonth() === monthNum - 1 && date.getDate() === dayNum;
			},
			isBetween: function (checkDate, date1, date2) {
				return this.isAfter(checkDate, date1) && this.isBefore(checkDate, date2);
			},
			isAfter: function (date1, date2) {
				if (!angular.isDate(date1) || !angular.isDate(date2)) {
					return false;
				}
				return date1.getTime() > date2.getTime();
			},
			isBefore: function (date1, date2) {
				if (!angular.isDate(date1) || !angular.isDate(date2)) {
					return false;
				}
				return date1.getTime() < date2.getTime();
			},
			isSameYear: function (date1, date2) {
				if (!angular.isDate(date1) || !angular.isDate(date2)) {
					return false;
				}
				return date1.getFullYear() === date2.getFullYear();
			},
			isSameMonth: function (date1, date2) {
				if (!angular.isDate(date1) || !angular.isDate(date2)) {
					return false;
				}
				return this.isSameYear(date1, date2) && date1.getMonth() === date2.getMonth();
			},
			isSameDay: function (date1, date2) {
				if (!angular.isDate(date1) || !angular.isDate(date2)) {
					return false;
				}
				return this.isSameMonth(date1, date2) && date1.getDate() === date2.getDate();
			},
			isAfter331: function (date1, date2) {
				if (!angular.isDate(date1) || !angular.isDate(date2)) {
					return false;
				}
				var oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
				var diffDays = Math.round(Math.abs((date1.getTime() - date2.getTime()) / (oneDay)));
				return (diffDays >= 330);
			},
			msToDate: function (ms) {
				return new Date(ms);
			},
			isNextDay: function (date1, date2) {
				if (!angular.isDate(date1) || !angular.isDate(date2)) {
					return false;
				}
				return date1.getTime() < date2.getTime();
			},
			numDaysDifference: function (date1, date2) {
				if (!angular.isDate(date1) || !angular.isDate(date2)) {
					return false;
				}
				var oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
				return Math.round(Math.abs((date1.getTime() - date2.getTime()) / (oneDay)));
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haSitecoreModule', ['haGlobalsModule']);
	var failKeys = [];

	function isFailKey(key) {
		if (failKeys.indexOf(key) !== -1) {
			failKeys.splice(failKeys.indexOf(key), 1);
			return true;
		}
		return false;
	}

	function set(elm, attrs, value, name) {
		var localized = STRINGS(value);

		if (/text|html|value/.test(name)) {
			elm[name === 'value' ? 'val' : name](localized);
		}
		else {
			attrs[attrs.$normalize(name)] = localized;
			elm.attr(name, localized);
		}
		if (attrs['scsHideEmpty'] !== undefined && isFailKey(value)) {
			elm.addClass('hidden');
		}
	}

	function hyphenate(name) {
		return name.replace(/[A-Z]/g, function (c, i) {
			return (i ? '-' : '') + c.toLowerCase();
		}).replace(/^[^-]+-/, '');
	}

	function format(str, values, scope) {
		//first pass: replace all number placeholders
		str = str.replace(/{(\d+)}/g, function (match, i) {
			return values && values[i];
		});

		//second pass: $eval all other placeholders
		if (scope) {
			str = str.replace(/{([^}]+)}/g, function (match, key) {
				return (scope.$eval(key)) || match;
			});
		}
		return str;
	}

	function getKeys(elm) {
		if (!elm[0].attributes[0]) {
			throw new Error('`scs` element must have a keys attribute');
		}
		return elm[0].attributes[0].name;
	}

	function fail(key) {
		console.error('SCString not found: ', key);
		failKeys.push(key);
		return '[' + key + ']';
	}

	var STRINGS = fail;


	module.factory('haSitecoreStrings', [
		'$parse',
		'haGlobals',
		'$q',
		'haConfig',
		'haHttpService',

		function ($parse, haGlobals, $q, config, $http) {

			var cdn = config.getSitecoreResourceUrl;
			STRINGS = function (path, params) {
				//				console.log('scs', path);
				var value = $parse(path.toLowerCase())(STRINGS);
				if (value && angular.isArray(params)) {
					value = format(value, params);
				}
				return value || fail(path);
			};
			function truthy(path) {
				var value = $parse(path.toLowerCase())(STRINGS);
				if (typeof value === 'undefined') {
					fail(path);
				}
				return !!value && !/0|false/.test(value) && value;
			}

			function request(guid) {
				return $http.GET(cdn(guid)).then(function (res) {
					return res.data ? (STRINGS[guid] = res.data) : Promise.reject(new Error('Invalid response data'));
				});
			}

			function get(ns_key) {
				var match = /^(\w+)(?:(?:\.|\[['"])([\w ]+))?/.exec(ns_key);
				if (!match) {
					return Promise.reject(new Error('Invalid ns_key argument: ' + ns_key));
				}
				var ns = match[1].toLowerCase();
				var key = match[2];
				var promise = STRINGS[ns] ? Promise.resolve(STRINGS[ns]) : (STRINGS[ns] = request(ns));
				return promise.then(function (resources) {
					return key ? STRINGS(ns_key) : resources;
				});
			}

			STRINGS.truthy = truthy;
			STRINGS.request = request;
			STRINGS.get = get;
			STRINGS.fetch = function (guids) {
				if (typeof guids !== 'string') {
					return Promise.resolve(STRINGS);
				}
				return Promise.all(guids.split(',').map(get));
			};

			haGlobals('HA', function (HA) {
				angular.extend(STRINGS, HA.SCStrings);
			});

			return STRINGS;
		}
	]);

	module.factory('haSitecore', [
		'haGlobals',
		'haConfig',
		'haHttpService',
		function (haGlobals, config, $http) {

			//$img
			var loaded = 0;
			var cdn = config.getImgUrl;
			var imageDataUrl = config.getDynamicJsonUrl('sitecoreimages');

			function img(path, attrs) {
				if (!attrs) {
					attrs = {};
				}
				path = path.toLowerCase();
				attrs.src = cdn('/static/images/' + path);
				var $img = angular.element('<img alt>').attr(attrs);
				img.alt(path).then(function (alt) {
					$img.attr('alt', alt || '');
				});
				['catch'](function (err) {
					console.error(err);
				});
				return $img;
			}

			img.alt = function (path) {
				path = path.toLowerCase();
				if (loaded) {
					var alt = img[path];
					if (typeof alt === 'undefined') {
						return Promise.reject(new Error('Sitecore image not found: ' + path));
					}
					return Promise.resolve(alt);
				}
				return $http.GET(imageDataUrl).then(function (res) {
					if (res.data) {
						loaded++;
						angular.extend(img, res.data);
						return;
					}
					if (loaded < 3) {
						return img.alt(path);
					}
					return Promise.reject(new Error('Data unavailable'));
				});
			};

			//$switch
			function sw(key) {
				return !!sw[key.toLowerCase()];
			}

			haGlobals('HA', function (HA) {
				angular.extend(sw, HA.SCSwitches);
			});

			return {
				$switch: sw,
				$img: img
			};
		}
	]);

	['scsText', 'scsHtml', 'scsLabel', 'scsEyebrow', 'scsPlaceholder', 'scsErrorMessage', 'scsHref', 'scsSrc', 'scsDescription', 'scsHeader', 'scsValue']
	.forEach(function (directiveName) {
		var destination = hyphenate(directiveName);

		module.directive(directiveName, function () {
			return {
				priority: 500,
				link: {
					pre: function (scope, elm, attrs) {
						set(elm, attrs, attrs[directiveName], destination);
					}
				}
			};
		});
	});

	module.directive('i18nContent', function () {
		return {
			restrict: 'A',
			link: function (scope, elm, attrs) {
				var keys = format(attrs.i18nContent, [], scope);
				// angular trims space from the `attrs` values so
				// we need to 'do the icky'® and get it directly off the elm.
				var formatString = elm.attr('format') || elm.text();

				STRINGS.fetch(keys).then(function (results) {
					var children = elm.contents();
					if (children.length > 1 || (children.length === 1 && children[0].nodeType !== 3 && children[0].nodeType !== 8)) {
						return console.error('i18n-content directive can only only be applied to an element with a text node child.');
					}
					var setter = elm[0].nodeName === 'INPUT' ? 'val' : 'html';
					elm[setter](formatString ? format(formatString, results, scope) : results.join(' '));
				});
			}
		};
	});

	module.directive('i18nBackgroundImage', function () {
		return {
			restrict: 'A',
			link: function (scope, elm, attrs) {
				var key = format(attrs.i18nBackgroundImage, [], scope);
				STRINGS.get(key).then(function (value) {
					elm.css('background-image', format('url(\'{0}\')', [scope.getMediaImage(value)]));
				});
			}
		};
	});

	module.directive('i18nSrc', function () {
		return {
			restrict: 'A',
			link: function (scope, elm, attrs) {
				var key = format(attrs.i18nSrc, [], scope);
				STRINGS.get(key).then(function (value) {
					elm.attr('src', scope.getMediaImage(value));
				});
			}
		};
	});

	// CS: This directive takes an input in the following format <i18nimage key="guidNameConstant.fieldName"></i18nimage>, and generates a proper image tag from the sitecore ajax endpoint
	module.directive('i18nimage', ['haUtils', function (haUtils) {
		return {
			restrict: 'E',
			replace: true,
			scope: {},
			template: '<img ng-src="{{object.src}}" alt=""/>',
			link: function ($scope, elm) {
				var key = elm.attr('key');
				STRINGS.get(key).then(function (value) {
					if (value !== '['+key+']') {
						$scope.object = haUtils.getImageObjectFromSiteCoreString(value);
						if ($scope.object.alt) {
							elm.attr('alt', $scope.object.alt);
						}
						if ($scope.object.width) {
							elm.attr('width', $scope.object.width);
						}
						if ($scope.object.height) {
							elm.attr('height', $scope.object.height);
						}
					} else {
						// display key.value text in place of the image
						elm.replaceWith(value);
					}
				});
			}
		};
	}]);

	// Parses sitecore links into anchor tags
	// Ex: <link text="All Restriction" anchor="" linktype="internal" class="" title="All Restriction" querystring="" 
	//      id="{27942FFA-4159-4C6D-B8C7-992A668EDBBA}" url="/Contact Us" />
	module.directive('i18nLink', ['$window', function ($window) {
		return {
			restrict: 'E',
			replace: true,
			scope: {},
			template: '<a></a>',
			link: function ($scope, elm) {
				var key = elm.attr('key');
				STRINGS.get(key).then(function (value) {
					var linkEl = $(value);
					if (!linkEl[0]) {
						return;
					}

					elm.attr('href', 'http://' + (linkEl.attr('linktype') === 'internal' ? $window.location.host : '') + linkEl.attr('url'));
					elm.attr('title', linkEl.attr('title'));
					elm.text(linkEl.attr('text'));
				});
			}
		};
	}]);

	module.directive('i18n', function () {
		return {
			restrict: 'E',
			link: function (scope, elm) {
				var keys = elm.attr('key') || elm.attr('keys') || getKeys(elm);
				STRINGS.fetch(keys).then(function (results) {
					var formatString = elm.text();
					elm.replaceWith(formatString ? format(formatString, results, scope) : results.join(' '));
				});
			}
		};
	});

	module.directive('scsAttrs', ['$parse', function ($parse) {
		return {
			link: {
				pre: function (scope, elm, attrs) {
					//convert attribute string '{'a':'Hello'}' to an object {a:'Hello'}
					var scsAttrs = $parse(attrs.scsAttrs)(scope);
					if (typeof scsAttrs !== 'object') {
						return;
					}
					angular.forEach(scsAttrs, set.bind(this, elm, attrs));
				}
			}
		};
	}]);


	module.run([
		'$rootScope',
		'haSitecoreStrings',
		'haSitecore',

		function ($rootScope, $scs, sitecore) {
			//assign it to __proto__ so it is accessible by isolated scopes
			$rootScope.constructor.prototype.$scs = $scs;
			$rootScope.constructor.prototype.$switch = sitecore.$switch;
			$rootScope.constructor.prototype.$img = sitecore.$img;

			//TODO: remove. These are only temp
			window.$scs = $scs;
			window.$switch = sitecore.$switch;
			window.$img = sitecore.$img;
		}
	]);

})(angular);
;
(function (angular) {

	// Hawaiian Sitecore Strings Service
	// =================================
	//
	// * **Class:** haSitecoreStrings
	// * **Author:** Nathan Probst
	//
	// Access strings from Sitecore Resources.

	'use strict';

	// var module = angular.module('haViewModelModule', []);
	var module;
	try {
		module = angular.module('haViewModelModule');
	} catch (e) {
		module = angular.module('haViewModelModule', []);
	}

	module.factory('haViewModelSvc', [
		'$log',
		'$q',

		function ($log, $q) {

			var viewModels = {};
			var deferreds = {};

			var defer = function (vmName, deferred) {
				$log.debug('_defer', vmName);
				var d = deferreds[vmName] = deferreds[vmName] || [];
				d.push(deferred);
			};

			var resolve = function (vmName) {
				if (viewModels[vmName] == null) {
					return;
				}

				var ds = deferreds[vmName] || [];
				while (ds.length > 0) {
					$log.debug('_resolve', vmName);
					var d = ds.pop();
					d.resolve(viewModels[vmName]);
				}
			};

			var put = function (vmName, data) {
				viewModels[vmName] = data;
				resolve(vmName);
			};

			var get = function (vmName) {
				var d = $q.defer();

				if (viewModels[vmName] != null) {
					d.resolve(viewModels[vmName]);
				} else {
					defer(vmName, d);
				}

				return d.promise;
			};

			var getSync = function (vmName) {
				return viewModels[vmName];
			};

			return {
				put: put,
				get: get,
				getSync: getSync
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Hawaiian Secondary Sticky Header Service
	// ========================================
	//
	// * **Class:** haSecondaryStickyHeaderSvc
	// * **Author:** Nathan Probst
	//
	// Singleton used to add a secondary header to the sticky header.

	'use strict';

	var module = angular.module('haSecondaryHeaderModule', []);

	module.factory('haSecondaryHeaderSvc', [
		'$log',

		function () {

			var svc = {
				template: null,
				ctrl: {}
			};

			return svc;
		}
	]);

	module.controller('haSecondaryHeaderCtrl', [
		'$log',
		'$scope',
		'haSecondaryHeaderSvc',

		function ($log, $scope, svc) {
			return svc.ctrl;
		}
	]);

})(angular);
;
// Hawaiian Geo Data Service
// =========================
//
// * **Class:** haGeoDataSvc
// * **Author:** Nathan Probst
//
// Service for accessing geo data and logic

(function (angular) {
	'use strict';

	var module;
	try {
		module = angular.module('haGeoDataModule');
	} catch (e) {
		module = angular.module('haGeoDataModule', ['haHttpService']);
	}

	module.factory('haGeoDataSvc', [
		'$q',
		'$log',
		'$filter',
		'$cacheFactory',
		'haGeoDataAPI',

		function ($q, $log, $filter, $cacheFactory, api) {
			var cache = $cacheFactory('haGeoDataCache');
			var regexCache = $cacheFactory('regexCache');
			var activeCountry = null;
			var COUNTRY = 'COUNTRY';
			var STATES = 'STATES_';
			var CITIES = 'CITIES_';
			var COUNTRY_PHONE_CODES = 'COUNTRY_PHONE_CODES';

			var svc = {
				setCountryData: function (data) {
					data = $filter('orderBy')(data, ['Sorting', 'DisplayName']);
					return cache.put(COUNTRY, data);
				},
				setActiveCountry: function (country) {
					activeCountry = country;
				},
				getCountries: function () {
					var KEY = COUNTRY;
					var cached = cache.get(KEY);
					if (cached != null && cached.length > 0) {
						var d = $q.defer();
						d.resolve(cached);
						return d.promise;
					} else {
						return api.getCountries().then(function (data) {
							data = $filter('orderBy')(data, 'Sorting');
							return cache.put(KEY, data);
						});
					}
				},
				getStates: function (countryKey) {
					var KEY = STATES + countryKey;
					var cached = cache.get(KEY);
					if (cached != null) {
						var d = $q.defer();
						d.resolve(cached);
						return d.promise;
					} else {
						return api.getStates(countryKey).then(function (data) {
							return cache.put(KEY, data);
						});
					}
				},
				getStateAndCities: function (countryKey, postalCode) {
					var KEY = CITIES + countryKey + postalCode;
					var cached = cache.get(KEY);
					if (cached != null) {
						var d = $q.defer();
						d.resolve(cached);
						return d.promise;
					} else {
						return api.getStateAndCities(countryKey, postalCode).then(function (data) {
							return cache.put(KEY, data);
						});
					}
				},
				getAddressStyle: function (countryKey) {
					countryKey = countryKey || (activeCountry && activeCountry.Key);
					var cached = cache.get(COUNTRY);
					if (cached != null) {
						for (var i = 0; i < cached.length; i++) {
							var c = cached[i];
							if (c != null && c.Key === countryKey) {
								return c.AddressStyle || c.Iso2Code;
							}
						}
					}
					return null;
				},
				noStates: function (countryKey) {
					countryKey = countryKey || (activeCountry && activeCountry.Key);
					var cached = cache.get(COUNTRY);
					if (cached != null) {
						for (var i = 0; i < cached.length; i++) {
							var c = cached[i];
							if (c != null && c.Key === countryKey) {
								if (c.StateValidation === 'X') {
									return true;
								}
								break;
							}
						}
					}
					return false;
				},
				hasPostalCode: function (countryKey) {
					countryKey = countryKey || (activeCountry && activeCountry.Key);
					var cached = cache.get(COUNTRY);
					if (cached != null) {
						for (var i = 0; i < cached.length; i++) {
							var c = cached[i];
							if (c != null && c.Key === countryKey) {
								if (c.PostalCodeValidation === 'X') {
									return false;
								}
								break;
							}
						}
					}
					return true;
				},
				getPostalCodeRegex: function (countryKey) {
					countryKey = countryKey || (activeCountry && activeCountry.Key);

					// check the regex cache for this countryKey
					var re = regexCache.get(countryKey) || regexCache.put(countryKey, {});
					if (re.PostalCodeValidation) {
						return re.PostalCodeValidation;
					}

					var cached = cache.get(COUNTRY);
					if (cached != null) {
						for (var i = 0; i < cached.length; i++) {
							var c = cached[i];
							if (c != null && c.Key === countryKey) {
								if (c.PostalCodeValidation != null && c.PostalCodeValidation !== 'X') {
									// cache the generated regex to prevent infinite digest loops with ng-pattern
									re.PostalCodeValidation = new RegExp(c.PostalCodeValidation);
									return re.PostalCodeValidation;
								}
								break;
							}
						}
					}
					re.PostalCodeValidation = /.*/;
					return re.PostalCodeValidation;    // Match anything
				},
				getPhoneNumberRegex: function (countryKey) {
					countryKey = countryKey || (activeCountry && activeCountry.Key);

					// check the regex cache for this countryKey
					var re = regexCache.get(countryKey) || regexCache.put(countryKey, {});
					if (re.PhoneNumberValidation) {
						return re.PhoneNumberValidation;
					}

					var cached = cache.get(COUNTRY);
					if (cached != null) {
						for (var i = 0; i < cached.length; i++) {
							var c = cached[i];
							if (c != null && c.Key === countryKey) {
								if (c.PhoneNumberValidation != null) {
									// cache the generated regex to prevent infinite digest loops with ng-pattern
									re.PhoneNumberValidation = new RegExp(c.PhoneNumberValidation);
									return re.PhoneNumberValidation;
								}
								break;
							}
						}
					}
					re.PhoneNumberValidation = /.*/;
					return re.PhoneNumberValidation;    // Match anything
				},
				getCityRegex: function (countryKey) {
					countryKey = countryKey || (activeCountry && activeCountry.Key);

					// check the regex cache for this countryKey
					var re = regexCache.get(countryKey) || regexCache.put(countryKey, {});
					if (re.CityValidation) {
						return re.CityValidation;
					}

					var cached = cache.get(COUNTRY);
					if (cached != null) {
						for (var i = 0; i < cached.length; i++) {
							var c = cached[i];
							if (c != null && c.Key === countryKey) {
								if (c.CityValidation != null) {
									// cache the generated regex to prevent infinite digest loops with ng-pattern
									re.CityValidation = new RegExp(c.CityValidation);
									return re.CityValidation;
								}
								break;
							}
						}
					}
					re.CityValidation = /^[a-zA-Z- ]{0,30}$/;
					return re.CityValidation;   // Default
				},
				getPhoneCountryCodes: function () {
					var KEY = COUNTRY_PHONE_CODES;
					var cached = cache.get(KEY);
					if (cached != null && cached.length>0) {
						return cached;
					} else {
						var countries = cache.get(COUNTRY);
						var customFilter = function (country) {
							// USA > Tier 1 > Rest, All sorted by ISO Code
							return country.IsoCode === 'USA' ? 1 : (parseInt(country.CountryTier) === 1 ? 2 : 3);
						};
						countries = $filter('orderBy')(countries, [customFilter, 'IsoCode']);
						if (countries != null) {
							var data = [];
							for (var i = 0; i < countries.length; i++) {
								var code = countries[i].PhoneCountryCode;
								var iso = countries[i].IsoCode;
								var key = countries[i].Key;
								if (code > 0) {
									data.push({
										Code: code,
										Key: key,
										Name: '+' + code,
										Value: iso
									});
								}
							}
							return cache.put(KEY, data);
						} else {
							return [];
						}
					}
				},
				lookupCountryByCode: function (countryCode) {
					var cached = cache.get(COUNTRY);
					if (cached != null) {
						for (var i = 0; i < cached.length; i++) {
							var c = cached[i];
							if (c != null && c.IsoCode === countryCode) {
								return c;
							}
						}
					}
					return null;
				}
			};

			return svc;
		}
	]);

})(angular);
;
// Hawaiian Ancillaries Service
// ============================
//
// * **Class:** haAncillariesSvc
// * **Author:** Nathan Probst
//
// Service for accessing Itinerary Ancillaries data and logic

(function (angular) {
	'use strict';

	var module;
	try {
		module = angular.module('haAncillariesModule');
	} catch (e) {
		module = angular.module('haAncillariesModule', ['haHttpService']);
	}

	module.factory('haAncillariesSvc', [
		'$q',
		'$log',
		'$cacheFactory',
		'haAncillariesAPI',

		function ($q, $log, $cacheFactory, api) {
			var svc = {
				getNeatFallbackUrl: api.getNeatFallbackUrl,
				getAncillaries: api.getAncillaries,
				getAncillariesAsync: api.getAncillariesAsync,
				getVacationPackageList: api.getVacationPackageList,
				getRepriceHotelDetails: api.getRepriceHotelDetails,
				addHotel: api.addHotel,
				removeHotel: api.removeHotel,
				setRooms: api.setRooms,
				addRentalCar: api.addRentalCar,
				getRentalTerms: api.getRentalTerms,
				removeRentalCar: api.removeRentalCar,
				searchCarRentals: api.searchCarRentals,
				addAirportShuttle: api.addAirportShuttle,
				removeAirportShuttle: api.removeAirportShuttle,
				addLeiGreeting: api.addLeiGreeting,
				removeLeiGreeting: api.removeLeiGreeting,
				getGiftCardState: api.getGiftCardState,
				addTripInsurance: api.addTripInsurance,
				removeTripInsurance: api.removeTripInsurance
			};

			return svc;
		}
	]);

})(angular);
;
(function (angular) {

	// Hawaiian RegExp Service
	// =================================
	//
	// * **Class:** haRegexService
	// * **Author:** Jamie Perkins
	//
	// Centralized regexes

	'use strict';

	var module = angular.module('haRegexModule', []);

	module.factory('haRegexService', function () {

	    var pnr = /^[a-zA-Z0-9]{6}$/;
		var confirmationCode = /^[0-9]{13}$/;
		var owwConfCode = /^(((PB|pb|Pb|pB)[a-zA-Z0-9]{13,13})|\d{12,14})$/;
		var hmNumber = /^(\d){9}$/;
		var email = /^[\w\d\.\-]+@[a-zA-Z\d\.\-]+\.[a-zA-Z]{2,15}$/; // test here: http://regexr.com/3c0b8
		var username = /^(?=.*[a-zA-Z])([a-zA-Z0-9\.]{6,30})$/;
		var regexObj = {
		    alphaNumeric: /[a-zA-Z0-9- ]/gi,
		    hmNumber: hmNumber,
		    email: email,
		    phone: /[0-9]{10,15}/,
		    name: /^[a-zA-Z-\s]{0,30}$/,
			addressLine: /^[a-zA-Z0-9-\.\,\/\\ ]{0,29}$/,
		    city: /^[a-zA-Z-\s]{0,30}$/,
		    state: /^[a-zA-Z0-9- ]{0,30}$/,
		    username: username,
		    login: new RegExp(hmNumber.source + '|' + email.source + '|' + username.source),
		    password: /^(?=.*[a-z])(?=.*\d)(?=.*[A-Z]).{10,16}$/, // http://regexr.com/3c65b
		    usernameOrHmNumber: new RegExp(hmNumber.source + '|' + username.source),
		    creditCard: /^[0-9]{15,16}$/, // note that ha-validate-card-type does the heavy lifting
		    redress: /^[a-zA-Z0-9]{0,13}$/, // as per PO updated the minimum character restriction
		    knownTraveler: /^[a-zA-Z0-9]{0,25}$/, // as per PO updated the minimum character restriction		    
		    jalFlightNumber: /(^(JL|NU)?[0-9]{1,4}$)|^[0-9]{1,4}$/i,//Jal and JTA specific flight num validation
		    flightStatusByNumber: /^(ha|HA)?[0-9]{1,4}$/,
		    pnr: pnr,
		    confirmationCode: confirmationCode,
		    owwConfCode: owwConfCode,
		    pnrOrConfCode: new RegExp(pnr.source + '|' + confirmationCode.source),
		    pnrOrConfOrOwwConfCode: new RegExp(pnr.source + '|' + confirmationCode.source + '|' + owwConfCode.source),
		    maikaiNumber: /^[0-9]{12}/, // also validated in ha-foodland-registration.js
		    agentCode: /^[a-zA-Z0-9]{1,10}$/,
		    giftcardnumber: /\d{19}/,
		    giftcardpin: /\d{4}/,
		    chinaRewardMemberShipNumber: /^(cr|CR)\d{16}$/,
		    ticketOrMSR: /^\d{13}$/,
		    cashbagNum: /^\d{16}$/,
			promoCode: /^[a-zA-Z0-9_-]{1,15}$/,
			carMembershipNumber: /^[a-zA-Z0-9]{6}$/,
			carDiscountNumber: /^[a-zA-Z]{1}[0-9]{6}$/,
			carCouponNumber: /^[a-zA-Z]{4}[0-9]{3}$/,
			flightNumber: /^[0-9]{1,5}$/
		};

		return regexObj;
	});

	module.run([
		'$rootScope',
		'haRegexService',

		// attach regexes to root scope
		function ($rootScope, svc) {
			$rootScope.$regex = svc;
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Seat Map Service
	// --------------------------------------------
	//
	// * **Class:** haPaymentTypesService
	// * **Author:** Cory Shaw
	//
	// Service for managing payment types


	'use strict';

	var mod = angular.module('haPaymentTypesService', []);

	mod.service('haPaymentTypesService', ['haPaymentAPI', '$log', function (haPaymentAPI, $log) {

		var service = {

			// This call should be made on pageload and ANYTIME changes are made to anceilaries on a payment page.
			updatePaymentTypes: function () {
				service.getAvailablePaymentTypes();
			},
			getAvailablePaymentTypes: function () {
				haPaymentAPI.getPaymentMethodsState().then(function (data) {
					service.setPaymentTypes(data);
				}, function (reason) {
					$log.error('getPaymentMethodsState API call Failed: ' + reason);
				});
			},

			// handle the response and set payment types
			setPaymentTypes: function (data) {
				var type = data && data.data;

				// show or hide payment methods as a whole
				if (!type.IsPaymentRequired) {
					service.enablePaymentMethods = false;
					return;
				} else {
					service.enablePaymentMethods = true;
				}

				service.paymentTypesState = type.PaymentMethods;

				// if only credit/debit option visible, hide the payment type radios
				if (service.paymentTypesState.CreditDebit.Enabled && !service.paymentTypesState.Gift.Visible && !service.paymentTypesState.MasterPass.Visible && !(enableAliPay && $language.toLowerCase() === 'zh-cn')) {
					service.hidePaymentTypeRadios = true;
				}

				// if credit/debit option the only available, select it by default
				if (service.paymentTypesState.CreditDebit.Enabled && !service.paymentTypesState.Gift.Enabled && !service.paymentTypesState.MasterPass.Enabled) {
					service.paymentMethod = 'creditDebit';
				}

				if (type.IsBarclayReturned) {
					service.paymentMethod = 'creditDebit';
				}

			}
		};

		return service;
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	//TODO: Move all share method from ha-pax-controller and ha-manage-travelers-corporate-controller here
	angular.module('haPaxServiceModule', [])
	.factory('haPaxService', [
		function () {

			var selectedTravelersList = [];

			function selectedTravelers() {
				var setList = function (list) {
					selectedTravelersList = list;
				};
				var getList = function () {
					return selectedTravelersList;
				};
				return {
					get: getList,
					set: setList
				};
			}

			return {
				selectedTravelers: selectedTravelers
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Ha AriaLive Service
	// --------------------------------------------
	//
	// * **Class:** haAriaLiveService
	// * **Author:** Cory Shaw
	//
	// Service for updating and destroying the content of the aira-live element for screen readers


	'use strict';

	var mod = angular.module('haAriaLiveModule', []);

	mod.service('haAriaLiveService', ['$rootScope', '$timeout', '$log', function ($rootScope, $timeout) {

		var service = {

			updateMessage: function (message) {
				// $log.log('AriaLive Service Update Message called');
				$rootScope.ariaLiveMessage = message;
				// destroy the message after 2 seconds so that if it's updated with the same information again, the screen reader will read it off again.
				$timeout(function () {
					$rootScope.ariaLiveMessage = '';
				}, 5000);
			}

		};

		return service;
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var mod;
	try {
		mod = angular.module('haVerticalSeatmapModule');
	} catch (e) {
		mod = angular.module('haVerticalSeatmapModule', ['ui.router']);
	}

	// Author: Jamie Perkins
	mod.service('haVerticalSeatmapService', ['$log', '$filter', 'haHttpService', '$q', '$timeout', '$interval', '$rootScope', 'haModal', 'haConfig', 'haSitecoreStrings', 'haUtils', 'haCitiesSvc', function ($log, $filter, $http, $q, $timeout, $interval, $rootScope, haModal, haConfig, $scs, haUtils, haCitiesSvc) {
		var service = {
			legs: [],
			activeLegIndex: 0,
			selectedPassengerIndex: 0,
			pageLoading: true,
			seatMapLoading: false,
			passengerSeatSelection: [],
			seatChargeSubtotals: [],
			isForSelection: true,
			error: null,
			strings: {},
			lastNameFirst: ($rootScope.$language === 'ja-jp' || $rootScope.$language === 'ko-kr' || $rootScope.$language === 'zh-cn' || $rootScope.$language === 'zh-tw'),
			svgScaleFactor: 1,
			reshopSeatCredits: [],
			promiseResponses: {},
			currency: $rootScope.$currency,

			seatTypeFilters: {
				standard: {
					label: 'Standard',
					value: true
				},
				extra: {
					label: 'Extra Comfort',
					value: true
				},
				flat: {
					label: 'Lie-Flat',
					value: true
				},
				exit: {
					label: 'Exit Row',
					value: true
				},
				first: {
					label: 'First Class',
					value: true
				},
				preferred: {
					label: 'Preferred',
					value: true
				}

			},

			handleError: function (error) {
				$log.error(error);
				service.error = error;
			},

			// backend has mis-matched seat types. For booking/reshop, seats come down as EC = 1, PS = 2,
			// but they must be posted EC = 2, PS = 1. For Change seats, they come down and post consistently.
			getSeatType: function (type) {
				if (type === 1) {
					return 2;
				}
				else if (type === 2) {
					return 1;
				}
				else {
					return 0;
				}
			},

			// fetch seat map page vm, expected to be in root scope thanks to haInFlightOptionsController
			getFlightVM: function () {

				$scs.get('InFlightOptionsInfo').then(function (data) {
					service.strings = data;
				});

				// '/Date(1420009200000)/' -> Date object (needed for change seats model)
				function extractDate(datestring) {
					var time = datestring.match(/[\d]+/);
					time = Number(time[0]);
					return new Date(time);
				}

				function cabinNameToClass(cabinName) {
					switch (cabinName) {
						case 2:
							return 'first';
						case 3:
							return 'business';
						case 0:
						case 1:
						case 4:
							return 'coach';
						default:
							$log.error('Unknown CabinName:', cabinName);
							return '';
					}
				}

				// '09:15' -> '9:15a' (for change seats)
				function formatTime(timeString) {
					var time = timeString.split(':'),
						hours = parseInt(time[0]),
						pm = hours > 12,
						ampm = 'a';
					if (pm) {
						hours -= 12;
						ampm = 'p';
					}
					return hours + ':' + time[1] + ampm;
				}

				function seatTypeStringToType(type) {
					switch (type) {
						case 'ExtraComfort':
						case 'ec':
							return 1;
						case 'Preferred':
						case 'preferred':
							return 2;
						default:
							return 0;
					}
				}

				function checkPaxForInfants(passengers) {
					var hasInfant = false;
					angular.forEach(passengers, function (pax) {
						if (pax.Type && (pax.Type.toLowerCase() === 'infant')) {
							hasInfant = true;
						}
					});
					return hasInfant;
				}

				service.disableSeatUpgrades = $rootScope.IsDisableSeatUpgrade;
				service.disableExitRows = $rootScope.IsDisableExitRows;

				// booking or reshop
				if ($rootScope.TripSummary) {

					// get "legs" - each segment from each trip
					angular.forEach($rootScope.TripSummary.Trips, function (trip) {
						angular.forEach(trip.Flights[0].Legs, function (leg) {
							leg.SelectedFareClass = trip.Flights[0].SelectedFareClass.toLowerCase();
							//leg.Cabin = leg.ClassOfService[0].Cabin.toLowerCase(); // dupe of fare class
							service.legs.push(leg);
							service.seatChargeSubtotals.push(0);
						});
					});
					// passengers
					service.passengers = $rootScope.TripSummary.Passengers;
					// currency
					service.currency = $rootScope.TripSummary.currency;

					if ($rootScope.PassengerTripSummary && $rootScope.PassengerTripSummary.Passengers) {
						angular.forEach(service.passengers, function (pax, i) {
							var first = $rootScope.TripSummary.Passengers[i].FirstName,
								last = $rootScope.TripSummary.Passengers[i].LastName;
							// extend pax info with PassengerTripSummary.Passengers for:
							// - HM account number
							// - Make Trip summary reflect seat position
							angular.extend(pax, $rootScope.PassengerTripSummary.Passengers[i]);
							// PassengerTripSummary can get polluted with cached pax names from a previous flight search
							pax.FirstName = first;
							pax.LastName = last;

							// if reshop, wipe out current seat selections
							if ($rootScope.IsChangeFlightBooking && pax.Seats && pax.Seats.length) {
								angular.forEach(pax.Seats, function (paxSeat) {
									paxSeat.SeatLocation = null;
									paxSeat.Type = 0;
									paxSeat.SeatAmount = 0;
								});
							}
							if (!pax.Seats) {
								pax.Seats = [];
								angular.forEach(service.legs, function (leg, j) {
									pax.Seats.push({ SegmentID: j + 1 });
								});
							}

						});

					}
					// make sure a seat object exists for every leg and every pax
					angular.forEach(service.passengers, function (pax) {
						if (!pax.Seats) {
							pax.Seats = [];
						}
						angular.forEach(service.legs, function (leg, i) {
							if (!pax.Seats[i]) {
								pax.Seats.push({
									SegmentID: i + 1,
									SeatLocation: null,
									SeatAmount: 0,
									Type: 0
								});
							}
						});
					});

					// reshop
					if ($rootScope.IsChangeFlightBooking) {
						var reshopflightSearchCookie = haUtils.getReshopFlightQueryModelCookie();
						$log.debug('reshop cookie', reshopflightSearchCookie);
						service.reshopPNR = reshopflightSearchCookie.PNR_ID;
						service.reshopSegmentsIDs = [];
						angular.forEach(reshopflightSearchCookie.OldFlightSearchSegmentList, function (segment) {
							service.reshopSegmentsIDs.push(segment.SegmentID);
						});


						// fix names
						angular.forEach(service.passengers, function (pax) {
							pax.FirstName = service.convertToSentenceCase(pax.FirstName);
							pax.LastName = service.convertToSentenceCase(pax.LastName);
						});

						// get original trip
						var url = '/api/trip/' + service.reshopPNR + '/' + service.passengers[0].LastName
						$http.GET(url).success(function (data, status, headers) {
							$log.debug('trip data', data);

							angular.forEach(data.segments, function (segment, i) {
								angular.forEach(segment, function (origLeg) {

									// determine seats to persist
									angular.forEach(service.legs, function (reshopLeg, j) {

										// same leg & fare class?
										var reshopFlight = reshopLeg.OperatingCarrier + '-' + reshopLeg.FlightNumber,
											origLegDeparture = moment(origLeg.depart).format('YYYY-MM-DD'),
											origFare = origLeg.cabin;
										if (origFare === 'extracomfort') {
											origFare = 'coach';
										}
										if (origLeg.flight === reshopFlight && origLegDeparture === reshopLeg.DepartureDate && origFare === reshopLeg.SelectedFareClass) {
											// same flight: persist original seat
											angular.forEach(origLeg.seats, function (seat) {
												angular.forEach(service.passengers, function (pax) {
													if (seat.traveler === pax.TravelerAssociationID) {
														$log.debug('same flight - seat ' + seat.id + ' persisted');
														pax.Seats[j].SeatLocation = seat.id;
														pax.Seats[j].SeatAmount = seat.cost;
													}
												});
											});
										}
									});
									// find seat credits on reshoped segments

									var isReshopedSegment = service.reshopSegmentsIDs.indexOf(i + 1) !== -1;
									angular.forEach(origLeg.seats, function (seat) {
										if (isReshopedSegment && seat.cost > 0) {
											service.reshopSeatCredits.push({
												type: seatTypeStringToType(seat.type),
												price: seat.cost,
												isExtraComfort: (seat.type === 'ec'),
												isPreferred: (seat.type === 'preferred'),
												TravelerAssociationID: seat.traveler
											});
										}
									});
								});
							});

							service.disableRow4 = checkPaxForInfants(service.passengers);
							service.pageLoading = false;

						}).error(function (data, status/*, headers, config*/) {
							// fail silently
							$log.error('Error getting original trip data: ' + status);

							service.disableRow4 = checkPaxForInfants(service.passengers);
							service.pageLoading = false;
						});
					}
					else {

						service.disableRow4 = checkPaxForInfants(service.passengers);
						service.pageLoading = false;
					}
				}

				// change seats - completely different model ¯\_(ツ)_/¯
				else if ($rootScope.AirGroup) {

					service.isChangeSeats = true;
					// legs
					angular.forEach($rootScope.AirGroup.Flight, function (flight) {
						// map missing properties
						var carrier = flight.OperatingCarrier.AirlineCode;
						if (carrier === 'EM') { carrier = 'HA'; }
						var leg = {
							FlightNumber: flight.Carrier.FlightNumber.Value,
							OperatingCarrier: carrier,
							DepartureDate: moment(extractDate(flight.Departure.Date)).format('MM/DD/YYYY'),
							DepartureTime: formatTime(flight.Departure.Time),
							DepartureCityCode: flight.Departure.AirportCode,
							DepartureCity: flight.Departure.City,
							ArrivalCityCode: flight.Arrival.AirportCode,
							ArrivalCity: flight.Arrival.City,
							Duration: flight.FlightDuration,
							ClassOfService: [{ ClassValue: flight.ClassOfService }],
							IsCodeShare: flight.IsCodeshare,
							SelectedFareClass: cabinNameToClass(flight.CabinName),
							ElementNumber: parseInt(flight.ElementNumber)
							//Cabin: codeToClass(flight.ClassOfService)
						};
						service.legs.push(leg);
					});

					// pax and seats
					service.passengers = [];
					angular.forEach($rootScope.Traveler, function (passenger, i) {
						if (service.paxOkayToAdd(passenger.ElementNumber)) {
							var pax = {
								type: 'Adult', // hardcoded because there is no way to check age with given info.
								isUser: (i === 0),
								FirstName: service.convertToSentenceCase(passenger.TravelerName.GivenName),
								LastName: service.convertToSentenceCase(passenger.TravelerName.Surname),
								Id: passenger.ElementNumber,
								TravelerAssociationID: passenger.ElementNumber
							};
							pax.Seats = [];
							angular.forEach(service.legs, function (leg, i) {
								var paxLegSeat = { SegmentID: i + 1 }
								// if existing seats - map to pax and leg
								if ($rootScope.Seat && $rootScope.Seat.length) {

									service.currency = $rootScope.Seat[0].CurrencyCode;

									angular.forEach($rootScope.Seat, function (seat) {

										if (parseInt(seat.SegmentElementNumber.Value) === leg.ElementNumber && seat.Assignment.TravelerElementNumber.Value === pax.Id) {
											var seatName = seat.Assignment.SeatLocation;
											if (seatName.indexOf('0') === 0) {
												seatName = seatName.substr(-2);
											}
											paxLegSeat.SeatLocation = seatName;
											paxLegSeat.Type = service.getSeatType(seat.SeatType);
											paxLegSeat.SeatAmount = seat.SeatAmount;
											paxLegSeat.originalSeat = {
												type: service.getSeatType(seat.SeatType),
												price: seat.SeatAmount,
												credit: seat.SeatCredit,
												isExtraComfort: (seat.SeatType === 2),
												isPreferred: (seat.SeatType === 1)
											};
										}
									});
								}
								pax.Seats.push(paxLegSeat);
							});
							service.passengers.push(pax);
						}
					});

					service.pageLoading = false;

				}
				// expert booking path
				else if ($rootScope.ExpertBookingSeatmapData) {
					// legs
					service.legs = [];
					angular.forEach($rootScope.ExpertBookingSeatmapData.segments, function(segment) {
						angular.forEach(segment.legs, function(leg) {
							var serviceLeg = {
								FlightNumber: leg.flight,
								OperatingCarrier: leg.carrier,
								DepartureDate: moment(leg.departure).format('MM/DD/YYYY'),
								DepartureTime: formatTime(moment(leg.departure).format('h:mma')),
								DepartureCityCode: leg.origin,
								DepartureCity: haCitiesSvc.getCityByCodeSync(leg.origin).LongDescription,
								ArrivalCityCode: leg.destination,
								ArrivalCity: haCitiesSvc.getCityByCodeSync(leg.destination).LongDescription,
								Duration: $filter('formatMinutes')(leg.duration),
								//ClassOfService: [{ ClassValue: flight.ClassOfService }],
								IsCodeShare: (leg.operator && leg.operator !== "HA"),
								SelectedFareClass: leg.cabin,
								//ElementNumber: parseInt(flight.ElementNumber)
								//Cabin: codeToClass(flight.ClassOfService) // Maybe not needed
								id: leg.id,
								disableFirstClass: $rootScope.ExpertBookingSeatmapData.existingPNR && leg.cabin !== 'FIRST',
								disableMainCabin: leg.cabin === 'FIRST'
							};
							service.legs.push(serviceLeg);
						});
					});

					// pax and seats
					service.passengers = [];
					angular.forEach($rootScope.ExpertBookingSeatmapData.travelers, function(traveler) {
						var pax = {
							type: traveler.type,
							isUser: traveler.hmnumber && traveler.hmnumber == $rootScope.user.haMiles,
							FirstName: traveler.firstname,
							LastName: traveler.lastname,
							TravelerID: traveler.travelerid,
							ID: traveler.id
							// TravelerAssociationID: passenger.ElementNumber
						};

						pax.Seats = [];
						angular.forEach(service.legs, function (leg, i) {
							var paxLegSeat = { SegmentID: i + 1 }
							// if existing seats - map to pax and leg
							if (traveler.seats && traveler.seats.length) {

								service.currency = $rootScope.ExpertBookingSeatmapData.currency;

								angular.forEach(traveler.seats, function (seat) {

									if (seat.id === leg.id) {
										var seatName = seat.seat;
										paxLegSeat.SeatLocation = seatName;
										paxLegSeat.Type = seatTypeStringToType(seat.type);
										paxLegSeat.SeatAmount = seat.amount / 100; // ExpertBooking prices are in cents
										paxLegSeat.originalSeat = {
											type: seatTypeStringToType(seat.type),
											price: seat.amount / 100, // ExpertBooking prices are in cents
											isExtraComfort: seatTypeStringToType(seat.type) === 1,
											isPreferred: seatTypeStringToType(seat.type) === 2
										};
									}
								});
							}
							pax.Seats.push(paxLegSeat);
						});
						service.passengers.push(pax);

					});

					// Seat restrictions
					if ($rootScope.ExpertBookingSeatmapData.restrictions) {
						service.disableSeatUpgrades = $rootScope.ExpertBookingSeatmapData.restrictions.blockseatupgrades;
						service.disableExitRows = $rootScope.ExpertBookingSeatmapData.restrictions.blockexitrows;
					}

					// Make a copy of the initial passenger seats in order to see if they change
					service.passengersInitial = angular.copy(service.passengers);
					service.pageLoading = false;
				}
				else {
					var error = 'View model is missing from root scope.';
					service.handleError(error);
					service.pageLoading = false;
				}
			},

			paxOkayToAdd: function(paxId) {
				if ($rootScope.infantLapList && $rootScope.infantLapList.indexOf(paxId) > -1) {
					return false;
				}
				if ($rootScope.groupPNRSelectedPax && $rootScope.groupPNRSelectedPax.length > 0 && $rootScope.groupPNRSelectedPax.indexOf(paxId) === -1) {
					return false;
				}
				return true;
			},

			// for reshop, apply seat credits where they match the upgrade available for the leg
			// must fire AFTER seatmap renders, in order to know leg upgrade type
			applyAnyAvailableSeatCredits: function(legIndex) {

				var legUpgradeType = 'none';
				if (service.legs[legIndex].seatModel && (service.legs[legIndex].seatModel.hasPreferred || service.legs[legIndex].seatModel.hasMainCabinPreferred)) {
					legUpgradeType = 'preferred';
				}
				else if (service.legs[legIndex].seatModel && service.legs[legIndex].seatModel.hasExtraComfort) {
					legUpgradeType = 'extraComfort';
				}

				if (service.reshopSeatCredits.length) {

					angular.forEach(service.reshopSeatCredits, function (credit) {
						if (!credit.used && ((legUpgradeType === 'preferred' && credit.isPreferred) ||
							(legUpgradeType === 'extraComfort' && credit.isExtraComfort))) {
							// matching credit found. find matching pax to assign credit to
							angular.forEach(service.passengers, function (pax) {
								var paxSeat = pax.Seats[legIndex];
								if (credit.TravelerAssociationID === pax.TravelerAssociationID && !paxSeat.originalSeat) {
									paxSeat.originalSeat = angular.copy(credit);
									paxSeat.IsCredit = true;
									paxSeat.SurchargeOk = true;
									credit.used = true;
								}
							});
						}
					});
				}
			},

			// establish seatmap VM for seatmap preview
			initPreviewSegments: function (segments, market, disableSeatUpgrades) {

				service.isForSelection = false;
				service.disableSeatUpgrades = disableSeatUpgrades;
				service.legs = []; // reset legs

				$scs.get('InFlightOptionsInfo').then(function (data) {
					service.strings = data;
				});

				var IsInternational = Boolean(market > 2);
				// get legs
				angular.forEach(segments, function (leg) {
					// map missing properties
					if (leg.OperatingAirline === 'EM') {
						leg.IsCodeShare = false;
						leg.OperatingCarrier = 'HA';
					} else {
						leg.OperatingCarrier = leg.OperatingAirline;
					}

					leg.DepartureDate = moment(leg.DepartureDateTime).format('MM/DD/YYYY');
					leg.DepartureCityCode = leg.DepartureCityCode || leg.OriginCityCode;
					leg.DepartureCity = leg.DepartureCity || leg.OriginCityName;
					leg.DepartureTime = moment(leg.DepartureDateTime).format('h:mma');
					leg.ArrivalCityCode = leg.ArrivalCityCode || leg.DestinationCityCode;
					leg.ArrivalCity = leg.ArrivalCity || leg.DestinationCityName;
					leg.IsInternational = IsInternational;
					service.legs.push(leg);
				});

				service.pageLoading = false;
			},

			// returns seats post object which for some reason is completely different than the vm
			createSeatsPostObject: function () {
				var postObject = {
					InFlightOptions: [],
					Legs: [],
					TotalPremiumSeatAmount: 0
				};

				angular.forEach(service.legs, function (leg, i) {

					var legObject = {
						AirlineCode: leg.OperatingCarrier,
						ArrivalCityCode: leg.ArrivalCityCode,
						ClassOfService: leg.ClassOfService[0].ClassValue,
						DepartureCityCode: leg.DepartureCityCode,
						DepartureDate: leg.DepartureDate,
						DepartureTime: leg.DepartureTime,
						FlightNumber: leg.FlightNumber,
						IsCodeShare: Boolean(leg.IsCodeShare),
						SeatSelections: {
							ExtraComfortSelectionsCount: 0,
							PreferredSeatSelectionsCount: 0,
							InFlightOptions: [],
							Seats: []
						}
					};
					// TODO: revisit for VSP
					var extraComfortFee = 0;
					var preferredSeatFee = 0;
					if (leg.ExtraComfortFee) {
						extraComfortFee = leg.ExtraComfortFee;
					}
					if (leg.PreferredSeatFee) {
						preferredSeatFee = leg.PreferredSeatFee;
					}
					else if (leg.seatModel && leg.seatModel.extraComfortPrice) {
						extraComfortFee = leg.seatModel.extraComfortPrice;
					}
					else if (leg.seatModel && leg.seatModel.preferredSeatPrice) {
						preferredSeatFee = leg.seatModel.preferredSeatPrice;
					}

					if (service.isChangeSeats) {
						legObject.ElementNumber = i + 1;
					} else {
						legObject.SelectedFareClass = leg.SelectedFareClass;
						legObject.ExtraComfortFee = extraComfortFee;
						legObject.PreferredSeatFee = preferredSeatFee;
						legObject.IsExtraComfortAvailable = leg.IsExtraComfortAvailable;
					}
					angular.forEach(service.passengers, function (pax) {
						var paxSeat = pax.Seats[i];
						legObject.SeatSelections[pax.TravelerAssociationID] = paxSeat.SeatLocation;
						var seat = {
							Flight: leg.OperatingCarrier + leg.FlightNumber,
							PaxId: pax.TravelerAssociationID,
							SeatAmount: paxSeat.SeatAmount,
							SeatName: paxSeat.SeatLocation,
							SeatType: service.getSeatType(paxSeat.Type),
							SelectedFareClass: leg.SelectedFareClass,
							ExtraComfortFee: extraComfortFee,
							PreferredSeatFee: preferredSeatFee,
							IsExtraComfortAvailable: leg.IsExtraComfortAvailable
						};
						if (service.isChangeSeats) {
							seat.InFlightOptions = [];
						}
						if (service.includeSeatNumber) {
							seat.SeatNumber = paxSeat.SeatLocation;
						}
						// upgrades
						if (paxSeat.Type > 0) {
							var descr = '';

							if (paxSeat.Type === 2) {
								descr = service.strings['preferredseattext'];
								legObject.SeatSelections.PreferredSeatSelectionsCount++;
							}
							else if (paxSeat.Type === 1) {
								descr = service.strings['extracomfortupgradetext'];
								legObject.SeatSelections.ExtraComfortSelectionsCount++;
							}
							var seatUpgrade = {
								Description: descr,
								Flight: leg.OperatingCarrier + leg.FlightNumber,
								Price: paxSeat.SeatAmount,
								SeatName: paxSeat.SeatLocation
							};
							if (!service.isChangeSeats) {
								if ((paxSeat.Type === 1 && leg.SelectedFareClass !== 'extracomfort') || (paxSeat.Type === 2 && leg.SelectedFareClass !== 'preferred')) {
									legObject.SeatSelections.InFlightOptions.push(seatUpgrade);
									postObject.InFlightOptions.push(seatUpgrade);
									postObject.TotalPremiumSeatAmount += paxSeat.SeatAmount;
								}
							}
							else if (service.isChangeSeats && paxSeat.originalSeat && seat.SeatAmount > paxSeat.originalSeat.price) {
								seat.InFlightOptions.push(seatUpgrade);
							}
						}
						// downgrades
						if (paxSeat.DowngradeOk && paxSeat.SeatAmount === 0 && !$rootScope.IsChangeFlightBooking) {
							var seatDowngrade = {
								Description: service.strings['extracomfortdowngradetext'],
								SeatName: paxSeat.SeatLocation,
								Flight: leg.OperatingCarrier + leg.FlightNumber,
								Price: paxSeat.SeatAmount,
							}
							seat.IsDowngrade = true;
							legObject.SeatSelections.InFlightOptions.push(seatDowngrade);
							postObject.InFlightOptions.push(seatDowngrade);
						}
						legObject.SeatSelections.Seats.push(seat);
					});

					postObject.Legs.push(legObject);
				});
				return postObject;
			},

			// gets seats with API v2
			getSeatsForLeg: function (legIndex) {

				var deferred = $q.defer();

				// construct url
				var url = '/api/seats/';
				//url += service.legs[legIndex].AirlineCode + '-';
				url += 'ha-'; // currently we can only get seat data for HA flights
				url += service.legs[legIndex].FlightNumber + '/';
				url += moment(service.legs[legIndex].DepartureDate).format('YYYY-MM-DD');
				var query = {
					o: service.legs[legIndex].DepartureCityCode,
					d: service.legs[legIndex].ArrivalCityCode,
					currency: $rootScope.$currency,
					time: service.legs[legIndex].DepartureTime,
					arrivalTime: service.legs[legIndex].ArrivalTime,
					arrivalDate: moment(service.legs[legIndex].ArrivalDate).format('YYYY-MM-DD'),
				};
				query.hm = service.getHMNumbers();
				url += '?' + $.param(query);

				$http.GET(url).success(function (data, status, headers) {
					if (!service.legs[legIndex].EquipmentCode) {
						service.legs[legIndex].EquipmentCode = headers('X-Plane-Type');
					}
					service.legs[legIndex].seatModel = {
					    seats: data.SeatDetails,
					    disableSeatUpgradeFlx: data.DisableSeatUpgradeFlx
					};
					service.legs[legIndex].missingEquipmentCodeErrMessage = data.MissingEquipmentCodeErrMessage;
					// in some rare cases, seat data comes back as an empty object. Show an error in that case.
					if (Object.getOwnPropertyNames(data).length === 0) {
						service.noSeatsAvailableForLeg(legIndex);
					}
					deferred.resolve();

				}).error(function (data, status/*, headers, config*/) {
					deferred.reject('Error getting seat availability data: ' + status);
				});

				return deferred.promise;

			},

			// gets list of HM numbers
			getHMNumbers: function () {
				var hmNums = [];
				if ($rootScope.isLoggedIn && $rootScope.user && $rootScope.user.haMiles) {
					hmNums.push($rootScope.user.haMiles);
				}
				angular.forEach(service.passengers, function (pax) {
					if (pax.AccountNo) {
						hmNums.push(pax.AccountNo);
					}
				});

				return hmNums.join(',');
			},

			setUpgradableSeat: function (available) {
				//set a session variable when there are upgradable seats
				var deferred = $q.defer();

				$http.POST('/book/shared/SetUpgradeSeat?available=' + available)
					.success(function () {
						deferred.resolve();
					})
					.error(function () {
						deferred.resolve();
					});
				return deferred.promise;
			},

			upgradeToMainCabin: function () {
				return $http.POST('/Book/InFlightOptions/UpgradeToMainCabin');
			},

			getSeatsForChangeSeats: function (legIndex) {

				var deferred = $q.defer();

				var postObject = service.createSeatsPostObject();
				//$log.debug('request seats for change seats', postObject.Legs[legIndex]);

				$http.POST('/MyAccount/PostPurchaseEditSeats/GetSeatMap', {
					seatMapRequest: postObject.Legs[legIndex]
				}).success(function (data, status, headers) {
					service.legs[legIndex].EquipmentCode = headers('X-Plane-Type');
					service.BlockRowFour = data.BlockRowFour;
					var seats = {};
					var seatsAvailable = 0;
					angular.forEach(data.rows, function (row) {
						if (row.number && row.number.length) {
							angular.forEach(row.cols, function (col) {
								if (col.isSeat && col.available) {
									seatsAvailable ++;
									var price = 0;
									if (col.extraComfortSeatPrice && col.extraComfortSeatPrice > 0) {
										price = col.extraComfortSeatPrice;
									}
									else if (col.preferredSeatPrice && col.preferredSeatPrice > 0) {
										price = col.preferredSeatPrice;
									}
									seats[col.name] = { price: price };
								}
							});
						}
					});
					service.legs[legIndex].seatModel = {
						seats: seats
					};
					if (!seatsAvailable) {
						service.noSeatsAvailableForLeg(legIndex);
					}



					deferred.resolve();

				}).error(function () {
					deferred.reject('Error getting seat availability data: ' + status);
				});

				return deferred.promise;
			},

			// displays an error message and hides the seat map when no seats are available for the given flight
			noSeatsAvailableForLeg: function (legIndex) {
				service.legs[legIndex].seatModel = {
					seatsUnavailable: true
				};
				// this string is not availble on some pages so we have to fetch it
				$scs.get('InFlightOptions.NoSeatsAvailableContent').then(function(string){
					service.noSeatsAvailableErrorMessage = string;
				});
			},

			// decides which endpoint to get seat data and equipment type, returns promise
			getAvailableSeatsForLeg: function (legIndex) {

				$log.debug('get available seats for index:', legIndex);
				// already resolved?
				if (service.legs[legIndex].seatModel) {
					var deferred = $q.defer();
					deferred.resolve();
					return deferred.promise;
				}

				// redundant check for codeshare
				if (service.legs[legIndex].IsCodeShare) {
					var deferred = $q.defer();
					deferred.reject('Can\'t get available seats for codeshare flight.');
					return deferred.promise;
				}

				if (service.isChangeSeats) {
					return service.getSeatsForChangeSeats(legIndex);
				}
				else {
					return service.getSeatsForLeg(legIndex);
				}

			},

			// save duplicate call to a promise
			loadGetPromise: function (url) {
				if (!service.promiseResponses[url]) {
					var deferred = $q.defer();
					service.promiseResponses[url] = deferred.promise;
					$http.GET(url).success(function (response) {
						deferred.resolve(response);
					}).error(function (data, status) {
						deferred.reject(status);
					});
				}
				return service.promiseResponses[url];
			},

			// avoid calling the same endpoint twice
			loadPlaneConfiguration: function () {
				//Getting SVG info from Sitecore
				var equipmentUrl = '/planes/configuration';
				return service.loadGetPromise(equipmentUrl);
			},

			// always loads svg once for then entire flight result
			loadSvgByUrl: function (url) {
				return service.loadGetPromise(url);
			},

			// always loads svg into an element with the id 'svgSeatmap0' where 0 = legIndex
			// returns promise
			loadSvgSeatmapForLeg: function (legIndex) {

				var deferred = $q.defer();

				// skip request if it's already loaded
				if ($('#svgSeatmap' + legIndex).find('svg').length) {
					deferred.resolve();
					return deferred.promise;
				}

				var equipmentCode = service.legs[legIndex].EquipmentCode, url;

				if (!equipmentCode) {
					deferred.reject(service.legs[legIndex].missingEquipmentCodeErrMessage);
					return deferred.promise;
				}
				$log.debug('equipment code:' + equipmentCode);

				//Getting SVG info from Sitecore
				service.loadPlaneConfiguration().then(function (planeList) {

					var getEquipmentInfoByCode = function (code) {
						var equipmentList = planeList || [];
						for (var i = 0, len = equipmentList.length; i < len; i++) {
							var item = equipmentList[i];
							if ($.inArray(code, item.codes) > -1) {
								return item;
							}
						}
						return undefined;
					};

					var foundInfo = getEquipmentInfoByCode(equipmentCode);
					if (!foundInfo) {
						$scs.get('BookingWidget.equipmentcodesvgerrortext').then(function (data) {
							deferred.reject(data);
						});
						return deferred.promise;
					}

					service.legs[legIndex].EquipmentName = foundInfo.name;
					url = foundInfo.url;

					service.loadSvgByUrl(url).then(function (response) {
						service.error = '';
						// make sure container exists
						if ($('#svgSeatmap' + legIndex).length) {
							$('#svgSeatmap' + legIndex).html(response);
							deferred.resolve();
						} else {
							var waitForContainer = $interval(function () {
								if ($('#svgSeatmap' + legIndex).length) {
									$interval.cancel(waitForContainer);
									$('#svgSeatmap' + legIndex).html(response);
									$log.debug('svg loaded after waiting for container');
									deferred.resolve();
								}
							}, 100, 500);
						}
						$timeout(service.getNextLeg, 1000);
					}, function (error) {
						deferred.reject('Error getting SVG seatmap: ' + error);
					});
				}, function (error) {
					deferred.reject('Error getting SVG seatmap: ' + error);
				});

				return deferred.promise;
			},

			//Check if aircraft has IFE available
			loadIFE: function () {
				var deferred = $q.defer();

				//Getting IFE info from Sitecore
				var equipmentUrl = haConfig.getDynamicJsonUrl('/sitecoreresources/entertainment_data');
				$http.GET(equipmentUrl).success(function (info) {
					deferred.resolve(info);
				}).error(function () {
					deferred.resolve([]);
				});

				return deferred.promise;
			},

			setIFE: function (enabled) {
				var deferred = $q.defer();

				//Getting IFE info from Sitecore
				$http.GET('/book/shared/setinflightentertainment?enabled=' + !!enabled).success(function (info) {
					deferred.resolve(info);
				}).error(function () {
					deferred.reject();
				});

				return deferred.promise;
			},

			// get next legs' seat data & svg. Do not get ALL legs, because
			// seat availability call takes a long time, and UI rendering
			// gets blocked until ALL calls are resolved.
			getNextLeg: function () {
				if (service.legs.length > 1) {

					var next = 0;
					if (service.activeLegIndex === service.legs.length - 1) {
						// user clicked back so initial leg is the last leg
						for (var i = service.legs.length - 1; i >= 0; i--) {
							if (service.legs[i]) {
								if (service.legs[i].IsCodeShare) {
									continue;
								}
								if (!service.legs[i].promise) {
									var next = i;
									break;
								}
							}
						}
					}
					else {
						for (var i = 0, len = service.legs.length; i < len; i++) {
							if (service.legs[i]) {
								if (service.legs[i].IsCodeShare) {
									continue;
								}
								if (!service.legs[i].promise) {
									var next = i;
									break;
								}
							}
						}
					}
					if (service.legs[next] && !service.legs[next].promise) {
						$timeout(function () { // give strings a few ms to load before this blocks anything
							service.legs[next].promise = service.getAvailableSeatsForLeg(next).then(service.getNextLeg);
						}, 500);
					}
				}
			},

			openSeatClassModalWindow: function (sitecoreFieldName) {
				var guid = 'InFlightOptionsInfo.' + sitecoreFieldName;
				var content = $scs(guid);
				haModal('', {
					id: 'contextual-help',
					backdrop: 'true',
					template: content
				});
			},

			// because backend gives us names like JAMIE PERKINS
			// and we don't want to be shouted at, --> Jamie Perkins
			convertToSentenceCase: function (stringToConvert) {
				if (stringToConvert.length === 0) { return; }
				var firstLetter = stringToConvert.substr(0, 1),
					otherLetters = stringToConvert.substr(1, stringToConvert.length);
				return firstLetter.toUpperCase() + otherLetters.toLowerCase();
			},

			convertToMilitaryTime: function (twelveHour) {
				var parts = twelveHour.split(':'),
					hours = parts[0],
					minutes = parts[1].substr(0, 2),
					ampm = parts[1].substr(2, 1);
				if (ampm.toLowerCase() === 'p') {
					hours = parseInt(hours) + 12;
				} else {
					hours = '00' + hours.slice(-2);
				}
				return '' + hours + minutes;
			},

			//Method to determine whether to allow prior seat selection based on the route
			disallowAdvanceSeatSelection: function (item, enabled, airportCode, svc) {
				var cityPair = item.ArrivalCityCode + '-' + item.DepartureCityCode;
				//If FAA-Restriction is enabled and PPG is part of leg
				if (enabled && cityPair.toLowerCase().indexOf(airportCode) > -1 && item.SelectedFareClass !== 'business') {
					return true;
				}
				//Set FAA Restriction to false (hide alert)
				item.faaRestricted = false;

				if (svc) {
					svc.faaRestricted = false;
				}

				//Allow seat selection
				return false;
			},

			setFAARestriction: function (leg, scope) {
				leg.resolved = leg.faaRestricted = true;
				if (scope) {
					scope.faaRestricted = true;
				}
			},
			// returns "Jamie P." or "J.P."
			formatNameWithLastInitial: function(first, last) {
				var firstName = service.convertToSentenceCase(first),
					lastName = last.substr(0, 1).toUpperCase() +'.',
					middleText = ' ';
				if (firstName.length === 1) {
					middleText = '.';
					if (service.lastNameFirst) {
						firstName += '.';
					}
				}
				return (service.lastNameFirst) ? lastName+middleText+firstName : firstName+middleText+lastName;
			}
		};

		return service;


	}]);

})(angular);

;
(function (angular) {

	'use strict';

	var module;
	try {
		module = angular.module('haMobileService');
	} catch (e) {
		module = angular.module('haMobileService', []);
	}

	module.service('haMobileSvc', [

		function () {

			var mobilebreakpoint = 767;

			this.determineIsMobile = function() {
                if (window.innerWidth > mobilebreakpoint) {
					window.isMobile = false;
                    return false;
                } else {
					window.isMobile = true;
                    return true;
                }
            };

		}
	]);

	/*
	 forces a nested column to span the width of the viewport, ONLY on mobile
	 */
	module.directive('haFlushFittingCol',  ['$rootScope', function ($rootScope) {

		return {
			restrict: 'A',
			link: function ($scope, $el, $attrs) {

				if ($rootScope.isMobile) {

					var negativeMargin = 0 - $el.offset().left;

					$el.css({
						marginLeft: negativeMargin,
						marginRight: negativeMargin,
						width: 'auto',
						overflow: 'hidden'
					});

					if ( $attrs.preservepadding !== "true" ) {
						$el.css({
							padding: 0
						});
					}
				}
			}
		}
	}]);

	/*
	 makes all columns in a row equal heights, ONLY on mobile. Requires at least 50px height
	 */
	module.directive('haEqualHeightColumns',  ['$rootScope', '$interval', function ($rootScope, $interval) {

		return {
			restrict: 'A',
			link: function ($scope, $el) {

				if ($rootScope.isMobile) {
					var highest = 0,
						minRequiredHeight = 50;
					// wait for render
					var ready = $interval(function() {
						if ($el.children(':last').outerHeight() > minRequiredHeight) {
							$interval.cancel(ready);
							$el.children().each(function() {
								if ($(this).outerHeight() > highest) {
									highest = $(this).outerHeight();
								}
							});
							$el.children().each(function() {
								if ($(this).outerHeight() < highest) {
									$(this).css('height', highest);
								}
							});
						}
					}, 50);
				}
			}
		}
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haAirportTimezonesModule', []);

	module.factory('haAirportTimezones', ['haHttpService', function (http) {

		var airportTimezonesURL = '/Scripts/app/services/airport-timezones.json';

		return {

			getAirportTimeZone: function (code) {

				return http.GET(airportTimezonesURL).then(function (response) {

					return response.data.filter( function(airport) {
						return airport.code === code;
					})[0];
				});
			}
		};

	}]);

})(angular);
;
(function (angular) {

	// Re-Authentication Service
	// =================================
	//
	// * **Class:** haReAuthService
	// * **Author:** Mark Hagelberg
	//
	// Keep track of fields needing re-authentication

	'use strict';

	var module = angular.module('haReAuthServiceModule', []);

	module.service('haReAuthService', [function () {

		var svc = this;

		svc.criticalFields = [];

		svc.getField = function (fieldName) {
			var fieldArray = svc.criticalFields.filter(function (cf) {
				return cf.name === fieldName;
			});
			return fieldArray[0]; // there will only be the one
		};

		svc.registerField = function (fieldName) {
			var field = {
				name: fieldName,
				changed: false,
				initialValue: getDomValue(fieldName) 
			};
			svc.criticalFields.push(field);
		};

		svc.updateField = function (field) {
			field = svc.getField(field);

			field.value = getDomValue(field.name);
			field.changed = field.value !== field.initialValue;
			
			var index = findWithAttr(svc.criticalFields, 'name', field.name);
			if (index > -1) svc.criticalFields.splice(index, 1, field);
		};

		svc.fieldIsRegistered = function (field) {
			if (svc.criticalFields.length && field && field.name) {
				return svc.criticalFields.some(function (cf) {
					return cf.name === field.name;
				});
			} else {
				return false;
			}
		};

		svc.formRequiresReAuth = function (fields) {
			fields.forEach(function (field) {
				svc.updateField(field);
			});

			return svc.criticalFields.some(function (cf) {
				return cf.changed;
			});
		};

		// Reset method. Always called after form directive is destroyed
		svc.cleanUp = function () {
			svc.criticalFields = [];
		};

		function getDomValue(fieldName) {
			return angular.element('[name="' + fieldName + '"]').val();
		};

		function findWithAttr(array, attr, value) {
			for(var i = 0; i < array.length; i += 1) {
				if(array[i][attr] === value) {
					return i;
				}
			}
			return -1;
		};

		return svc;
	}]);

})(angular);
;
(function (angular) {
	'use strict';

	var module;
	try {
		module = angular.module('haUpgradeModalModule');
	} catch (e) {
		module = angular.module('haUpgradeModalModule', []);
	}

	var API_URL = '/Api/OnlineUpgrade/Purchase';

	module.factory('haUpgradeService', [
		'haHttpService',
		function ($http) {
			var svc = {
				submitUpgradeRequest: function(data) {
					return $http.POST(API_URL, data);
				}
			};

			return svc;
		}
	]);
})(angular);
;
(function (ng) {

	'use strict';

	var module;
	try {
		module = ng.module('haAncillaryUpsellsServiceModule');
	} catch (e) {
		module = ng.module('haAncillaryUpsellsServiceModule', []);
	}

	module.factory('haAncillaryUpsellsService', ['haUtils', 'haSitecoreStrings',
		function (haUtils, $scs) {
			var service = {};

			// Defaults if sitecore data is unavailable
			var BUFFER_MINUTES_PICKUP = 60;
			var BUFFER_MINUTES_DROPOFF = 180;
			var ONEWAY_DAYS = 5;

			function initBuffers(marketType) {
				var bufferDict = $scs('carrentalbuffertimes');
				if (bufferDict.onewaydays) {
					ONEWAY_DAYS = parseInt(bufferDict.onewaydays);
				}
				switch (marketType) {
					case 1:
						// InterIsland
						if (bufferDict.interislandpickup && bufferDict.interislanddropoff) {
							BUFFER_MINUTES_PICKUP = parseInt(bufferDict.interislandpickup);
							BUFFER_MINUTES_DROPOFF = parseInt(bufferDict.interislanddropoff);
						}
						break;

					case 2:
						// TransPacific
						if (bufferDict.transpacificpickup && bufferDict.transpacificdropoff) {
							BUFFER_MINUTES_PICKUP = parseInt(bufferDict.transpacificpickup);
							BUFFER_MINUTES_DROPOFF = parseInt(bufferDict.transpacificdropoff);
						}
						break;

					case 3:
						// SouthPacific
						if (bufferDict.southpacificpickup && bufferDict.southpacificdropoff) {
							BUFFER_MINUTES_PICKUP = parseInt(bufferDict.southpacificpickup);
							BUFFER_MINUTES_DROPOFF = parseInt(bufferDict.southpacificdropoff);
						}
						break;

					default:
						// Other
						if (bufferDict.otherpickup && bufferDict.otherdropoff) {
							BUFFER_MINUTES_PICKUP = parseInt(bufferDict.otherpickup);
							BUFFER_MINUTES_DROPOFF = parseInt(bufferDict.otherdropoff);
						}
						break;
				}
			}

			function buildDateTime(datetime, minuteDiff) {
				minuteDiff = minuteDiff || 0;
				var dt = moment(datetime);
				dt.add(minuteDiff, 'minutes');
				return dt.format('YYYY-MM-DD HH:mm:ss');
			}
			function getPickupTime(VM, ignoreBuffer) {
				var segment = VM.Segments[0];
				var flight = segment.Flights[segment.Flights.length - 1];
				return buildDateTime(flight.IsoArrivalDatetime, ignoreBuffer ? 0 : BUFFER_MINUTES_PICKUP);
			}
			function getDropoffTime(VM, tripType, ignoreBuffer) {
				var formatted;
				if (tripType == 1) {
					// one way - add 5 days to pickup datetime
					var m = moment(getPickupTime(VM));
					m = m.add(ONEWAY_DAYS, 'days');
					formatted = m.format('YYYY-MM-DD HH:mm:ss');
				} else if (tripType == 2) {
					// round trip
					var segment = VM.Segments[1];
					var flight = segment.Flights[0];
					formatted = buildDateTime(flight.IsoDepartureDatetime, ignoreBuffer ? 0 : -BUFFER_MINUTES_DROPOFF);
				}

				return formatted;
			}
			function getDestination(VM) {
				var segment = VM.Segments[0];
				var lastFlight = segment.Flights[segment.Flights.length - 1];
				return lastFlight.ArrivalCityCode;

			}
			function getPrimaryPassenger(VM) {
				for (var i = 0; i < VM.Travellers.length; i++) {
					if (VM.Travellers[i].IsPrimaryPassenger) {
						return VM.Travellers[i];
					}
				}
			}
			function getPassengerAge(passenger) {
				if (!passenger.DOB) {
					return;
				}
				var dob = moment(passenger.DOB);
				var now = moment(new Date());

				return now.diff(dob, 'years');
			}
			function getLinkURL(upsell, tripType, viewModel, itineraryUrl) {
				var trackingID;
				var placeholder = '{params}';

				initBuffers(viewModel.ItineraryMarketType);
				if (window.location.href.match(/confirmation/i)) {
					trackingID = upsell.ConfirmationPageTrackingID;
				} else {
					trackingID = upsell.ItineraryPageTrackingID;
				}
				if (upsell.AncillaryType === 'GTAIRPORTSHUTTLE') {
					var urlComponents = upsell.DeepLinkURL.split(placeholder);
					if (urlComponents.length < 2) {
						throw "DeepLinkURL parameter placeholder not found";
					}

					// Grab static params from sitecore
					var params = upsell.DeepLinkParams; // foo=bar&name=value...

					// Build dynamic params from VM data (use ha-utils service)
					var cityCode = getDestination(viewModel);
					var dynamicParams = {
						adults: viewModel.Travellers.filter(function (p) { return p.Type === 'ADT'; }).length,
						oneway: true,
						pickupIATACode: cityCode,
						pickupDateTime: getPickupTime(viewModel),
						clientId: trackingID
					};

					var qs = haUtils.createQueryString(dynamicParams) + '&' + params;
					return urlComponents[0] + qs + urlComponents[1];

				} else if (upsell.AncillaryType === 'CAR') {
					if (upsell.DeepLinkURL && tripType > 0) {
						// Build cartrawler deeplink
						var newLink = upsell.DeepLinkURL;
						var urlComponents;
						if (newLink.indexOf(placeholder) != -1) {
							urlComponents = newLink.split(placeholder);
						} else {
							throw "DeepLinkURL parameter placeholder not found";
						}

						// Grab static params from sitecore
						var params = upsell.DeepLinkParams; // foo=bar&name=value...

						// Build dynamic params from VM data (use ha-utils service)
						var cityCode = getDestination(viewModel);
						var dynamicParams = {
							'age': getPassengerAge(getPrimaryPassenger(viewModel)),
							'pickupIATACode': cityCode,
							'returnIATACode': cityCode,
							'pickupDateTime': getPickupTime(viewModel),
							'returnDateTime': getDropoffTime(viewModel, tripType),
							'clientId': trackingID,
						}

						var qs = haUtils.createQueryString(dynamicParams) + '&' + params;
						return urlComponents[0] + qs + urlComponents[1];

					} else if (upsell.LinkURL.match(/cartrawler/i)) {
						// No deeplink for some reason, but has the old LinkURL
						var queryIndex = upsell.LinkURL.indexOf('?');
						var hashIndex = upsell.LinkURL.indexOf('#');
						if (queryIndex > 0) {
							var linkBeforeQuery = upsell.LinkURL.substr(0, queryIndex + 1);
							var linkAfterQuery = upsell.LinkURL.substr(queryIndex + 1);
							return linkBeforeQuery + 'clientID=' + trackingID + '&' + linkAfterQuery;
						} else if (hashIndex > 0) {
							var linkBeforeHash = upsell.LinkURL.substr(0, hashIndex);
							var linkAfterHash = upsell.LinkURL.substr(hashIndex);
							return linkBeforeHash + '?clientID=' + trackingID + linkAfterHash;
						} else {
							return upsell.LinkURL + '?clientID=' + trackingID;
						}
					}

				} else if (upsell.LinkURL.match(/select-or-upgrade-seats/i)) {
					if (typeof itineraryUrl === 'undefined') {
						return upsell.LinkURL;
					}
					return itineraryUrl;
				} else if (upsell.AncillaryType === 'HOTEL') {
					if (upsell.DeepLinkURL && tripType > 0) {
						var newLink = upsell.DeepLinkURL;
						var urlComponents;
						if (newLink.indexOf(placeholder) != -1) {
							urlComponents = newLink.split(placeholder);
						} else {
							throw "DeepLinkURL parameter placeholder not found";
						}

						var params = upsell.DeepLinkParams;
						var cityCode = getDestination(viewModel);
						var dynamicParams = {
							'MDPCID': trackingID,
							'destination': cityCode,
							'startDate': moment(getPickupTime(viewModel, true)).format('YYYY-MM-DD'),
							'endDate': moment(getDropoffTime(viewModel, tripType, true)).format('YYYY-MM-DD')
						}

						var qs = haUtils.createQueryString(dynamicParams) + '&' + params;
						return urlComponents[0] + qs + urlComponents[1];
					}
				} else {
					return upsell.LinkURL;
				}
			}
			service.getLinkURL = getLinkURL;
			return service;
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haEncryptionModule', [])
		.factory('haEncryptionService', ['haEncryptionAPI', function (haEncryptionAPI) {
			return {
				Encrypt: function (dataToEncrypt, key) {
					if (validString(dataToEncrypt) && validString(key)) {
						return haEncryptionAPI.encryptString(dataToEncrypt, key);
					}

					return Promise.reject(formatError());
				},
				EncryptString: function (dataToEncrypt, key) {
					return this.Encrypt(dataToEncrypt, key).then(
						function (response) {
							return response.data;
						},
						function (error) {
							console.error("Encryption Error: "+error);
							return "";
					});

				}
			};

			function formatError() {
				return "Format of data is invalid in EncryptionService.";
			}

			function validString(data) {
				return (data && typeof (data) === 'string' && data !== '') ? true : false;
			}
		}
		]);
}
)(angular)
;
(function(angular) {
		
	'use strict';

	angular.module('haLaunchDarklyModule',[])
		.factory('haLaunchDarklyService', [ 'haLaunchDarklyAPI', "$q", function(halaunchDarklyAPI,$q) {
					
			return {
				GetFlagVariant: function (key, type, variant, useCache) {
					var validFormat = false;	
					switch (type) {
						case "boolean":
							validFormat = validBooleanVariant(variant);	
						break;

						case "integer":
							validFormat = validIntegerVariant(variant);
						break;

						case "float":
							validFormat = validFloatVariant(variant);
						break;

						case "dictionary":
							validFormat = validDictionaryVariant(variant)
						break;

						case "string":
							validFormat = validStringVariant(variant)
						break;
							default:
								return Promise.reject("Invalid type: "+type+" sent to LaunchDarklyService.");
					}

					if (validFormat) {
						return halaunchDarklyAPI.getFeatureFlag(key, type, variant.toString(), useCache);			
					} else {
						return Promise.reject(formatError(type, variant));
					}
							
						},
						trackResult: function(id) {
							halaunchDarklyAPI.trackResult(id);
						},
						GetFlagVariantAsBoolean: function(key, type, variant) {
							
							 return this.GetFlagVariant(key, type, variant).then(function (response) {
								 
									return response.data === "ENABLED" ? true : false;
								},
								function(error) {
									console.error("Launch Darkly Error: "+error+"	KEY: "+attributes.key);
									return false;						
								});
						}
					};

					function formatError(type, variant) {

						return "Format of variant "+variant+" is invalid for type "+type+" in LaunchDarklyService.";
					}

					function validBooleanVariant(variant) {
						return variant === true || variant === false ? true : false;
					}

					function validIntegerVariant(variant) {
						return typeof (variant) === "number" ? true : false;
					}

					function validFloatVariant(variant) {
						return typeof (variant) === "number" && variant.toString().includes(".") ? true : false;
					}

					function validStringVariant(variant) {
						return typeof (variant) === 'string' ? true : false;
					}

					// Dictionary variant format is {JPath}-{value}
					// Value will a simple string literal. Not  object string
					// JPath is a string for selecting a json value https://www.newtonsoft.com/json/help/html/SelectToken.htm
					function validDictionaryVariant(variant) {
						return typeof (variant) === 'string' &&
							variant.indexOf("-") !== variant.length - 1
							? true
							: false;
					}
				}
			]);
}
)(angular)
;
(function (angular) {

	'use strict';

	var mod;
	try {
		mod = angular.module('haVariableSeatmapModule');
	} catch (e) {
		mod = angular.module('haVariableSeatmapModule', ['ui.router']);
	}

	// Author: Jamie Perkins
	mod.service('haVariableSeatmapService', ['$log', '$filter', 'haHttpService', '$q', '$timeout', '$interval', '$rootScope', 'haModal', 'haConfig', 'haSitecoreStrings', 'haUtils', 'haCitiesSvc', function ($log, $filter, $http, $q, $timeout, $interval, $rootScope, haModal, haConfig, $scs, haUtils, haCitiesSvc) {
		var service = {
			legs: [],
			activeLegIndex: 0,
			selectedPassengerIndex: 0,
			pageLoading: true,
			seatMapLoading: false,
			passengerSeatSelection: [],
			seatChargeSubtotals: [],
			seatChargeTotal: 0,
			isForSelection: true,
			error: null,
			strings: {},
			lastNameFirst: ($rootScope.$language === 'ja-jp' || $rootScope.$language === 'ko-kr' || $rootScope.$language === 'zh-cn' || $rootScope.$language === 'zh-tw'),
			svgScaleFactor: 1,
			reshopSeatCredits: [],
			promiseResponses: {},
			currency: $rootScope.$currency,
			originalPassengerFares: null,
			tooltipDescriptions: null,

			handleError: function (error) {
				$log.error(error);
				service.error = error;
			},

			// backend has mis-matched seat types. For booking/reshop, seats come down as EC = 1, PS = 2,
			// but they must be posted EC = 2, PS = 1. For Change seats, they come down and post consistently.
			getSeatType: function (type) {
				if (type === 1) {
					return 2;
				}
				else if (type === 2) {
					return 1;
				}
				else {
					return 0;
				}
			},

			getInitialsForSeat: function(seatObj) {
				return service.getInitialsForPax(seatObj.pax);
			},

			getInitialsForPax: function(pax) {
				var initials = service.lastNameFirst ?
					pax.LastName[0] + pax.FirstName[0] :
					pax.FirstName[0] + pax.LastName[0];
					return initials.toUpperCase();
			},

			// fetch seat map page vm, expected to be in root scope thanks to haInFlightOptionsController
			getFlightVM: function () {

				$scs.get('InFlightOptionsInfo').then(function (data) {
					service.strings = data;
				});

				// '/Date(1420009200000)/' -> Date object (needed for change seats model)
				function extractDate(datestring) {
					var time = datestring.match(/[\d]+/);
					time = Number(time[0]);
					return new Date(time);
				}

				function cabinNameToClass(cabinName) {
					switch (cabinName) {
						case 2:
							return 'first';
						case 3:
							return 'business';
						case 0:
						case 1:
						case 4:
							return 'coach';
						default:
							$log.error('Unknown CabinName:', cabinName);
							return '';
					}
				}

				// '09:15' -> '9:15a' (for change seats)
				function formatTime(timeString) {
					var time = timeString.split(':'),
						hours = parseInt(time[0]),
						pm = hours > 12,
						ampm = 'a';
					if (pm) {
						hours -= 12;
						ampm = 'p';
					}
					return hours + ':' + time[1] + ampm;
				}

				function seatTypeStringToType(type) {
					switch (type) {
						case 'ExtraComfort':
						case 'ec':
							return 1;
						case 'Preferred':
						case 'preferred':
							return 2;
						default:
							return 0;
					}
				}

				function checkPaxForInfants(passengers) {
					var hasInfant = false;
					angular.forEach(passengers, function (pax) {
						if (pax.Type && (pax.Type.toLowerCase() === 'infant')) {
							hasInfant = true;
						}
					});
					return hasInfant;
				}

				service.disableSeatUpgrades = $rootScope.IsDisableSeatUpgrade;
				service.disableExitRows = $rootScope.IsDisableExitRows;

				// booking or reshop
				if ($rootScope.TripSummary) {

					// get "legs" - each segment from each trip
					angular.forEach($rootScope.TripSummary.Trips, function (trip) {
						angular.forEach(trip.Flights[0].Legs, function (leg) {
							leg.SelectedFareClass = trip.Flights[0].SelectedFareClass.toLowerCase();
							//leg.Cabin = leg.ClassOfService[0].Cabin.toLowerCase(); // dupe of fare class
							service.legs.push(leg);
							service.seatChargeSubtotals.push(0);
						});
					});
					// passengers
					service.passengers = $rootScope.TripSummary.Passengers;
					// currency
					service.currency = $rootScope.TripSummary.currency;

					if ($rootScope.PassengerTripSummary && $rootScope.PassengerTripSummary.Passengers) {
						angular.forEach(service.passengers, function (pax, i) {
							var first = $rootScope.TripSummary.Passengers[i].FirstName,
								last = $rootScope.TripSummary.Passengers[i].LastName;
							// extend pax info with PassengerTripSummary.Passengers for:
							// - HM account number
							// - Make Trip summary reflect seat position
							angular.extend(pax, $rootScope.PassengerTripSummary.Passengers[i]);
							// PassengerTripSummary can get polluted with cached pax names from a previous flight search
							pax.FirstName = first;
							pax.LastName = last;

							// if reshop, wipe out current seat selections
							if ($rootScope.IsChangeFlightBooking && pax.Seats && pax.Seats.length) {
								angular.forEach(pax.Seats, function (paxSeat) {
									paxSeat.SeatLocation = null;
									paxSeat.Type = 0;
									paxSeat.SeatAmount = 0;
								});
							}
							if (!pax.Seats) {
								pax.Seats = [];
								angular.forEach(service.legs, function (leg, j) {
									pax.Seats.push({ SegmentID: j + 1 });
								});
							}

						});

					}
					// make sure a seat object exists for every leg and every pax
					angular.forEach(service.passengers, function (pax) {
						if (!pax.Seats) {
							pax.Seats = [];
						}
						angular.forEach(service.legs, function (leg, i) {
							if (!pax.Seats[i]) {
								pax.Seats.push({
									SegmentID: i + 1,
									SeatLocation: null,
									SeatAmount: 0,
									Type: 0
								});
							}
						});
					});

					// reshop
					if ($rootScope.IsChangeFlightBooking) {
						var reshopflightSearchCookie = haUtils.getReshopFlightQueryModelCookie();
						$log.debug('reshop cookie', reshopflightSearchCookie);
						service.reshopPNR = reshopflightSearchCookie.PNR_ID;
						service.reshopSegmentsIDs = [];
						angular.forEach(reshopflightSearchCookie.OldFlightSearchSegmentList, function (segment) {
							service.reshopSegmentsIDs.push(segment.SegmentID);
						});


						// fix names
						angular.forEach(service.passengers, function (pax) {
							pax.FirstName = service.convertToSentenceCase(pax.FirstName);
							pax.LastName = service.convertToSentenceCase(pax.LastName);
						});

						// get original trip
						var url = '/api/trip/' + service.reshopPNR + '/' + service.passengers[0].LastName
						$http.GET(url).success(function (data, status, headers) {
							$log.debug('trip data', data);

							angular.forEach(data.segments, function (segment, i) {
								angular.forEach(segment, function (origLeg) {

									// determine seats to persist
									angular.forEach(service.legs, function (reshopLeg, j) {

										// same leg & fare class?
										var reshopFlight = reshopLeg.OperatingCarrier + '-' + reshopLeg.FlightNumber,
											origLegDeparture = moment(origLeg.depart).format('YYYY-MM-DD'),
											origFare = origLeg.cabin;
										if (origFare === 'extracomfort') {
											origFare = 'coach';
										}
										if (origLeg.flight === reshopFlight && origLegDeparture === reshopLeg.DepartureDate && origFare === reshopLeg.SelectedFareClass) {
											// same flight: persist original seat
											angular.forEach(origLeg.seats, function (seat) {
												angular.forEach(service.passengers, function (pax) {
													if (seat.traveler === pax.TravelerAssociationID) {
														$log.debug('same flight - seat ' + seat.id + ' persisted');
														pax.Seats[j].SeatLocation = seat.id;
														pax.Seats[j].SeatAmount = seat.cost;
													}
												});
											});
										}
									});
									// find seat credits on reshoped segments

									var isReshopedSegment = service.reshopSegmentsIDs.indexOf(i + 1) !== -1;
									angular.forEach(origLeg.seats, function (seat) {
										if (isReshopedSegment && seat.cost > 0) {
											service.reshopSeatCredits.push({
												type: seatTypeStringToType(seat.type),
												price: seat.cost,
												credit: seat.cost,
												isExtraComfort: (seat.type === 'ec'),
												isMainCabinPreferred: (seat.type === 'preferred'),
												TravelerAssociationID: seat.traveler,
											    isPrereservedSeat: seat.isPrereservedSeat
											});
										}
									});
								});
							});

							service.disableRow4 = checkPaxForInfants(service.passengers);
							service.pageLoading = false;

						}).error(function (data, status/*, headers, config*/) {
							// fail silently
							$log.error('Error getting original trip data: ' + status);

							service.disableRow4 = checkPaxForInfants(service.passengers);
							service.pageLoading = false;
						});
					}
					else {

						service.disableRow4 = checkPaxForInfants(service.passengers);
						service.pageLoading = false;
					}

					// Save the original fares for use in the case of upsell grid for flight result selection.
					service.originalPassengerFares = service.passengers.map(function(p) {
						return angular.copy(p.Seats);
					});
				}

				// change seats - completely different model ¯\_(ツ)_/¯
				else if ($rootScope.AirGroup) {

					service.isChangeSeats = true;
					// legs
					angular.forEach($rootScope.AirGroup.Flight, function (flight) {
						// map missing properties
						var carrier = flight.OperatingCarrier.AirlineCode;
						if (carrier === 'EM') { carrier = 'HA'; }
						var leg = {
							FlightNumber: flight.Carrier.FlightNumber.Value,
							OperatingCarrier: carrier,
							DepartureDate: moment(extractDate(flight.Departure.Date)).format('MM/DD/YYYY'),
							DepartureTime: formatTime(flight.Departure.Time),
							DepartureCityCode: flight.Departure.AirportCode,
							DepartureCity: flight.Departure.City,
							ArrivalCityCode: flight.Arrival.AirportCode,
							ArrivalCity: flight.Arrival.City,
							Duration: flight.FlightDuration,
							ClassOfService: [{ ClassValue: flight.ClassOfService }],
							IsCodeShare: flight.IsCodeshare,
							SelectedFareClass: cabinNameToClass(flight.CabinName),
							ElementNumber: parseInt(flight.ElementNumber)
							//Cabin: codeToClass(flight.ClassOfService)
						};
						service.legs.push(leg);
					});

					// pax and seats
					service.passengers = [];
					angular.forEach($rootScope.Traveler, function (passenger, i) {
						if (service.paxOkayToAdd(passenger.ElementNumber)) {
							var pax = {
								type: 'Adult', // hardcoded because there is no way to check age with given info.
								isUser: (i === 0),
								FirstName: service.convertToSentenceCase(passenger.TravelerName.GivenName),
								LastName: service.convertToSentenceCase(passenger.TravelerName.Surname),
								Id: passenger.ElementNumber,
								TravelerAssociationID: passenger.ElementNumber,
								IsMemberEligibleForDiscount: passenger.IsMemberEligibleForDiscount,
								MemberType: passenger.MemberType
							};
							pax.Seats = [];
							angular.forEach(service.legs, function (leg, i) {
								var paxLegSeat = { SegmentID: i + 1 }
								// if existing seats - map to pax and leg
								if ($rootScope.Seat && $rootScope.Seat.length) {

									service.currency = $rootScope.Seat[0].CurrencyCode;

									angular.forEach($rootScope.Seat, function (seat) {

										if (parseInt(seat.SegmentElementNumber.Value) === leg.ElementNumber && seat.Assignment.TravelerElementNumber.Value === pax.Id) {
											var seatName = seat.Assignment.SeatLocation;
											if (seatName.indexOf('0') === 0) {
												seatName = seatName.substr(-2);
											}
											paxLegSeat.SeatLocation = seatName;
											paxLegSeat.Type = service.getSeatType(seat.SeatType);
											paxLegSeat.SeatAmount = seat.SeatAmount;
											paxLegSeat.originalSeat = {
												type: service.getSeatType(seat.SeatType),
												price: seat.SeatAmount,
												credit: seat.SeatCredit,
												isExtraComfort: (seat.SeatType === 2),
												isMainCabinPreferred: (seat.SeatType === 1),
												SeatLocation: seatName,
												isPrereservedSeat: seat.IsPrereservedSeat
											};
										}
									});
								}
								pax.Seats.push(paxLegSeat);
							});
							service.passengers.push(pax);
						}
					});

					service.pageLoading = false;

				}
				// expert booking path
				else if ($rootScope.ExpertBookingSeatmapData) {
					// legs
					service.legs = [];
					angular.forEach($rootScope.ExpertBookingSeatmapData.segments, function(segment) {
						angular.forEach(segment.legs, function(leg) {
							var serviceLeg = {
								FlightNumber: leg.flight,
								OperatingCarrier: leg.carrier,
								DepartureDate: moment(leg.departure).format('MM/DD/YYYY'),
								DepartureTime: formatTime(moment(leg.departure).format('h:mma')),
								DepartureCityCode: leg.origin,
								DepartureCity: haCitiesSvc.getCityByCodeSync(leg.origin).LongDescription,
								ArrivalCityCode: leg.destination,
								ArrivalCity: haCitiesSvc.getCityByCodeSync(leg.destination).LongDescription,
								Duration: $filter('formatMinutes')(leg.duration),
								//ClassOfService: [{ ClassValue: flight.ClassOfService }],
								IsCodeShare: (leg.operator && leg.operator !== "HA"),
								SelectedFareClass: leg.cabin,
								//ElementNumber: parseInt(flight.ElementNumber)
								//Cabin: codeToClass(flight.ClassOfService) // Maybe not needed
								id: leg.id,
								disableFirstClass: $rootScope.ExpertBookingSeatmapData.existingPNR && leg.cabin !== 'FIRST',
								disableMainCabin: leg.cabin === 'FIRST'
							};
							service.legs.push(serviceLeg);
						});
					});

					// pax and seats
					service.passengers = [];
					angular.forEach($rootScope.ExpertBookingSeatmapData.travelers, function(traveler) {
						var pax = {
							type: traveler.type,
							isUser: traveler.hmnumber && traveler.hmnumber == $rootScope.user.haMiles,
							FirstName: traveler.firstname,
							LastName: traveler.lastname,
							TravelerID: traveler.travelerid,
							ID: traveler.id
							// TravelerAssociationID: passenger.ElementNumber
						};

						pax.Seats = [];
						angular.forEach(service.legs, function (leg, i) {
							var paxLegSeat = { SegmentID: i + 1 }
							// if existing seats - map to pax and leg
							if (traveler.seats && traveler.seats.length) {

								service.currency = $rootScope.ExpertBookingSeatmapData.currency;

								angular.forEach(traveler.seats, function (seat) {

									if (seat.id === leg.id) {
										var seatName = seat.seat;
										paxLegSeat.SeatLocation = seatName;
										paxLegSeat.Type = seatTypeStringToType(seat.type);
										paxLegSeat.SeatAmount = seat.amount / 100; // ExpertBooking prices are in cents
										paxLegSeat.originalSeat = {
											type: seatTypeStringToType(seat.type),
											price: seat.amount / 100, // ExpertBooking prices are in cents
											isExtraComfort: seatTypeStringToType(seat.type) === 1,
											isMainCabinPreferred: seatTypeStringToType(seat.type) === 2
										};
									}
								});
							}
							pax.Seats.push(paxLegSeat);
						});
						service.passengers.push(pax);

					});

					// Seat restrictions
					if ($rootScope.ExpertBookingSeatmapData.restrictions) {
						service.disableSeatUpgrades = $rootScope.ExpertBookingSeatmapData.restrictions.blockseatupgrades;
						service.disableExitRows = $rootScope.ExpertBookingSeatmapData.restrictions.blockexitrows;
					}

					// Make a copy of the initial passenger seats in order to see if they change
					service.passengersInitial = angular.copy(service.passengers);
					service.pageLoading = false;
				}
				else {
					var error = 'View model is missing from root scope.';
					service.handleError(error);
					service.pageLoading = false;
					return;
				}
			},

			paxOkayToAdd: function(paxId) {
				if ($rootScope.infantLapList && $rootScope.infantLapList.indexOf(paxId) > -1) {
					return false;
				}
				if ($rootScope.groupPNRSelectedPax && $rootScope.groupPNRSelectedPax.length > 0 && $rootScope.groupPNRSelectedPax.indexOf(paxId) === -1) {
					return false;
				}
				return true;
			},

			isGroupBooking: function() {
				return $rootScope.groupPNRSelectedPax && $rootScope.groupPNRSelectedPax.length > 0;
			},

			// for reshop, apply seat credits where they match the upgrade available for the leg
			// must fire AFTER seatmap renders, in order to know leg upgrade type
			applyAnyAvailableSeatCredits: function(legIndex) {
				if (service.reshopSeatCredits.length) {

					angular.forEach(service.reshopSeatCredits, function (credit) {
						if (!credit.used && (credit.isMainCabinPreferred ||
							credit.isExtraComfort)) {
							// matching credit found. find matching pax to assign credit to
							angular.forEach(service.passengers, function (pax) {
								var paxSeat = pax.Seats[legIndex];
								if (credit.TravelerAssociationID === pax.TravelerAssociationID && !paxSeat.originalSeat) {
									paxSeat.originalSeat = angular.copy(credit);
									paxSeat.IsCredit = true;
									paxSeat.SurchargeOk = true;
									credit.used = true;
								}
							});
						}
					});

					$rootScope.$broadcast('reshopCreditAppliedForLeg', legIndex);
				}
			},

			// establish seatmap VM for seatmap preview
			initPreviewSegments: function (segments, market, disableSeatUpgrades) {

				service.isPreview = true;
				service.isForSelection = false;
				service.disableSeatUpgrades = disableSeatUpgrades;
				service.legs = []; // reset legs

				$scs.get('InFlightOptionsInfo').then(function (data) {
					service.strings = data;
				});

				var IsInternational = Boolean(market > 2);
				// get legs
				angular.forEach(segments, function (leg) {
					// map missing properties
					if (leg.OperatingAirline === 'EM') {
						leg.IsCodeShare = false;
						leg.OperatingCarrier = 'HA';
					} else {
						leg.OperatingCarrier = leg.OperatingAirline;
					}

					leg.DepartureDate = moment(leg.DepartureDateTime).format('MM/DD/YYYY');
					leg.DepartureCityCode = leg.DepartureCityCode || leg.OriginCityCode;
					leg.DepartureCity = leg.DepartureCity || leg.OriginCityName;
					leg.DepartureTime = moment(leg.DepartureDateTime).format('h:mma');
					leg.ArrivalCityCode = leg.ArrivalCityCode || leg.DestinationCityCode;
					leg.ArrivalCity = leg.ArrivalCity || leg.DestinationCityName;
					leg.IsInternational = IsInternational;
					service.legs.push(leg);
				});

				service.pageLoading = false;
			},

			// returns seats post object which for some reason is completely different than the vm
			createSeatsPostObject: function () {
				var postObject = {
					InFlightOptions: [],
					Legs: [],
					TotalPremiumSeatAmount: 0
				};

				angular.forEach(service.legs, function (leg, i) {

					var legObject = {
						AirlineCode: leg.OperatingCarrier,
						ArrivalCityCode: leg.ArrivalCityCode,
						ClassOfService: leg.ClassOfService[0].ClassValue,
						DepartureCityCode: leg.DepartureCityCode,
						DepartureDate: leg.DepartureDate,
						DepartureTime: leg.DepartureTime,
						FlightNumber: leg.FlightNumber,
						IsCodeShare: Boolean(leg.IsCodeShare),
						SelectedFareClass: leg.SelectedFareClass,
						SeatSelections: {
							ExtraComfortSelectionsCount: 0,
							PreferredSeatSelectionsCount: 0,
							InFlightOptions: [],
							Seats: []
						}
					};
					var extraComfortFee = 0;
					var preferredSeatFee = 0;
					if (leg.ExtraComfortFee) {
						extraComfortFee = leg.ExtraComfortFee;
					}
					if (leg.PreferredSeatFee) {
						preferredSeatFee = leg.PreferredSeatFee;
					}
					else if (leg.seatModel && leg.seatModel.extraComfortPrice) {
						extraComfortFee = leg.seatModel.extraComfortPrice;
					}
					else if (leg.seatModel && leg.seatModel.preferredSeatPrice) {
						preferredSeatFee = leg.seatModel.preferredSeatPrice;
					}

					if (service.isChangeSeats) {
						legObject.ElementNumber = i + 1;
					} else {
						legObject.SelectedFareClass = leg.SelectedFareClass;
						legObject.ExtraComfortFee = extraComfortFee;
						legObject.PreferredSeatFee = preferredSeatFee;
						legObject.IsExtraComfortAvailable = leg.IsExtraComfortAvailable;
					}
					angular.forEach(service.passengers, function (pax) {
						var paxSeat = pax.Seats[i];
						legObject.SeatSelections[pax.TravelerAssociationID] = paxSeat.SeatLocation;
						var seatAmount = paxSeat.SeatAmount;

						if (service.isGroupBooking() && paxSeat.isMainCabinPreferred && paxSeat.PreferredSeatOriginalPrice) {
							seatAmount = paxSeat.PreferredSeatOriginalPrice;
						}

						var seat = {
							Flight: leg.OperatingCarrier + leg.FlightNumber,
							PaxId: pax.TravelerAssociationID,
							SeatAmount: seatAmount,
							SeatName: paxSeat.SeatLocation,
							PreferredSeatOriginalPrice: paxSeat.PreferredSeatOriginalPrice,
							SeatType: service.getSeatType(paxSeat.Type),
							SelectedFareClass: leg.SelectedFareClass,
							ExtraComfortFee: extraComfortFee,
							PreferredSeatFee: preferredSeatFee,
							IsExtraComfortAvailable: leg.IsExtraComfortAvailable,
                            WaiverCode: leg.WaiverCode
						};
						if (service.isChangeSeats) {
							seat.InFlightOptions = [];
						}
						if (service.includeSeatNumber) {
							seat.SeatNumber = paxSeat.SeatLocation;
						}
						// upgrades
						if (paxSeat.Type > 0) {
							var descr = '';

							if (paxSeat.Type === 2) {
								descr = service.strings['preferredseattext'];
								legObject.SeatSelections.PreferredSeatSelectionsCount++;
							}
							else if (paxSeat.Type === 1) {
								descr = service.strings['extracomfortupgradetext'];
								legObject.SeatSelections.ExtraComfortSelectionsCount++;
							}
							var seatUpgrade = {
								Description: descr,
								Flight: leg.OperatingCarrier + leg.FlightNumber,
								Price: paxSeat.SeatAmount,
								SeatName: paxSeat.SeatLocation
							};
							if (!service.isChangeSeats) {
								if ((paxSeat.Type === 1 && leg.SelectedFareClass !== 'extracomfort') || (paxSeat.Type === 2 && leg.SelectedFareClass !== 'preferred')) {
									legObject.SeatSelections.InFlightOptions.push(seatUpgrade);
									postObject.InFlightOptions.push(seatUpgrade);
									postObject.TotalPremiumSeatAmount += paxSeat.SeatAmount;
								}
							}
							else if (service.isChangeSeats && paxSeat.originalSeat && seat.SeatAmount > paxSeat.originalSeat.price) {
								seat.InFlightOptions.push(seatUpgrade);
							}
						}
						// downgrades
						if (paxSeat.DowngradeOk && paxSeat.SeatAmount === 0 && !$rootScope.IsChangeFlightBooking) {
							var seatDowngrade = {
								Description: service.strings['extracomfortdowngradetext'],
								SeatName: paxSeat.SeatLocation,
								Flight: leg.OperatingCarrier + leg.FlightNumber,
								Price: paxSeat.SeatAmount,
							}
							seat.IsDowngrade = true;
							legObject.SeatSelections.InFlightOptions.push(seatDowngrade);
							postObject.InFlightOptions.push(seatDowngrade);
						}
						legObject.SeatSelections.Seats.push(seat);
					});

					postObject.Legs.push(legObject);
				});
				return postObject;
			},

			// gets seats with API v2
			getSeatsForLeg: function (legIndex) {

				var deferred = $q.defer();

				// construct url
				var url = '/api/seats/';
				//url += service.legs[legIndex].AirlineCode + '-';
				url += 'ha-'; // currently we can only get seat data for HA flights
				url += service.legs[legIndex].FlightNumber + '/';
				url += moment(service.legs[legIndex].DepartureDate).format('YYYY-MM-DD');
				var query = {
					o: service.legs[legIndex].DepartureCityCode,
					d: service.legs[legIndex].ArrivalCityCode,
					currency: $rootScope.$currency,
					time: service.legs[legIndex].DepartureTime,
					arrivalTime: service.legs[legIndex].ArrivalTime,
					arrivalDate: moment(service.legs[legIndex].ArrivalDate).format('YYYY-MM-DD'),
				};
				query.hm = service.getHMNumbers();
				url += '?' + $.param(query);

				$http.GET(url).success(function (data, status, headers) {
					if (!service.legs[legIndex].EquipmentCode) {
						service.legs[legIndex].EquipmentCode = headers('X-Plane-Type');
					}
					service.legs[legIndex].seatModel = {
					    seats: data.SeatDetails,
					    disableSeatUpgradeFlx: data.DisableSeatUpgradeFlx
					};
					service.legs[legIndex].disableFirstClass = !data.isFirstClassAvailable
					service.legs[legIndex].isEliteMember = data.isEliteMember;
					service.legs[legIndex].isCorporateMember = data.isCorporateMember;
					service.legs[legIndex].HMMemberType = data.HMMemberType;
					service.legs[legIndex].WaiverCode = data.WaiverCode;
				    // Drop the HMMemberType into the trip summary
					if ($rootScope.PassengerTripSummary && !$rootScope.PassengerTripSummary.HMMemberType) {
						$rootScope.PassengerTripSummary.HMMemberType = data.HMMemberType;
					}
					service.legs[legIndex].missingEquipmentCodeErrMessage = data.MissingEquipmentCodeErrMessage;
					// in some rare cases, seat data comes back as an empty object. Show an error in that case.
					if (Object.getOwnPropertyNames(data).length === 0) {
						service.noSeatsAvailableForLeg(legIndex);
					}
					deferred.resolve();

				}).error(function (data, status/*, headers, config*/) {
					deferred.reject('Error getting seat availability data: ' + status);
				});

				return deferred.promise;

			},

			// gets list of HM numbers
			getHMNumbers: function () {
				var hmNums = [];
				// if ($rootScope.isLoggedIn && $rootScope.user && $rootScope.user.haMiles) {
				// 	hmNums.push($rootScope.user.haMiles);
				// }
				angular.forEach(service.passengers, function (pax) {
					if (pax.AccountNo) {
						hmNums.push(pax.AccountNo);
					}
				});

				return hmNums.join(',');
			},

			setUpgradableSeat: function (available) {
				//set a session variable when there are upgradable seats
				var deferred = $q.defer();

				$http.POST('/book/shared/SetUpgradeSeat?available=' + available)
					.success(function () {
						deferred.resolve();
					})
					.error(function () {
						deferred.resolve();
					});
				return deferred.promise;
			},

			upgradeToMainCabin: function () {
				return $http.POST('/Book/InFlightOptions/UpgradeToMainCabin');
			},

			getSeatmapTooltips: function () {
				return $http.POST('/MyAccount/PostPurchaseEditSeats/SeatMapToolTip');
			},

			getSeatsForChangeSeats: function (legIndex) {

				var deferred = $q.defer();

				var postObject = service.createSeatsPostObject();
				//$log.debug('request seats for change seats', postObject.Legs[legIndex]);

				$http.POST('/MyAccount/PostPurchaseEditSeats/GetSeatMap', {
					seatMapRequest: postObject.Legs[legIndex]
				}).success(function (data, status, headers) {
					service.legs[legIndex].EquipmentCode = headers('X-Plane-Type');
					service.legs[legIndex].isEliteMember = data.isEliteMember;
					service.legs[legIndex].isCorporateMember = data.isCorporateMember;
					service.legs[legIndex].HMMemberType = data.HMMemberType;
					// Drop the HMMemberType into the trip summary
					if ($rootScope.PassengerTripSummary && !$rootScope.PassengerTripSummary.HMMemberType) {
						$rootScope.PassengerTripSummary.HMMemberType = data.HMMemberType;
					}
					service.BlockRowFour = data.BlockRowFour;
					var seats = {};
					var seatsAvailable = 0;
					angular.forEach(data.rows, function (row) {
						if (row.number && row.number.length) {
							angular.forEach(row.cols, function (col) {
								if (col.isSeat) {
									if (col.available) {
										seatsAvailable ++;
									}
									var price = 0;
									if (col.extraComfortSeatPrice && col.extraComfortSeatPrice > 0) {
										price = col.extraComfortSeatPrice;
									}
									else if (col.preferredSeatPrice && col.preferredSeatPrice > 0) {
										price = col.preferredSeatPrice;
									}
									seats[col.name] = {
										price: price,
										available: col.available,
										originalPrice: col.originalPrice,
									 };
									// VSP 2.0 -- Indicator of Main Cabin Preferred seat.
									if (col.preferredSeat === true) {
										seats[col.name].seatType = 1;
									}
								}
							});
						}
					});
					service.legs[legIndex].seatModel = {
						seats: seats
					};
					if (!seatsAvailable) {
						service.noSeatsAvailableForLeg(legIndex);
					}

					deferred.resolve();

				}).error(function () {
					deferred.reject('Error getting seat availability data: ' + status);
				});

				return deferred.promise;
			},

			// displays an error message and hides the seat map when no seats are available for the given flight
			noSeatsAvailableForLeg: function (legIndex) {
				service.legs[legIndex].seatModel = {
					seatsUnavailable: true
				};
				// this string is not availble on some pages so we have to fetch it
				$scs.get('InFlightOptions.NoSeatsAvailableContent').then(function(string){
					service.noSeatsAvailableErrorMessage = string;
				});
			},

			// decides which endpoint to get seat data and equipment type, returns promise
			getAvailableSeatsForLeg: function (legIndex) {

				$log.debug('get available seats for index:', legIndex);
				// already resolved?
				if (service.legs[legIndex].seatModel) {
					var deferred = $q.defer();
					deferred.resolve();
					return deferred.promise;
				}

				// redundant check for codeshare
				if (service.legs[legIndex].IsCodeShare) {
					var deferred = $q.defer();
					deferred.reject('Can\'t get available seats for codeshare flight.');
					return deferred.promise;
				}

				if (service.isChangeSeats) {
					return service.getSeatsForChangeSeats(legIndex);
				}
				else {
					return service.getSeatsForLeg(legIndex);
				}

			},

			// save duplicate call to a promise
			loadGetPromise: function (url) {
				if (!service.promiseResponses[url]) {
					var deferred = $q.defer();
					service.promiseResponses[url] = deferred.promise;
					$http.GET(url).success(function (response) {
						deferred.resolve(response);
					}).error(function (data, status) {
						deferred.reject(status);
					});
				}
				return service.promiseResponses[url];
			},

			// avoid calling the same endpoint twice
			loadPlaneConfiguration: function () {
				//Getting SVG info from Sitecore
				var equipmentUrl = '/planes/configuration';
				return service.loadGetPromise(equipmentUrl);
			},

			// always loads svg once for then entire flight result
			loadSvgByUrl: function (url) {
				return service.loadGetPromise(url);
			},

			// always loads svg into an element with the id 'svgSeatmap0' where 0 = legIndex
			// returns promise
			loadSvgSeatmapForLeg: function (legIndex) {

				var deferred = $q.defer();

				// skip request if it's already loaded
				if ($('#svgSeatmap' + legIndex).find('svg').length) {
					deferred.resolve();
					return deferred.promise;
				}

				var equipmentCode = service.legs[legIndex].EquipmentCode, url; //"33L", url;

				if (!equipmentCode) {
					deferred.reject(service.legs[legIndex].missingEquipmentCodeErrMessage);
					return deferred.promise;
				}
				$log.debug('equipment code:' + equipmentCode);

				//Getting SVG info from Sitecore
				service.loadPlaneConfiguration().then(function (planeList) {

					var getEquipmentInfoByCode = function (code) {
						var equipmentList = planeList || [];
						for (var i = 0, len = equipmentList.length; i < len; i++) {
							var item = equipmentList[i];
							if ($.inArray(code, item.codes) > -1) {
								return item;
							}
						}
						return undefined;
					};

					var foundInfo = getEquipmentInfoByCode(equipmentCode);
					if (!foundInfo) {
						$scs.get('BookingWidget.equipmentcodesvgerrortext').then(function (data) {
							deferred.reject(data);
						});
						return deferred.promise;
					}

					service.legs[legIndex].EquipmentName = foundInfo.name;
					url = foundInfo.url;

					service.loadSvgByUrl(url).then(function (response) {
						service.error = '';
						// make sure container exists
						if ($('#svgSeatmap' + legIndex).length) {
							$('#svgSeatmap' + legIndex).html(response);
							deferred.resolve();
						} else {
							var waitForContainer = $interval(function () {
								if ($('#svgSeatmap' + legIndex).length) {
									$interval.cancel(waitForContainer);
									$('#svgSeatmap' + legIndex).html(response);
									$log.debug('svg loaded after waiting for container');
									deferred.resolve();
								}
							}, 100, 500);
						}
						$timeout(service.getNextLeg, 1000);
					}, function (error) {
						deferred.reject('Error getting SVG seatmap: ' + error);
					});
				}, function (error) {
					deferred.reject('Error getting SVG seatmap: ' + error);
				});

				return deferred.promise;
			},

			//Check if aircraft has IFE available
			loadIFE: function () {
				var deferred = $q.defer();

				//Getting IFE info from Sitecore
				var equipmentUrl = haConfig.getDynamicJsonUrl('/sitecoreresources/entertainment_data');
				$http.GET(equipmentUrl).success(function (info) {
					deferred.resolve(info);
				}).error(function () {
					deferred.resolve([]);
				});

				return deferred.promise;
			},

			setIFE: function (enabled) {
				var deferred = $q.defer();

				//Getting IFE info from Sitecore
				$http.GET('/book/shared/setinflightentertainment?enabled=' + !!enabled).success(function (info) {
					deferred.resolve(info);
				}).error(function () {
					deferred.reject();
				});

				return deferred.promise;
			},

			// get next legs' seat data & svg. Do not get ALL legs, because
			// seat availability call takes a long time, and UI rendering
			// gets blocked until ALL calls are resolved.
			getNextLeg: function () {
				if (service.legs.length > 1) {

					var next = 0;
					if (service.activeLegIndex === service.legs.length - 1) {
						// user clicked back so initial leg is the last leg
						for (var i = service.legs.length - 1; i >= 0; i--) {
							if (service.legs[i]) {
								if (service.legs[i].IsCodeShare) {
									continue;
								}
								if (!service.legs[i].promise) {
									var next = i;
									break;
								}
							}
						}
					}
					else {
						for (var i = 0, len = service.legs.length; i < len; i++) {
							if (service.legs[i]) {
								if (service.legs[i].IsCodeShare) {
									continue;
								}
								if (!service.legs[i].promise) {
									var next = i;
									break;
								}
							}
						}
					}
					if (service.legs[next] && !service.legs[next].promise) {
						$timeout(function () { // give strings a few ms to load before this blocks anything
							service.legs[next].promise = service.getAvailableSeatsForLeg(next).then(service.getNextLeg);
						}, 500);
					}
				}
			},

			openSeatClassModalWindow: function (sitecoreFieldName) {
				var guid = 'InFlightOptionsInfo.' + sitecoreFieldName;
				var content = $scs(guid);
				haModal('', {
					id: 'contextual-help',
					backdrop: 'true',
					template: content
				});
			},

			// because backend gives us names like JAMIE PERKINS
			// and we don't want to be shouted at, --> Jamie Perkins
			convertToSentenceCase: function (stringToConvert) {
				if (stringToConvert.length === 0) { return; }
				var firstLetter = stringToConvert.substr(0, 1),
					otherLetters = stringToConvert.substr(1, stringToConvert.length);
				return firstLetter.toUpperCase() + otherLetters.toLowerCase();
			},

			convertToMilitaryTime: function (twelveHour) {
				var parts = twelveHour.split(':'),
					hours = parts[0],
					minutes = parts[1].substr(0, 2),
					ampm = parts[1].substr(2, 1);
				if (ampm.toLowerCase() === 'p') {
					hours = parseInt(hours) + 12;
				} else {
					hours = '00' + hours.slice(-2);
				}
				return '' + hours + minutes;
			},

			//Method to determine whether to allow prior seat selection based on the route
			disallowAdvanceSeatSelection: function (item, enabled, airportCode, svc) {
				var cityPair = item.ArrivalCityCode + '-' + item.DepartureCityCode;
				//If FAA-Restriction is enabled and PPG is part of leg
				if (enabled && cityPair.toLowerCase().indexOf(airportCode) > -1 && item.SelectedFareClass !== 'business') {
					return true;
				}
				//Set FAA Restriction to false (hide alert)
				item.faaRestricted = false;

				if (svc) {
					svc.faaRestricted = false;
				}

				//Allow seat selection
				return false;
			},

			setFAARestriction: function (leg, scope) {
				leg.resolved = leg.faaRestricted = true;
				if (scope) {
					scope.faaRestricted = true;
				}
			},
			// returns "Jamie P." or "J.P."
			formatNameWithLastInitial: function(first, last) {
				var firstName = service.convertToSentenceCase(first),
					lastName = last.substr(0, 1).toUpperCase() +'.',
					middleText = ' ';
				if (firstName.length === 1) {
					middleText = '.';
					if (service.lastNameFirst) {
						firstName += '.';
					}
				}
				return (service.lastNameFirst) ? lastName+middleText+firstName : firstName+middleText+lastName;
			}
		};

		return service;


	}]);

})(angular);
;
(function (angular) {
	'use strict';

	var mod = angular.module('haUplift', []);

	mod.service('haUplift', ['haHttpService', 'haLaunchDarklyAPI', 'haPaymentAPI', '$rootScope', '$q', 'haUtils', function (haHttpService, halaunchDarklyAPI, haPaymentAPI, $rootScope, $q, haUtils) {
		var isCheckout = false;
		var UpAPI = "";
		var UpCode = "";
		var upliftConfiguration =  window.upliftConfiguration;
		window.UpliftGlobals = window.UpliftGlobals || {};
		window.UpliftGlobals.isUpliftEnabled = false;

		$rootScope.isUpEnabled = false;

        //load Uplift
		function loadUpliftJS() {
			if (upliftConfiguration) {
				UpCode = upliftConfiguration.UpliftCode;
				UpAPI = upliftConfiguration.UpliftAPI;
				$rootScope.UpTimeout = upliftConfiguration.UpliftTimeout;
				$rootScope.isUpEnabled = upliftConfiguration.IsUpliftEnabled;
				$rootScope.UpliftComponents = upliftConfiguration.ComponentCollection;

				//Load uplift
				setTimeout(function () {
						; (function (u, p, l, i, f, t, b, j) { u['UpLiftPlatformObject'] = f; u[f] = u[f] || function () { (u[f].q = u[f].q || []).push(arguments) }, u[f].l = 1 * new Date(); b = p.createElement(l), j = p.getElementsByTagName(l)[0]; b.async = 1; b.src = i + '?id=' + t; j.parentNode.insertBefore(b, j); var o = window.location.host.match(/[w-]+.w{2,3}(:d+)?$/); if (o) o = o[0]; u[f]('create', t, o) })(window, document, 'script', '//cdn.uplift-platform.com/a/up.js', 'up', UpCode)
				}, $rootScope.UpTimeout);

            }
			else {
				$rootScope.isUpEnabled = false;
			}
		}

		function initPayMonthly() {
			try {
				if (isCheckout && document.getElementById("up-pay-monthly-container") != null) {
					//initialize uplift for checkout phase
					window.Uplift.Payments.init({
						apiKey: UpAPI,
						locale: "en-US",
						currency: "USD",
						checkout: true,                         // set to true for Checkout phase
						container: "#up-pay-monthly-container", // CSS selector for Uplift iframe
						onChange: myOnChangeCallback
					});
				}
				else {
					//initialize uplift for shopping phase
					window.Uplift.Payments.init({
						apiKey: UpAPI,
						locale: "en-US",
						currency: "USD"
					});
				}
			} catch (e) {}
		}

		function myOnChangeCallback(response) {
			var statusHandlers = {
				OFFER_AVAILABLE: function () {
					//hide error prompt
					$("#uplifterror_mobile").hide();
					$("#uplifterror_web").hide();
					$("#upliftSegment").show();
				},
				TOKEN_AVAILABLE: function () {
					Uplift.Payments.getToken();
				},
				TOKEN_RETRIEVED: function () {
					var token = response.token;
					$rootScope.UpToken = token;
					//call to cde and pass token

					//hide confirm button when uplift application is not done
					var confirmBooking = $("#confirmBooking");
					var confirmBookingAmount = $("#confirmBookingAmount");

					if (confirmBooking && confirmBookingAmount) {
						confirmBooking.show();
						confirmBookingAmount.show();
					}
					var CDEToken = token.card_token;
					var OrderId = window.sessionStorage['up_br'];
					$rootScope.UpliftOrderId = OrderId;
					//send CDE token to api to save token into session
					haPaymentAPI.setUpliftToken(CDEToken, OrderId).success(function () {
						console.log("Token stored");
					}).error(function () {
						document.write();
						console.log("Token not stored");
					});
				},
				OFFER_UNAVAILABLE: function () {
					if (window.UpliftGlobals.ChangeCallback) {
						window.UpliftGlobals.ChangeCallback('OFFER_UNAVAILABLE');
					}
					//show error prompt
					if ($rootScope.isMobile) {
						$("#uplifterror_mobile").show();
					} else {
						$("#uplifterror_web").show();
					}
					//hide iframe
					$("#up-pay-monthly-container").hide();
					//disable pay monthly selector
					$("#check-monthly").attr('disabled', true);
					$("#upliftSegment").hide();
				},
				SERVICE_UNAVAILABLE: function () {
					if (window.UpliftGlobals.ChangeCallback) {
						window.UpliftGlobals.ChangeCallback('SERVICE_UNAVAILABLE');
					}
					//show error prompt
					if ($rootScope.isMobile) {
						$("#uplifterror_mobile").show();
					} else {
						$("#uplifterror_web").show();
					}
					$("#up-pay-monthly-container").hide();
					$("#check-monthly").attr('disabled', true);
					$("#upliftSegment").hide();
				}
			};
			statusHandlers[response.status]();
		}

		//compile trip info
		var upliftTripInfo = [];
		function buildTripInfo() {
			var travelersData = [];
			//send actual travelers information if used for checkout
			if (isCheckout) {
				var travellers = upliftTripInfo.itenerary.Travellers;
				var passengers = upliftTripInfo.itenerary.PassengerTripSummary.Passengers;

				//get passenger date of birth
				for (var i = 0; i < travellers.length; i++) {
					var month = travellers[i].DOBMonth < 10 ? "0" + travellers[i].DOBMonth : travellers[i].DOBMonth;
					var day = travellers[i].DOBDay < 10 ? "0" +travellers[i].DOBDay : travellers[i].DOBDay;
					travelersData.push({
						id: i + 1,
						first_name: passengers[i].FirstName,
						last_name: passengers[i].LastName,
						date_of_birth: month+"/"+day+"/"+travellers[i].DOBYear
					});
				}
			} else {
				//use generic traveler information for shopping phase
				var traveler = [
					{
						id: upliftTripInfo.id,
						first_name: 'Xxxxx',
						last_name: 'Xxxxx',
						date_of_birth: '01/01/2001',
					}
				]
				travelersData = traveler;
			}

			var itineraryData = [{
				departure_apc: upliftTripInfo.Origin,
				arrival_apc: upliftTripInfo.Destination,
				departure_time: upliftTripInfo.DepartureDate,
				arrival_time: upliftTripInfo.ArrivalDate
			}]

			//add itinerary data for checkout page
			if (isCheckout) {
				itineraryData = upliftTripInfo.iteneraryTrips;
			}

			var air_reservations = [
				{
					origin: upliftTripInfo.Origin,
					destination: upliftTripInfo.Destination,
					trip_type: upliftTripInfo.tripType,
					itinerary: itineraryData,
				}
			];
			//add insurance to trip info only if insurance is selected
			if (upliftTripInfo.IsTripInsuranceSelected) {
				air_reservations = [
					{
						origin: upliftTripInfo.Origin,
						destination: upliftTripInfo.Destination,
						trip_type: upliftTripInfo.tripType,
						itinerary: itineraryData,
						insurance: [
							{
								types: ["cancellation"],
								price: upliftTripInfo.insuranceCost * 100 ,//send insurance cost in cents
								price_per_person: (upliftTripInfo.insuranceCost / travellers.length) * 100
							}
						]
					}
				];
			}

			return {
				order_amount: Math.trunc(upliftTripInfo.order_amount * 100), //send itinerary cost in cents
				travelers: travelersData,
				air_reservations: air_reservations
			}
		};

	    // Array of promises to resolve first
		var upliftPrerequisites = [];

		//Check LD and other settings
		var launchDarklyDeferred = $q.defer();
		upliftPrerequisites.push(launchDarklyDeferred.promise);

	    // Ensure that the uplift placeholder elements are present
		var emitterDeferred = $q.defer();
		upliftPrerequisites.push(emitterDeferred.promise);

		var upliftEmitterFired = false;
		$rootScope.$on('UpliftElementReady', function () {
		    if (upliftEmitterFired) {
		        return;
		    }
		    upliftEmitterFired = true;
		    isUpliftEnabled();
		    emitterDeferred.resolve();
		});

		function isUpliftEnabled() {
			var islegacyIE = /MSIE|Trident/.test(window.navigator.userAgent);

			//check if browser is compatible
			if (upliftConfiguration.IsUpliftEnabled && !islegacyIE) {
		        launchDarklyDeferred.resolve()
		    } else {
				launchDarklyDeferred.reject()
		    }
		}

	    //// Is the Adobe Target flag enabled?
		var targetFlagDeferred = $q.defer();
		upliftPrerequisites.push(targetFlagDeferred.promise);

		var targetWatcher = $rootScope.$watch('EnableUplift', function (nv) {
			if (nv === true) {
				targetFlagDeferred.resolve();
			} else if (nv === false) {
				targetFlagDeferred.reject();
			}
		});

	    // Wait until all required checks have passed before loading the uplift js file
		$q.all(upliftPrerequisites).then(function () {
			$rootScope.isUpEnabled = true;
			window.UpliftGlobals.isUpliftEnabled = true;
			loadUpliftJS();
		});

        // Find if a teaser shoud be displayed
		$rootScope.showUpliftTeaser = function (key) {
			if (upliftConfiguration  && upliftConfiguration.ComponentCollection) {
				return (upliftConfiguration.ComponentCollection[key] ? upliftConfiguration.ComponentCollection[key] : false);
			}
			else {
				return false;
			}
		};

	    //called to trigger initialize uplift then send trip info
		window.upReady = function () {
		    initPayMonthly();
		    var tripInfo = buildTripInfo();
		    // load Pay Monthly and submit trip info to uplift
			try {
				window.Uplift.Payments.load(tripInfo);
				$("#upliftTabSelector").show();
		    } catch (e) {}
		}

		return {
			//reinitialize uplift
			upReady: function () {
				//ensure uplift is initialized before calling upready
				if (UpAPI !== '' && UpCode !== '') {
					window.upReady();
				}
			},
			//used for loading additional monthy offers in the page
			refreshUplift: function () {
				//ensure uplift is initialized before calling upready
				if (UpAPI !== '' && UpCode !== '') {
					try {
						var tripInfo = buildTripInfo();
						window.Uplift.Payments.load(tripInfo);
						CheckUpliftComponentEnabled();
					} catch (e) {}
				}
			},

			// Prepare flight data for uplift from the results.
			loadUplift: function (results, trip) {
				upliftTripInfo.tripType = (trip == 1 ? "oneway" : "roundtrip");
				if (results.ActiveTab != null) {
					angular.forEach(results.ActiveTab.TripAndFareDetails, function (result, i) {
						upliftTripInfo.Origin = result.TripSlice.Origin;
						upliftTripInfo.DepartureDate = (moment(result.TripSlice.DepartureDate).format('YYYY/MM/DD').toString()).replaceAll('/', '');
						upliftTripInfo.Destination = result.TripSlice.Destination;
						upliftTripInfo.ArrivalDate = (moment(result.TripSlice.ArrivalDate).format('YYYY/MM/DD').toString()).replaceAll('/', '');
						var tripid = 1;
						angular.forEach(result.FareDetails, function (cabin) {
							if (cabin.IsAvailable == true) {
								angular.forEach(cabin.FareTypes, function (fareInfo) {
									angular.forEach(fareInfo.FareInfos, function (baseFare) {
										upliftTripInfo.order_amount = baseFare.FiledInFareInfo.BaseFare;
										upliftTripInfo.id = tripid++;
									});
								});
							}
						});
					});
				}
			},
			// Prepare flight data for itineray page and checkout page to load monthly offers
			selectUplift: function (results, trips, isCheckoutPage, itenerary) {
				isCheckout = isCheckoutPage;
				var inteneraryData = [];
				angular.forEach(trips.AvailGridTrips, function (result) {
					var origin = result.Origin;
					var destination = result.Destination;
					var departure = (moment(result.DepartureDate).format('YYYY/MM/DD').toString()).replaceAll('/', '');
					var arrival = (moment(result.ArrivalDate).format('YYYY/MM/DD').toString()).replaceAll('/', '');
					var fareClass = result.CabinName;
					var departureCity = result.OriginCityName;
					var arrivalCity = result.DestinationCityName;

					inteneraryData.push({
						departure_apc: origin,
						departure_city: departureCity,
						arrival_apc: destination,
						arrival_city: arrivalCity,
						departure_time: departure,
						arrival_time: arrival,
						fare_class: fareClass,
						carrier_code: "HA",
						ticket_type: "digital",
						reservation_type: "standard",
						airline: "Hawaiian Airlines"
					})
					upliftTripInfo.Origin = origin;
					upliftTripInfo.DepartureDate = departure;
					upliftTripInfo.Destination = destination;
					upliftTripInfo.ArrivalDate = arrival;
					upliftTripInfo.id = result.TripID;
				});
				upliftTripInfo.tripType = (trips.TripType == 1 ? "oneway" : "roundtrip");
				upliftTripInfo.order_amount = results;
				upliftTripInfo.trips = trips;
				if (isCheckoutPage && itenerary) {
					upliftTripInfo.itenerary = itenerary;
					upliftTripInfo.iteneraryTrips = inteneraryData;
					if (itenerary.TripSummary.TripInsurance) {
						upliftTripInfo.insuranceCost = itenerary.TripSummary.TripInsurance.InsuranceCost;
					}
				}
				//window.upReady();
			},
			//called when uplift payment option is selected on checkout
			selectPayMonthly: function (isTripInsuranceSelected, isValidate) {
				if (isTripInsuranceSelected) {
					upliftTripInfo.IsTripInsuranceSelected = isTripInsuranceSelected;
				} else {
					upliftTripInfo.IsTripInsuranceSelected = false;
				}
				if ($rootScope.isUpEnabled) {
					this.refreshUplift();
					window.Uplift.Payments.select();
				}  
			},
			deselectPayMonthly: function (paymentMethod, isTripInsuranceSelected) {
				if ($rootScope.isUpEnabled) {
					upliftTripInfo.IsTripInsuranceSelected = isTripInsuranceSelected;
					this.refreshUplift();
					window.Uplift.Payments.deselect(paymentMethod);
				}
			},
			//for confirmation page
			confirmPayMonthly: function (confirmationId) {
				loadUpliftJS();
				window.upReady = function () {
					isCheckout = true;
					initPayMonthly();
					window.Uplift.Payments.confirm(confirmationId);
				}
			},
			getUpliftConfig: function () {
				$.ajax({
					cache: false,
					async: true, // async: false hangs the page.
					type: "GET",
					// timeout: 1000,
					url: window.location.origin + "/book/Uplift/UpliftConfiguration",
					success: function (data) {
						upliftConfiguration = data;
						if (upliftConfiguration.IsUpliftEnabled) {
							loadUpliftJS();
 						}
					},
					error: function (abc, error) {
						console.log(JSON.stringify(abc));
						console.log(error);
					}
				});
			}
		}
	}]);
	mod.directive('haUpliftEmitter', ['$rootScope', function ($rootScope) {
		return {
			restrict: 'A',
			link: function (el) {
				$rootScope.$broadcast('UpliftElementReady', el);
			}
		}
	}]);
})(angular);
;
(function (angular) {

    // Ha Ancillaries API Service
    // --------------------------------------------
    //
    // * **Class:** haAncillariesAPI
    // * **Author:** Nathan Probst
    //
    // Service for fetching itenerary add ons

    'use strict';

    var module;
    try {
        module = angular.module('haAncillariesModule');
    } catch (e) {
        module = angular.module('haAncillariesModule', ['haHttpService']);
    }

    module.service('haAncillariesAPI', [
		'$q',
		'haHttpService',
		'$cacheFactory',

		function ($q, http) {

		    var ITINERARY_DETAILS_URL = '/Book/Itinerary/GetItineraryDetails';
		    var ANCILLARIES_URL = '/Book/Itinerary/GetAncillaries';
		    var ANCILLARIES_URL_ASYNC = '/Book/Itinerary/GetAncillary?type={0}';
		    var ANCILLARIES_FALLBACK_URL = '/Content/assets/models/GetAncillaries-HNL-ITO-1A.json';
		    //var VACATION_PACKAGE_LIST_URL = '/Book/Itinerary/GetCompleteVacationPackageList?hotelID={0}';
		    var VACATION_PACKAGE_LIST_FALLBACK_URL = '/Content/assets/models/GetVacationPackageList-HNL-ITO-1A.json';
		    var NEAT_FALLBACK_URL = '/Book/Itinerary/NEATFallBackURL';
		    var ADD_HOTEL_URL = '/Book/Itinerary/AddHotel';
		    var REMOVE_HOTEL_URL = '/Book/Itinerary/RemoveHotel';
		    var SET_ROOMS_URL = '/Book/Itinerary/SetRooms';
		    var ADD_RENTAL_CAR_URL = '/Book/Itinerary/AddRentalCar';
		    var CAR_RENTAL_SEARCH = '/Book/Itinerary/GetCarRentalWidgetResults';
		    var GET_RENTAL_TERMS_URL = '/Book/Itinerary/GetRentalTerms';
		    var REMOVE_RENTAL_CAR_URL = '/Book/Itinerary/RemoveRentalCar';
		    var ADD_AIRPORT_SHUTTLE_URL = '/Book/Itinerary/AddAirportShuttle';
		    var REMOVE_AIRPORT_SHUTTLE_URL = '/Book/Itinerary/RemoveAirportShuttle';
		    var ADD_LEI_GREETING_URL = '/Book/Payment/AddLeiGreetings';
		    var REMOVE_LEI_GREETING_URL = '/Book/Payment/RemoveLeiGreetings';
		    var ADD_TRIP_INSURANCE_URL = '/Book/Payment/AddTripInsuranceToItinerary';
			var REMOVE_TRIP_INSURANCE_URL = '/Book/Payment/RemoveTripInsuranceFromItinerary';

		    var VACATION_PACKAGE_LIST_URL = function (hotelId) {
		        return '/Book/Itinerary/GetCompleteVacationPackageList?hotelID=' + hotelId;
		    };

		    var REPRICE_HOTEL_DETAILS_URL = function (hotelId) {
		        return '/Book/Itinerary/RepriceHotelDetails?hotelID=' + hotelId;
		    };

		    var checkErr = function (data) {
		        if ((data == null) || (typeof data.ErrorCode === 'string')) {
		            var err = {
		                code: data.ErrorCode,
		                message: data.ErrorMessage,
		                pathType: data.PathType,
		                redirectUrl: data.RedirectURL
		            };
		            return $q.reject(err);
		        }
		        return false;
		    };

		    var getUrl = function (url) {
		        return function (config) {
		            config = config || {};
		            config.cache = false;
		            config.headers = {
		                'Cache-Control': 'no-cache'
		            };

		            return http.GET(url, config)
					.then(function (response) {
					    var data = response.data;
					    var reject = checkErr(data);

					    if (reject) {
					        return reject;
					    }

					    if (typeof data === 'string') {
					        return JSON.parse(data);
					    }

					    return data;
					});
		        };
		    };

		    var postUrl = function (url) {
		        return function (json, config) {
		            config = config || {};
		            return http.POST(url, json, config)
					.then(function (response) {
					    var data = response.data;
					    var reject = checkErr(data);

					    if (reject) {
					        return reject;
					    }

					    if (typeof data === 'string') {
					        return JSON.parse(data);
					    }

					    return data;
					});
		        };
		    };

		    var urlBuster = function () {
		        return '?' + Math.random() * (100000 - 5555) + 5555;
		    };

			var getAllAncillaries = function (ancillaryTypes, timeout) {

		        if (!ancillaryTypes.length) {
		            var promise = $q.defer();
		            promise.reject('No eligible types');

		            return promise.promise;
		        }

		        var url = ANCILLARIES_URL_ASYNC,
					ancillaryPromises = {};

				ancillaryTypes.forEach(function (type) {
		            window.performance.clearMarks("mark_end_" + type);
					ancillaryPromises[type] = http.GET(url.format(type),
                        {
                            timeout: timeout
                        });
		            window.performance.mark("mark_end_" + type);
		            if (window.performance.getEntriesByName("mark_end_" + type) && window.performance.getEntriesByName("mark_end_" + type)[0]) {
		                var hotelMeasure = window.performance.getEntriesByName("mark_end_" + type)[0].startTime;
		                if (window.BOOMR && BOOMR.version) {
		                    window.BOOMR.sendTimer(type + "_Timer", hotelMeasure);
		                }
		            }
		        });

		        return $q.allSettled(ancillaryPromises);
		    };

		    function searchCarRentals(query) {
				return http.POST(CAR_RENTAL_SEARCH, JSON.stringify(query));
			}

		    return {
		        getItineraryDetails: getUrl(ITINERARY_DETAILS_URL + ((/Windows /i).test(window.navigator.userAgent) ? urlBuster() : '')),
		        getAncillaries: getUrl(ANCILLARIES_URL),
		        getAncillariesAsync: getAllAncillaries,
		        getAncillariesFallback: getUrl(ANCILLARIES_FALLBACK_URL),
		        //getVacationPackageList: getUrl(VACATION_PACKAGE_LIST_URL),
		        getVacationPackageListFallback: getUrl(VACATION_PACKAGE_LIST_FALLBACK_URL),
		        getNeatFallbackUrl: function () {
		            return getUrl(NEAT_FALLBACK_URL)().then(function (json) {
		                return json.url;
		            });
		        },
		        getRepriceHotelDetails: function (hotelId, config) {
		            config = config || {};
		            return getUrl(REPRICE_HOTEL_DETAILS_URL(hotelId))(config);
		        },
		        getVacationPackageList: function (hotelId, config) {
		            config = config || {};
		            return getUrl(VACATION_PACKAGE_LIST_URL(hotelId))(config);
		        },
		        addHotel: postUrl(ADD_HOTEL_URL),
		        removeHotel: postUrl(REMOVE_HOTEL_URL),
		        setRooms: postUrl(SET_ROOMS_URL),
		        addRentalCar: postUrl(ADD_RENTAL_CAR_URL),
		        getRentalTerms: postUrl(GET_RENTAL_TERMS_URL),
		        removeRentalCar: postUrl(REMOVE_RENTAL_CAR_URL),
				searchCarRentals: searchCarRentals,
		        addAirportShuttle: postUrl(ADD_AIRPORT_SHUTTLE_URL),
		        removeAirportShuttle: postUrl(REMOVE_AIRPORT_SHUTTLE_URL),
		        addLeiGreeting: postUrl(ADD_LEI_GREETING_URL),
		        removeLeiGreeting: postUrl(REMOVE_LEI_GREETING_URL),
		        addTripInsurance: postUrl(ADD_TRIP_INSURANCE_URL),
		        removeTripInsurance: postUrl(REMOVE_TRIP_INSURANCE_URL)
		    };
		}
    ]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haCitiesAPI', ['haHttpService'])
		.service('haCitiesAPI', ['haHttpService', '$cacheFactory', 'haConfig', 'haUtils', '$rootScope', '$q', function (http, $cacheFactory, haConfig, haUtils, $rootScope, $q) {
			// TODO: Refactor this caching HTTP service into its own service
			var httpCache = $cacheFactory.get('$http');
			var citiesVersion = $scs('Cities_Version').versionnumber || '5.0.7';
			var carLocations;
			var carLocationsById;
			var api = {

				CITY_LIST_URL: haConfig.getDynamicJsonUrl(['/Book/City/', citiesVersion, '/GetCities'].join('')),
				getCityList: function () {
					return http.GET(api.CITY_LIST_URL, { cache: httpCache, timeout: 10 * 1000 })
						.then(function (response) {
							$rootScope.$broadcast('citiesavailable');
							return response.data.CityList.Cities;
						});
				},

				CITY_PAIRS_URL: haConfig.getDynamicJsonUrl(['/Book/City/', citiesVersion, '/GetValidCities'].join('')),
				getCityPairs: function () {
					return http.GET(api.CITY_PAIRS_URL, { cache: httpCache, timeout: 10 * 1000 })
						.then(function (response) {
							return response.data.CityList.Cities;
						});
				},

				EXPERT_BOOKING_LIST_URL: haConfig.getDynamicJsonUrl(['/Book/City/', citiesVersion, '/GetCitiesExpertBooking'].join('')),
				getCityListExpertBooking: function () {
					return http.GET(api.EXPERT_BOOKING_LIST_URL, { cache: httpCache, timeout: 10 * 1000 })
						.then(function (response) {
							return response.data.CityList.Cities;
						});
				},

				EXPERT_BOOKING_PAIRS_URL: haConfig.getDynamicJsonUrl(['/Book/City/', citiesVersion, '/GetValidCitiesExpertBooking'].join('')),
				getCityPairsExpertBooking: function () {
					return http.GET(api.EXPERT_BOOKING_PAIRS_URL, { cache: httpCache, timeout: 10 * 1000 })
						.then(function (response) {
							return response.data.CityList.Cities;
						});
				},

				NITP_MAP_URL: '/Content/assets/models/NitpMap.json',
				getNitpMap: function () {
					return http.GET(api.NITP_MAP_URL, { cache: httpCache, timeout: 10 * 1000 })
						.then(function (response) {
							return response.data;
						});
				},

				FILTERED_CITY_LIST_URL: function (index, isDepartCity) {
					return '/Book/City/' + citiesVersion + '/FilterCityList?index=' + index + '&isDepartCity=' + isDepartCity;
				},
				getFilteredCityList: function (index, isDepartCity) {
					return http.GET(api.FILTERED_CITY_LIST_URL(index, isDepartCity), { cache: httpCache, timeout: 10 * 1000 })
						.then(function (response) {
							return (response.data.CityList != null) && response.data.CityList.Cities || [];
						});
				},

				EXPEDIA_SUGGEST_URL: function (query) {
					var base = 'https://suggest.expedia.com/api/v4/typeahead/';
					var maxResults = 5;
					var clientId = 'HawaiianHotelWidget';
					var siteId = '70209';
					var locale = haUtils.getStandardLocale();
					locale = locale === 'en_US' || locale === 'ja_JP' || locale === 'en_AU' || locale === 'en_NZ' || locale === 'ko_KR' ? locale : 'en_US';

					var afterQuery = '?regiontype=1631&locale=' + locale
						+ '&lob=HOTELS&format=json&ab=7140.1%7C7949.1&features=ta_hierarchy&maxresults=' + maxResults
						+ '&client=' + clientId + '&siteid=' + siteId;

					return base + query + afterQuery;
				},
				getExpediaCityList: function (query) {
					return http.GET(api.EXPEDIA_SUGGEST_URL(query), { cache: httpCache, timeout: 10 * 1000 })
						.then(function (response) {
							return response.data.sr;
						})
						.catch(function (error) {
							return error;
						});
				},
				RENTAL_CAR_LOCATION_URL: haConfig.getDynamicJsonUrl('/api/v2/cars/locations'),
				searchCarLocations: function (query, countries) {
					return api.getAllCarLocations().then(function (locations) {
						var results = filterCarLocations(query, locations[countries[0]]);
						if (results.length < 15 && countries[1] && countries[0] !== countries[1]) {
							results = results.concat(filterCarLocations(query, locations[countries[1]]));
						}

						// Truncate the results to 15 items.
						results.length = Math.min(results.length, 15);

						return results;
					});
				},
				getAllCarLocations: function () {
					if (carLocations) {
						//if fetch is in-progress- use that promise
						if (carLocations.then) {
							return carLocations;
						}
						// data already fetched & cached
						return $q.resolve(carLocations);
					}

					//store the promise to serve as a flag indicating that the fetch is in-progress
					carLocations = http.GET(api.RENTAL_CAR_LOCATION_URL, { cache: httpCache, timeout: 600 * 1000 })
						.then(function (response) {
							//overwrite the promise with the real data
							//group the locations by country
							carLocations = _.groupBy(response.data, 'country');
							carLocationsById = _.indexBy(response.data, 'id');

							//create a property with the combined text values. Used for auto-suggest searching
							angular.forEach(Object.keys(carLocations), function(k) {
								angular.forEach(carLocations[k], function (s) {
									s.text = ['', s.name, s.postal, s.city, s.airport].join(' ').toLocaleLowerCase();
								});
							});
							return carLocations;
						})
						.catch(function (error) {
							return error;
						});
					return carLocations; // not really the carLocations data yet... it's the promise at this point.
				},
				getCarLocationById: function getCarLocationById(id) {
					return api.getAllCarLocations().then(function(){
						return carLocationsById[id];
					});
				}
			};

			function filterCarLocations(query, locations) {
				query = query.toLocaleLowerCase();

				if (!query) {
					return locations;
				}

				// Step 1) Filter full location list by what they have typed in
				var results = locations.filter(function (s) { return s.text.indexOf(query) > -1; });

				// Step 2) Sort by items that have the text they typed [at the beginning] of a word. (if text is in the middle, the result goes to the bottom of the list)
				results = results.sort(function (a, b) {
					return test(b.text, query) - test(a.text, query);
				});

				return results;
			}
			function test(txt, query) { return Number(txt.indexOf(' ' + query) > -1); }

			return api;
		}]);

})(angular);
;
(function (angular) {

	'use strict';

	var mod = angular.module('haEcertAPI', ['haHttpService']);

	mod.service('haEcertAPI', ['haHttpService', function (http) {
		return {
			getPassengerCredit: function (model) {
				return http.POST('/TravelCreditRedemption/GetPassengerCredit', model);
			},
			selectDuplicatePassenger: function (index) {
				return http.POST('/TravelCreditRedemption/SelectDuplicatePassenger', {
					index: index
				});
			},
			fetchPopup: function (name) {
				return http.GET('/TravelCreditRedemption/' + name);
			},
			isValidPromo: function (promoEligibilityRequest) {
				return http.POST('/Book/Home/PromotionValid', promoEligibilityRequest);
			},
			validateRedeemPromoCode: function (promoCode) {
			    return http.POST('/Book/ECert/ValidateRedeemPromoCode', {
                    OfferCode: promoCode
			    });
			},
		    removePromoCodeFromSession: function () {
		        return http.POST('/Book/ECert/RemovePromoCodeFromSession');
		    },
		    HandleAffiliates: function (promoCode) {
		        return http.POST('/Program/Affiliate/ValidateAffiliate', {
		            promoCode: promoCode
		    });
		}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haEmailAPI', ['haHttpService'])

	.service('haEmailAPI', ['haHttpService', function (http) {
		return {
			emailAvailabilityCheck: function (data, canceler) {
				return http.POST('/MyAccount/EmailAvailability/EmailAvailabilityCheck',
					{emailAddress: data},
					{timeout: canceler.promise}
				).then(function (response) {
					return response.data.IsSuccess ||
						(response.data.Message === 'Internal Processing Error');	// Don't block the user if we're broken.
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haUsernameAPI', ['haHttpService'])

	.service('haUsernameAPI', ['haHttpService', function (http) {
		return {
			checkAvailability: function (username, canceler) {
				return http.POST('/MyAccount/UserNameAvailability/UserNameAvailabilityCheck',
					{userName: username},
					{timeout: canceler.promise}
				).then(function (response) {
					return response.data.IsSuccess ||
						(response.data.Message === 'service Error');	// Don't block the user if we're broken.
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Flight Results Service
	// --------------------------------------------
	//
	// * **Class:** haFlightResultsAPI
	// * **Author:** Josh Nielsen & Nathan Probst
	//
	// Service for fetching flight results

	'use strict';

	var mod = angular.module('haFlightResultsAPI', ['haHttpService']);

	mod.service('haFlightResultsAPI', ['haHttpService', function (http) {
		return {

			SEARCH_FLIGHTS_URL: '/Book/FlightResults/SearchFlights',
			fetch: function (/* searchRequestKey */) {
				return http.POST(this.SEARCH_FLIGHTS_URL, {});
			},

			// temporary method that fetches an alternative tab, in the end the booking dates should just be changed and then use fetch
			SEARCH_FLIGHTS_ALTERNATE_DATE_URL: '/Book/FlightResults/SearchFlightsAlternateDate',
			fetchTab: function (TripID, SelectedDate /* , searchRequestKey */) {
				return http.POST(this.SEARCH_FLIGHTS_ALTERNATE_DATE_URL, {
					tripId: TripID,
					selectedDate: SelectedDate
				});
			},

			SEARCH_FLIGHTS_STEP_THROUGH_URL: '/Book/FlightResults/SearchFlightsStepThrough',
			SearchFlightsStepThrough: function (jdDataList, TripId, isEdit) {
				return http.POST(this.SEARCH_FLIGHTS_STEP_THROUGH_URL, {
					JDDataList: jdDataList.JDDataList,
					tripId: TripId,
					isEdit: isEdit
				});
			},

			// temporary method that fetches an alternative currency result set, in the end the booking currency should just be changed and then use fetch
			fetchCurrency: function () {
				return http.GET('/scripts/modules/BOS_BKK_miles.json');
			},

			bookFlights: function (selectedfares, tripsummary) {
				return http.POST('/Book/FlightResults/ContinueBooking', {
					selectedFares: selectedfares,
					tripSummary: tripsummary
				});
			},

			// Method that fetch Modal content from sitecore for the passed modal name
			fetchModalContent: function (modalName) {
				return http.GET('/Book/FlightResults/ModalContent', {
					params: {
						modalName: modalName
					}
				});
			},

			checkFlightAvailability: function (selectedfares) {
				return http.POST('/Book/FlightResults/CheckFlightAvailability', {
					selectedFares: selectedfares
				});
			},

			fetchPopup: function (name) {
				return http.GET('/Book/FlightResults/' + name);
			},

			toggleCurrencyMilesService: function (segmentId, isMiles) {
				return http.POST('/Book/FlightResults/toggleCurrencyMiles', null, {
					params: {
						segmentID: segmentId,
						isMiles: isMiles
					}
				});
			},

			updateChangeFlightModel: function () {
				return http.POST('/Book/FlightResults/EditSearch');
			},

			updateSelectedHoldFare: function (selectedFare) {
				return http.POST('/Book/FlightResults/UpdatedSelectedHoldFare', {
					selectedfare: selectedFare
				});
			},

			fetchOnTimePerformanceDetails: function (flightNumbers) {
			    return http.GET('/Book/FlightResults/FetchOnTimePerformanceDetails', {
			        params: {
			            flightNumbers: flightNumbers
			        }
			    })
			}
		};
	}]);

	/*
	 mod.config(['$injector', function ($injector) {
	 var haSessionDataProvider;
	 try {
	 haSessionDataProvider = $injector.get('haSessionDataProvider');
	 haSessionDataProvider.when('POST', mod.constant('SEARCH_FLIGHTS_URL'), function (session) {
	 var data;
	 if (session != null) {
	 data = angular.copy(session.GLOBALS) || {};
	 data.Account = angular.copy(session.Account);
	 data.Discounts = angular.copy(session.Discounts);
	 data.Passengers = angular.copy(session.Passengers);
	 data.Trips = angular.copy(session.FlightResultTrips);
	 //data.Reshop = angular.copy(session.Reshop);
	 }
	 return data;
	 });
	 } catch (e) {}
	 }]);
	 */

})(angular);
;
(function (angular) {

	// Ha Flight Results Service
	// --------------------------------------------
	//
	// * **Class:** haFlightResultsEndOnEndAPI
	// * **Author:** Mari Yamaha
	//
	// Service for fetching flight results

	'use strict';

	var mod = angular.module('haFlightResultsEndOnEndAPI', ['haHttpService']);

	mod.service('haFlightResultsEndOnEndAPI', ['haHttpService', function (http) {
		return {

			selectPromo: function (offerId) {
				return http.POST('/Book/FlightResults/SelectPromo', {
					OfferId: offerId
				});
			},

			removePromo: function () {
				return http.POST('/Book/FlightResults/RemoveSelectedPromo', {});
			},

			SEARCH_FLIGHTS_URL: '/Book/FlightResults/SearchFlightsEndOnEnd',
			fetch: function (/* searchRequestKey */) {
				return http.POST(this.SEARCH_FLIGHTS_URL, { priority: 6 });
			},

			changeMilesOption: function (type) {
				return http.POST('/Book/FlightResults/ChangeMilesOption', {
					pricingType: type
				});
			},

			// temporary method that fetches an alternative tab, in the end the booking dates should just be changed and then use fetch
			SEARCH_FLIGHTS_ALTERNATE_DATE_URL: '/Book/FlightResults/SearchFlightsAlternateDateEndOnEnd',
			fetchTab: function (jdDataList, TripID, SelectedDate /* , searchRequestKey */) {
				return http.POST(this.SEARCH_FLIGHTS_ALTERNATE_DATE_URL, {
					JDDataList: jdDataList,
					tripId: TripID,
					selectedDate: SelectedDate
				});
			},

			SEARCH_FLIGHTS_STEP_THROUGH_URL: '/Book/FlightResults/SearchFlightsStepThroughEndOnEnd',
			SearchFlightsStepThrough: function (jdDataList, TripId, isEdit) {
				return http.POST(this.SEARCH_FLIGHTS_STEP_THROUGH_URL, {
					JDDataList: jdDataList,
					tripId: TripId,
					isEdit: isEdit
				});
			},

			GET_TRIP_SUMMARY_URL: '/Book/FlightResults/FlightResultsTripSummary',
			GetTripSummary: function (jdDataList) {
				return http.POST(this.GET_TRIP_SUMMARY_URL, {
					JDDataList: jdDataList
				});
			},

			// temporary method that fetches an alternative currency result set, in the end the booking currency should just be changed and then use fetch
			fetchCurrency: function () {
				return http.GET('/scripts/modules/BOS_BKK_miles.json');
			},

			bookFlights: function (selectedfares, tripsummary) {
				return http.POST('/Book/FlightResults/ContinueBookingEndOnEnd', {
					selectedFares: selectedfares,
					tripSummary: tripsummary
				});
			},

			// Method that fetch Modal content from sitecore for the passed modal name
			fetchModalContent: function (modalName) {
				return http.GET('/Book/FlightResults/ModalContent', {
					params: {
						modalName: modalName
					}
				});
			},

			checkFlightAvailability: function (selectedfares) {
				return http.POST('/Book/FlightResults/CheckFlightAvailabilityEndOnEnd', {
					selectedFares: selectedfares
				});
			},

			fetchPopup: function (name) {
				return http.GET('/Book/FlightResults/' + name);
			},

			toggleCurrencyMilesService: function (segmentId, isMiles) {
				return http.POST('/Book/FlightResults/toggleCurrencyMiles', null, {
					params: {
						segmentID: segmentId,
						isMiles: isMiles
					}
				});
			},

			updateChangeFlightModel: function () {
				return http.POST('/Book/FlightResults/EditSearch');
			},

			updateSelectedHoldFare: function (selectedFare) {
				return http.POST('/Book/FlightResults/UpdatedSelectedHoldFare', {
					selectedfare: selectedFare
				});
			},

			fetchOnTimePerformanceDetails: function (flightNumbers) {
				return http.GET('/Book/FlightResults/FetchOnTimePerformanceDetails', {
					params: {
						flightNumbers: flightNumbers
					}
				});
			},

			fetchPlaneConfiguration: function () {
				return http.GET('/planes/configuration', { priority: 1 });
			},
			fetchPlaneMarketingData: function () {
				return http.GET('/planes/marketingdata', { priority: 1 });
			},

			getSocialProofData: function (origin, destination, departureDate, flightResultCount) {
				return http.GET('/Book/FlightResults/GetSocialProofData', {
					params: {
						origin: origin,
						destination: destination,
						departureDate: departureDate,
						flightResultCount: flightResultCount
					}
				});
			},

			getRedirectionUrl: function () {
			    return http.GET('/Book/FlightResults/GetFlightResultsRedirectionPath');
			}
		};
	}]);
})(angular);
;
(function (angular) {

	// Ha Global Header Service
	// --------------------------------------------
	//
	// * **Class:** haGlobalHeaderAPI
	// * **Author:** Cinthia Miller & Nathan Probst
	//
	// Service for Global Header


	'use strict';

	var mod = angular.module('haGlobalHeaderAPI', ['haHttpService']);

	mod.service('haGlobalHeaderAPI', ['haHttpService',
		function (http) {
			return {
				selectCountry: function (country, itemId) {
					return http.POST('/Header/SelectCountry', {
						selectedCountry: country,
						contextItemId: itemId
					});
				},

				searchGoogle: function (searchText) {
					return http.POST('/Header/NewSearch', {
						GoogleText: searchText
					});
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var mod = angular.module('haInterstitialAPI', ['haHttpService']);

	mod.service('haInterstitialAPI', ['haHttpService', 'haConfig', function (http, haConfig) {
		return {
			getAirAvailability: function () {
				return http.GET(haConfig.getRazorTemplateUrl('InterstitialAirAvailability'));
			},
			changeSeatInterstitial: function () {
				return http.GET('/my-account/my-trips/Shared/InterstitialUpdateSeats');
			}
		};
	}]);

})(angular);;
(function (angular) {

	'use strict';

	angular.module('haItineraryAPI', ['haHttpService'])

	.service('haItineraryAPI', ['haHttpService', function (http) {
		return {
			updatePassengerInfo: function (pnr, travellerInformation) {
				return http.POST('/MyAccount/ItineraryDetails/UpdatePassengerInfo', {
					pnr: pnr,
					travellerInformation: travellerInformation
				});
			},

			GetReceiptInfo: function (ticketNo) {
				return http.POST('/MyAccount/ItineraryDetails/GetReceiptInfo', {
					ticketNo: ticketNo
				});
			},

			UpdatePaxAndFlightDetails: function (ChangeFlightModel) {
				return http.POST('/MyAccount/ItineraryDetails/UpdatePaxAndFlightDetails', {
					ChangeFlightModel: ChangeFlightModel
				});
			},

			isValidHMAccount: function (hmNumber, firstName, lastName) {
				return http.POST('/MyAccount/ItineraryDetails/IsValidHMAccount', {
					hmNumber: hmNumber,
					firstName: firstName,
					lastName: lastName,
					timeout: 60000
				});
			},

			HMEnrollAndUpdatePNR: function (pnr, travellerInformation) {
				return http.POST('/MyAccount/ItineraryDetails/HMEnrollAndUpdatePassengerInfo', {
					pnr: pnr,
					travellerInformation: travellerInformation,
					timeout: 60000
				});
			},

			IsEligibleForChangeFlight: function () {
				return http.POST('/MyAccount/ItineraryDetails/IsEligibleForChangeFlight');
			},

			CancelHoldTrip: function (cancelHoldTripRQ) {
				return http.POST('/MyAccount/ItineraryDetails/CancelHoldTrip', {
					cancelHoldTripRequest: cancelHoldTripRQ
				});
			},

			GroupPNRSelectTravellers: function (travellers) {
				return http.POST('/MyAccount/ItineraryDetails/GroupPNRSelectTravellers', {
					travellers: travellers
				});
			},
			CancelWaiverPnr: function (pnr, lastName, optionalCancellationEmailAddress) {
				return http.POST('/MyAccount/ItineraryDetails/CancelWaiverPnr', {
					pnr: pnr,
					lastName: lastName,
					optionalCancellationEmailAddress: optionalCancellationEmailAddress
				});
			},

		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haLoginAPI', ['haHttpService'])

	.service('haLoginAPI', ['haHttpService', function (http) {

		return {
			login: function (formData) {
				return http.POST('/MyAccount/Login/Login', formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			pageLoad: function () {
				return http.GET('/MyAccount/Login/AuthenticateLoad?dummy=' + Math.random());
			},

			ResetEmail: function (NewEmailAddress) {
				return http.POST('/MyAccount/Login/ResetEmailAddressUpdate', {
					newEmailAddress: NewEmailAddress
				});
			},

			ResetEmailAddress: function (formData) {
			    return http.POST('/MyAccount/Login/ResetEmailAddressLoad', formData, {
			        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
			    });
			},

			ValidateSecurityAnswers: function (AccountNo, Question1, Question2, Question3, Answer1, Answer2, Answer3) {
				return http.POST('/MyAccount/Login/ForgotEmailValidateSecurityAnswersSubmit', {
					accountNo: AccountNo,
					question1: Question1,
					question2: Question2,
					question3: Question3,
					answer1: Answer1,
					answer2: Answer2,
					answer3: Answer3
				});
			},

			UpdatePassword: function (formData) {
				return http.POST('/MyAccount/Login/UpdatePassword', formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			ForgotEmailAddressSubmit: function (fname, lname, phoneNo, country, zipCode) {
				return http.POST('/MyAccount/Login/ForgotEmailAddressLoad', {
					firstName: fname,
					lastName: lname,
					phoneNumber: phoneNo,
					country: country,
					zipCode: zipCode
				});
			},

			CreateUsername: function (userName) {
				return http.POST('/MyAccount/Login/CreateUsernameSubmit', {
					username: userName
				});
			},

			AuthenticateEmailOnly: function (email, zip) {
				return http.POST('/MyAccount/EmailOnly/AuthenticateEmailOnly', {
					email: email,
					zip: zip
				});
			},

			EmailOnlyPreferencesSubmit: function (data) {
				return http.POST('/MyAccount/EmailOnly/GetEmailOnlyPreferences', {
					EmailOnlyPreferences: data
				});
			},

			UnsubscribeEmailAddressSubmit: function (EmailAddress, UnSubscriptionID, IsUnsubscribeAll) {
				return http.POST('/MyAccount/EmailOnly/EmailUnsubscribe', {
					EmailAddress: EmailAddress,
					UnSubscriptionID: UnSubscriptionID,
					UnSubscribeAll: IsUnsubscribeAll
				});
			},

			UpdateEmailPreferencesSubmit: function (EmailAddress, ZipCode) {
				return http.POST('/MyAccount/EmailOnly/Unsubscribe', {
					EmailAddress: EmailAddress,
					ZipCode: ZipCode
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Passengers API
	// --------------------------------------------
	//
	// * **Class:** haPassengersAPI
	// * **Author:** Josh Nielsen & Nathan Probst
	//
	// Service for managing passengers

	'use strict';

	var mod = angular.module('haPassengersAPI', ['haHttpService']);

	mod.service('haPassengersAPI', ['haHttpService',
		function (http) {
			return {
				addeditpax: function (guest) {
					return http.POST('/Book/Pax/AddEditPax', {
						travellerInformation: guest
					});
				},

				editPaxLink: function (/* guest */) {
					return http.POST('/Book/Pax/PaxLoadLoggedIn');
				},

				editLoggedInPax: function (guest) {
					return http.POST('/Book/Pax/EditLoggedInPax', {
						editLoggedInTraveler: guest
					});
				},

				addPaxToPNR: function (guest) {
					return http.POST('/Book/Pax/AddPaxToPNR', {
						travellerInformationList: guest
					});
				},

				addExtraLoggedTravellerInfo: function (guest) {
					return http.POST('/Book/Pax/AdditionalLoggedInTravelerInfo', {
						additionalLoggedInTravelerInfo: guest
					});
				},

				associateinfants: function (selectedtravelers) {
					return http.POST('/Book/Pax/AssociateInfants', {
						travellerInformationList: selectedtravelers
					});
				},

				updatecontactinfo: function (contactinfo) {
					return http.POST('/Book/Pax/UpdateContactInfo', {
						ContactInfo: contactinfo
					});
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haPaymentAPI', ['haHttpService'])

	.service('haPaymentAPI', ['haHttpService', function (http) {
		return {
			FlightAvailability: function (formData) {
				return http.POST('/Book/Payment/FlightAvailability', formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			validateCreditCard: function (formData) {
				return http.POST('/Book/Payment/ValidateCreditCard', formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			ValidatePaymentAuthorization: function (formData) {
				return http.POST('/Book/Payment/ValidatePaymentAuthorization', formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			fetchScheduleMismatchPopup: function () {
				return http.GET('/Book/Payment/FlightScheduleMismatch');
			},

			postPaymentProcess: function (formData, updateSchedule, Token) {
				var url = '/Book/Payment/ProcessPayment?UpdateSchedule=' + updateSchedule + '&Token=' + Token;
				return http.POST(url, formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			postProcessPaymentForAsyncPNR: function (formData, updateSchedule, Token) {
				var url = '/Book/Payment/ProcessPaymentForAsyncPNR?UpdateSchedule=' + updateSchedule + '&Token=' + Token;
				return http.POST(url, formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			postProcessPaymentForHoldPNR: function (formData, updateSchedule, Token) {
				var url = '/Book/Payment/ProcessPaymentForHoldPNR?UpdateSchedule=' + updateSchedule + '&Token=' + Token;
				return http.POST(url, formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			postProcessNITPRedemption: function () {
				var url = '/Book/Payment/ProcessNITPRedemption';
				return http.POST(url, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},

			removePriceLockSession: function () {
				return http.POST('/Book/Payment/RemovePriceLockSession');
			},

			getPaymentOptionsState: function () {
				return http.GET('/Book/Payment/GetPaymentMethods');
			},

			fetchModalContent: function (modalName) {
				return http.GET('/Shared/TermsAndConditions/ModalContent', {
					params: {
						modalName: modalName
					}
				});
			},

			getPaymentMethodsState: function () {
			    return http.POST('/Book/Payment/GetPaymentMethods');
			},

			fetchMasterPassToken: function (purchaseType) {
				return http.POST('/Book/MasterPass/GetMasterPassRequestToken', {
					purchaseType: purchaseType
				});
			},

			fetchMasterPassPaymentInfo: function (data) {
				return http.POST('/Book/MasterPass/GetMasterPassPaymentInfo', {
					oauthToken: data.oauth_token,
					oauthVerifier: data.oauth_verifier,
					checkOutUrl: data.checkout_resource_url
				});
			},

			getAliPaySignedUrl: function () {
				return http.POST('/Api/AliPay/GetAliPaySignedUrl');
			}, 

			upgradeToMainCabin: function () {
				return http.POST('/Book/Payment/UpgradeToMainCabin');
			},

			getItineraryDetails: function () {
				return http.GET('/Book/Itinerary/GetItineraryDetails');
			},

			setUpliftToken: function (Token,OrderId) {
				return http.POST('/Book/Payment/SetUpliftToken',{
					Token: Token,
					OrderId: OrderId
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haPaymentMethodsAPI', ['haHttpService'])

	.service('haPaymentMethodsAPI', ['haHttpService', function (http) {
		return {
			PaymentMethodsAddEdit: function (formData) {
				return http.POST('/MyAccount/PaymentMethods/PaymentMethodAddEdit', formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'}
				});
			},
			PaymentMethodsDelete: function (ccID) {
				return http.POST('/MyAccount/PaymentMethods/PaymentMethodDelete', null, {
					params: {
						ccID: ccID
					}
				});
			},
			PaymentMethodsAddInitialize: function () {
				return http.POST('/MyAccount/PaymentMethods/PaymentMethodAdd');
			},
			PaymentMethodsAdd: function (data) {
				return http.POST('/MyAccount/PaymentMethods/PaymentMethodAddEdit', {
					ccPaymentInfo: data
				});
			},
			getCDESavedCard: function (ccID) {
				return http.GET('/MyAccount/PaymentMethods/GetCDEDataForSavedCard', {
					cache: false,
					params: {
						ccID: ccID
					}
				});
			},
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Global Header Service
	// --------------------------------------------
	//
	// * **Class:** haProfileSettingsAPI
	// * **Author:** Cinthia Miller & Nathan Probst
	//
	// Service for Global Header

	'use strict';

	var mod = angular.module('haProfileSettingsAPI', ['haHttpService']);

	mod.service('haProfileSettingsAPI', ['haHttpService',
		function (http) {
			return {
				updateAccountSettings: function (accountSettingsModel) {
					return http.POST('/MyAccount/ProfileSettings/AccountSettingsSubmit', {
						accountSettingsVM: accountSettingsModel
					});
				},

				updateTravelPreferences: function (formData) {
					return http.POST('/MyAccount/ProfileSettings/TravelPreferencesLoad', formData, {
						headers: {'Content-Type': 'application/x-www-form-urlencoded'}
					});
				},

				updateEmailSubscrption: function (data) {
					return http.POST('/MyAccount/ProfileSettings/EmailSubscriptionsSubmit', {
						emailSubscriptionviewmodel: data
					});
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haPurchaseMilesAPI', ['haHttpService'])

	.service('haPurchaseMilesAPI', ['haHttpService', function (http) {
		return {
			checkEligibility: function (milesSelected, sourceClient, AccountNumber, LastName, RecipientEmail) {
				return http.POST('/MyAccount/PurchaseMiles/PurchaseMilesEligibility', {
					selectedMiles: milesSelected,
					iSGiftPurchaseSelected: sourceClient,
					recipientAccountNumber: AccountNumber,
					recipientlastName: LastName,
					recipientEmail: RecipientEmail
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Passengers API
	// --------------------------------------------
	//
	// * **Class:** haPassengersAPI
	// * **Author:** Josh Nielsen & Nathan Probst
	//
	// Service for managing passengers

	'use strict';

	var mod = angular.module('haSeatMapAPI', ['haHttpService']);

	mod.service('haSeatMapAPI', ['haHttpService',
		function (http) {
			return {
				//Get seat map for a leg
				getSeatMapForLeg: function (seatmaprequestdata, isPreview) {

					//return http.POST('/Book/InFlightOptions/GetSeatMap', {
					//	seatMapRequest: seatmaprequestdata
					//});

					return http.POST((!isPreview) ? '/Book/InFlightOptions/GetSeatMap' : '/Book/InFlightOptions/GetSeatMapPreview', {
						seatMapRequest: seatmaprequestdata
					});
				},

				//update seat selections to serverUpdateIEditSeatsService
				UpdateInflightOptionsService: function (seatmapmodel, seatmapstring) {
					return http.POST('/Book/InFlightOptions/UpdateInflightOptions', {
						seatmapSelection: seatmapmodel,
						model: seatmapstring
					});
				},

				UpdateChangeFlightInflightoptions: function (seatmapmodel, seatmapstring) {
					return http.POST('/Book/InFlightOptions/UpdateChangeFlightInflightoptions', {
						seatmapSelection: seatmapmodel,
						model: seatmapstring
					});
				},
				ShowDownGradePopUp: function (seatmapmodel) {
					return http.POST('/Book/InFlightOptions/ShowDownGradePopUp', {
						seatmapSelection: seatmapmodel
					});
				},

				//Get seat map for a leg
				getSeatMapForPostLeg: function (seatmaprequestdata) {
					return http.POST('/MyAccount/PostPurchaseEditSeats/GetSeatMap', {
						seatMapRequest: seatmaprequestdata
					});
				},
				//update EditSeats selections to server
				ValidateEditSeatFareDifference: function (seatmapmodel) {
					return http.POST('/MyAccount/PostPurchaseEditSeats/ValidateEditSeatFareDifference', {
						seatMapSelection: seatmapmodel
					});
				},
				ConfirmEditSeatsService: function (seatmapmodel) {
					return http.POST('/MyAccount/PostPurchaseEditSeats/ConfirmEditSeats', {
						model: seatmapmodel
					});
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haTravelersAPI', ['haHttpService'])

	.service('haTravelersAPI', ['haHttpService', function (http) {
		return {
			TravelerAddEdit: function (formData) {
				return http.POST('/MyAccount/TravelersList/TravelerAddEdit', formData, {
					headers: {'Content-Type': 'application/x-www-form-urlencoded'},
				});
			},

			TravelerDelete: function (travelerID) {
				return http.POST('/MyAccount/TravelersList/TravelerDelete', null, {
					params: {
						travelerID: travelerID
					}
				});
			},

			// New endpoints
			GetTravelers: function (include) {
				return http.GET('/my-account/travelers', {
					params: {
						include: include
					}
				});
			},

			AddEditTraveler: function(travelerData, temp) {
				var params = temp ? { temp: true } : undefined;
				return http.POST('/my-account/travelers/TravelerAddEdit', travelerData, {
					params: params
				});
			},
			getUserType: function () {
				return http.POST('/my-account/Travelers/getUserType');
			}


		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haUserProfileAPI', ['haHttpService'])

	.service('haUserProfileAPI', ['haHttpService', function (http) {
		return {
			checkUsernameAvailability: function (data, canceler) {
				return http.POST('/MyAccount/UserProfile/CheckUsernameAvailability', data, {
					timeout: canceler.promise
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haPriceApiModule', ['haHttpService'])

	.service('haPriceApiService', ['$http', '$q', '$rootScope', 'haConfig', function ($http, $q, $rootScope, haConfig) {

		var enableGetRequest = $rootScope.$switch('BookingWidget:EnableDealsandOffersGetReq'),
			action = enableGetRequest ? 'GET' : 'POST';

		return {
			fetchInitialPrices: function (options) {
				var fetchInitialUrlFormat = '/Book/LowFareSearchChart/GetPriceChart{0}',
					queryString = enableGetRequest ? '?searchRequest=' + JSON.stringify(options.data.searchRequest) + '&isCalendar=' + JSON.stringify(options.data.isCalendar) : '',
					initialUrl = fetchInitialUrlFormat.format(queryString),
					//initialUrl = enableGetRequest ? haConfig.getDynamicJsonUrl(fetchInitialUrl) : fetchInitialUrl,
					promise = $q.defer(),
					postBody = enableGetRequest ? {} : options.data;

				// GET has a TTL set through Akamai
				$http({ method: action, url: initialUrl, data: postBody }).then(
					function (response) {
						promise.resolve(response.data);
					},
					function (err) {
						promise.reject(err);
					}
				);

				return promise.promise;
			},

			fetchPrices: function (startDate, direction, options) {
				options = options || {};
				var config = options.config;
				var url = options.url || '/Book/LowFareSearchChart/GetPriceChartAdditionalDays';
				return $http.post(url, { dateStr: startDate, direction: direction }, config);
			}
		};
	}]);

})(angular);
;
// Ha Geo Data API
// ---------------
//
// * **Class:** haGeoDataAPI
// * **Author:** Nathan Probst
//
// Service for fetching geo data

(function (angular) {
	'use strict';

	var module;
	try {
		module = angular.module('haGeoDataModule');
	} catch (e) {
		module = angular.module('haGeoDataModule', ['haHttpService']);
	}

	module.service('haGeoDataAPI', ['haHttpService', 'haConfig', function (http, config) {
		return {
			GET_COUNTRIES_URL: function () {
				return '/MyAccount/AccountShared/GetCountries' +
					'?languagecode=' + config.getLanguageCode();
			},
			getCountries: function () {
				return http.GET(this.GET_COUNTRIES_URL())
				.then(function (response) {
					return response.data;
				});
			},
			GET_STATES_URL: function (countryKey) {
				return '/MyAccount/AccountShared/GetStatesByCountry' +
					'?countrykey=' + countryKey +
					'&languagecode=' + config.getLanguageCode();

			},
			getStates: function (countryKey) {
				return http.GET(this.GET_STATES_URL(countryKey))
				.then(function (response) {
					return response.data;
				});
			},
			GET_STATE_AND_CITIES_URL: function (countryKey, postalCode) {
				return '/MyAccount/AccountShared/GetCityAndStateByCountryAndPostalCode' +
					'?countrykey=' + countryKey +
					'&postalcode=' + postalCode +
					'&languagecode=' + config.getLanguageCode();
			},
			getStateAndCities: function (countryKey, postalCode) {
				return http.GET(this.GET_STATE_AND_CITIES_URL(countryKey, postalCode))
				.then(function (response) {
					return response.data;
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Book Non Member Ecert Api
	// --------------------------------------------
	//
	// * **Class:** haBookNonMemberEcertAPI
	// * **Author:** Mari Yamaha
	//
	// Service for Non Member Ecert

	'use strict';

	var mod = angular.module('haBookNonMemberEcertAPI', ['haHttpService']);

	mod.service('haBookNonMemberEcertAPI', ['haHttpService', function (http) {
		return {

			isGAFETCO: function (request) {
				return http.POST('/Book/ECert/IsGAFETCO', {
					validateNoNameOfferRequest: request
				});
			},

			validatePromo: function (request) {
				return http.POST('/Book/ECert/ValidateRedeemEcert', {
					validateNoNameOfferRequest: request
				});
			},

			validateAccount: function (request) {
				return http.POST('/Book/ECert/ValidateRedeemHMNumber', {
					validateHMNumberRequest: request
				});
			},

			depositMiles: function (request) {
				return http.POST('/Book/ECert/DepositMilesToUserAccount', {
					depositMilesRequest: request
				});
			}
		};

	}]);

})(angular);
;
(function (angular) {

	// Ha My Account API
	// --------------------------------------------
	//
	// * **Class:** haMyAccountAPI
	// * **Author:** Cory Shaw
	//
	// Service for My Account Endpoints

	'use strict';

	var mod = angular.module('haMyAccountAPI', ['haHttpService']);

	mod.service('haMyAccountAPI', ['haHttpService', function (http) {
		return {

			getcontactsalesrepform: function () {
				return http.GET('/myaccount/mydashboardform/getcontactsalesrepform');
			},

			getchangerequestform: function () {
				return http.GET('/myaccount/mydashboardform/getchangerequestform');
			},

			getcorpwebaccountupdate: function () {
				return http.GET('/myaccount/mydashboardform/getcorpwebaccountupdate');
			}
		};

	}]);

})(angular);
;
(function () {
	'use strict';

	angular.module('haSessionTimeoutAPI', []).factory('haSessionTimeoutAPI', haSessionTimeoutApi);

	haSessionTimeoutApi.$inject = ['haHttpService'];

	function haSessionTimeoutApi(http) {
		return {
			getUserSessionTime: getUserSessionTime,
			restartUserSessionTime: restartUserSessionTime
		};

		function getUserSessionTime() {
			return http.GET('/Book/Shared/GetSessionTimeout')
				.then(getSessionTimeComplete)
				.catch(getSessionTimeFailed);

			function getSessionTimeComplete(response) {
				return response.data;
			}

			function getSessionTimeFailed(error) {
				console.log('XHR Failed for getSessionTime.' + error.data);
			}
		}

		function restartUserSessionTime() {
			return http.POST('/json/v1/session/extend')
				.then(restartSessionTimeComplete)
				.catch(restartSessionTimeFailed);

			function restartSessionTimeComplete(response) {
				return response;
			}

			function restartSessionTimeFailed(error) {
				console.log('XHR Failed for Extend Session: ' + error.status);
				return error;
			}
		}
	}

})();;
(function (angular) {

	'use strict';

	angular.module('haLaunchDarklyModule')
		.service('haLaunchDarklyAPI', ["haHttpService", function (http) {

			var launchDarklyAPIPath = window.location.protocol + '//' + window.location.host+"/api/v2/LaunchDarkly";

				return {
					getFeatureFlag: function (key, type, variant,useCache) {
						 return http.GET(launchDarklyAPIPath+"/"+key+"/"+type+"/"+variant+"/"+useCache).success(function(data){
							//console.log(key+":"+data)
							return Promise.resolve(data)
						 }).error(function(error){
							return Promise.reject(new Error(error))
						 });
					},
					trackResult: function(id) {
						return http.GET(launchDarklyAPIPath+"/TrackResult/"+id).success(function(data){
							return Promise.resolve(data)
						 }).error(function(error){
							return Promise.reject(new Error(error))
						 });
					}

				}
			}
		]);

})(angular);
;
(function (angular) {

	'use strict';

	angular.module('haEncryptionModule')
		.service('haEncryptionAPI', ["haHttpService", function (http) {

			var encryptionAPIPath = window.location.protocol + '//' + window.location.host + "/api/v2/Encryption";

			return {
				encryptString: function (dataToEncrypt, key) {
					return http.GET(encryptionAPIPath+"/String/"+dataToEncrypt+"/"+key).success(function (response) {
						return Promise.resolve(response);
					}).error(function (error) {
						return Promise.reject(new Error(error))
					});
				}
			}
		}]);

})(angular);
;
(function (ng) {

	// Hawaiian Airport Pair Directive
	// ===============================
	//
	// * **Class:** HaAirportPair
	// * **Author:** Nathan Probst
	//
	// Provide communication between two ha-airport-input directives.

	'use strict';

	var module;
	try {
		module = ng.module('haAirportsModule');
	} catch (e) {
		module = ng.module('haAirportsModule', ['haCitiesModule', 'haAdvanceToNextInputService', 'ui.bootstrap.typeahead.ha']);
	}

	module.directive('haAirportPair', [function () {
		var haAirportPairCtrl = ['$scope', function () {

			var ctrl = {
				originCtrl: null,
				destinationCtrl: null,
				register: function (inputCtrl) {
					inputCtrl.pairCtrl = ctrl;
					if (inputCtrl.isOrigin) {
						// $log.debug('originCtrl', inputCtrl);
						ctrl.originCtrl = inputCtrl;
					} else if (inputCtrl.isDestination) {
						// $log.debug('destinationCtrl', inputCtrl);
						ctrl.destinationCtrl = inputCtrl;
					} else {
						throw new Error('Attempted to register a non-origin/destination inputCtrl.');
					}
				},
				getOriginCode: function () {
					return ctrl.originCtrl != null ? ctrl.originCtrl.getCode() : null;
				},
				getDestinationCode: function () {
					return ctrl.destinationCtrl != null ? ctrl.destinationCtrl.getCode() : null;
				}
			};

			return ctrl;
		}];

		return {
			restrict: 'A',
			scope: true,
			controller: haAirportPairCtrl
		};
	}]);

})(angular);
;
(function (ng) {

	// Hawaiian Airport Input Directive
	// ================================
	//
	// * **Class:** HaAirportInput
	// * **Author:** Nathan Probst
	//
	// Provide a intelligent input for HA-served airports.

	'use strict';

	var module;
	try {
		module = ng.module('haAirportsModule');
	} catch (e) {
		module = ng.module('haAirportsModule', ['haCitiesModule', 'haAdvanceToNextInputService', 'ui.bootstrap.typeahead.ha']);
	}

	module.controller('haAirportInputCtrl', [
		'$log',
		'$scope',
		'$timeout',
		'haTemplateCache',
		'haCitiesSvc',
		'haConfig',

		function ($log, $scope, $timeout, haTemplateCache, haCitiesSvc, haConfig) {
			// Pre-Load template contents on initial load to the templateCache for faster rendering of modal popup
			$timeout(function () {
				haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-base'));
				haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-hawaii'));
				haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-na'));
				haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-asia'));
			}, 500 + 1000 * Math.random());

			var ctrl = {
				isOrigin: false,
				isDestination: false,
				isReshop: false,
				isExpertBooking: false,
				legIndex: 0,
				pairCtrl: null
			};

			var getPairCode = function () {
				if (ctrl.pairCtrl != null) {
					if (ctrl.isDestination) {
						return ctrl.pairCtrl.getOriginCode();
					} else if (ctrl.isOrigin) {
						return ctrl.pairCtrl.getDestinationCode();
					}
				}
				return null;
			};

			ctrl.getCode = function () {
				return $scope.ngModel ? $scope.ngModel.Code : null;
			};

			$scope.getMatchingAirports = function (nameOrCode) {
				if (ctrl.isReshop) {
					return haCitiesSvc.getMatchingFilteredCities(nameOrCode, ctrl.legIndex, ctrl.isOrigin);
				}

				if (ctrl.isExpertBooking) {
					return haCitiesSvc.getMatchingCitiesExpertBooking(nameOrCode, getPairCode(), $scope.filterFn);
				}

				// return all matching cities, not just valid pairs, per pbi 60
				return haCitiesSvc.getMatchingCities(nameOrCode, null, $scope.filterFn);

			};

			return ctrl;
		}
	]);

	module.filter('isHACity', function () {
		return function filterFn(city) {
			return (city != null) && (city.IsHACity === true);
		};
	});

	module.directive('haAirportInput', [
		'$log',
		'$timeout',
		'$window',
		'haConfig',

		function ($log, $timeout, $window) {
			return {
				restrict: 'A',
				require: ['haAirportInput', '?^haAirportPair'],
				scope: {
					ngModel: '=',
					preselectCode: '=',
					onChange: '@',
					filterFn: '@',
					label: '@',
					placeholder: '@',
					required: '@',
					disabled: '@',
					id: '@',
					name: '@',
					hidePin: '@',
					labelStyle: '@',
					size: '@'
				},
				// !!! if modifying below template, update the source file, ha-airport-input-template.html. !!!
				// !!! Follow the instruction in the file to minify the code and update the template. !!!
				template: "<div> <div has-autofill ng-class=\"{invalid: (invalid && !isFocused)}\"> <label class=\"ha-label\" ng-class=\"{'required-asterisk':required, 'ha-form-lg': !size || size=='large', 'ha-form-sm':size=='small', 'inline': labelStyle=='inline' || !labelStyle || labelStyle !=='eyebrow', 'wrap-on-mobile': labelStyle=='inlineWrapOnMobile'}\"> <span ng-if=\"label\">{{label}}<span class=\"mandatory-flag\" ng-if=\"required\">*</span></span> <input type=\"hidden\" name=\"{{name}}Code\" ng-value=\"ngModel.Code\"/> <div class=\"mobile-tap-mask\" ng-if=\"$root.isMobile\" ha-location-modal=\"hawaii\" ng-click=\"pinClicked()\" filter-fn=\"{{filterFn}}\"></div><input role=\"combobox\" aria-owns=\"airportMatch-{{elNumber}}\" aria-expanded=\"{{isOpen() || false}}\" placeholder=\"{{placeholder}}\"  id=\"{{id}}\" type=\"text\" name=\"{{name}}\" autocomplete=\"off\" ng-model=\"ngModel\" allow-non-english=\"true\" ha-typeahead=\"airport as airport.DisplayName for airport in getMatchingAirports($viewValue)\" typeahead-editable=\"false\" typeahead-min-length=\"3\" typeahead-wait-ms=\"150\" typeahead-on-select=\"onSelect($item, $model, $label)\" ng-required=\"required\" ng-disabled=\"disabled\" ng-focus=\"onFocus($event)\" ng-blur=\"onBlur($event)\" ng-class=\"{'has-pin':!hideWhereweflyPin}\" ha-errors > <div class=\"location-dropdown\"> <div class=\"ha-typeahead-results\" role=\"textbox\" id=\"airportMatch-{{elNumber}}\"></div></div><a href=\"\" ha-location-modal=\"hawaii\" ng-if=\"!hideWhereweflyPin\" ng-click=\"pinClicked()\" title=\"{{pinTitle || label}}\" filter-fn=\"{{filterFn}}\" city-list-type=\"{{cityListType}}\" class=\"ha-airport-input-pin\"> <i class=\"ha-icon fontIcon32-mapPin\"></i> </a> <em for=\"airportInput-{{elNumber}}\"></em> </label> </div></div>",
				//templateUrl: haConfig.getTemplateUrl('ha-airport-input-template.html'),
				controller: 'haAirportInputCtrl',
				link: function ($scope, $el, $attrs, $ctrls) {

					// make the input element readonly if on mobile to prevent keyboards from popping up on some android devices
					if ($scope.$root.isMobile) {
						$el.find('input[role="combobox"]').attr('readonly','readonly');
					}

					$scope.elNumber = Math.floor((Math.random() * 10000) + 1);
					var haAirportInputCtrl = $ctrls[0];
					haAirportInputCtrl.isOrigin = (typeof $attrs.origin !== 'undefined');
					haAirportInputCtrl.isDestination = (typeof $attrs.destination !== 'undefined');
					haAirportInputCtrl.isReshop = (typeof $attrs.reshop !== 'undefined');
					haAirportInputCtrl.isExpertBooking = (typeof $attrs.expertBooking !== 'undefined');
					haAirportInputCtrl.legIndex = ($attrs.legIndex != null) && $attrs.legIndex;

					$scope.cityListType = 'normal';
					if (haAirportInputCtrl.isReshop) {
						if (haAirportInputCtrl.isOrigin) {
							$scope.cityListType = 'reshop-' + haAirportInputCtrl.legIndex + '-origin';
						} else {
							$scope.cityListType = 'reshop-' + haAirportInputCtrl.legIndex + '-destination';
						}
					} else if (haAirportInputCtrl.isExpertBooking) {
						$scope.cityListType = 'expertbooking';
					}

					var haAirportPairCtrl = $ctrls[1];
					if (haAirportPairCtrl != null) {
						// $log.debug('haAirportPairCtrl', haAirportPairCtrl);
						haAirportPairCtrl.register(haAirportInputCtrl);
					}

					// Allow pre-selection with only an airport code
					if ($scope.preselectCode != null) {
						var pcode = $scope.preselectCode;
						$scope.getMatchingAirports(pcode).then(function (cityList) {
							for (var i = 0; i < cityList.length; i++) {
								if (cityList[i].Code === pcode) {
									$scope.ngModel = cityList[i];
									break;
								}
							}
						});
					}

					// To allow redirect on selection...
					$scope.$parent.setLocationPathname = function (newUrl) {
						if ($window.location.pathname !== newUrl) {
							console.log('$window.location.pathname = ' + newUrl);
							$window.location.pathname = newUrl;
						}
					};

					// To allow redirect on selection...
					$scope.$parent.setLocationPathname = function (newUrl) {
						if ($window.location.pathname !== newUrl) {
							console.log('$window.location.pathname = ' + newUrl);
							$window.location.pathname = newUrl;
						}
					};

					$scope.$watch('ngModel', function (newValue) {
						if ($scope.onChange != null && newValue) {
							$scope.$parent.$eval($scope.onChange);
						}
						$scope.$emit('airportChanged');
					});

					// fires when coming from modal window
					$scope.onSelected = function (city) {
						$scope.ngModel = city;
						$timeout(function () {
							$scope.focus();
						}, 0);
					};

					// fires when clicking or tabbing a highlighted result
					$scope.onSelect = function () {
						$timeout(function () {
							$scope.focus();
						}, 0);
						//$scope.$emit('haAdvanceToNextInput:next', $el);
					};

					$scope.pinClicked = function () {
						$scope.$emit('haWhereWeFlyPinClicked');
					};

					$scope.onClick = function ($event) {
						// select input value on click
						$event.target.select();
					};

					$scope.hideWhereweflyPin = $scope.$eval($scope.hidePin);
					$scope.labelStyle = $scope.$eval($scope.labelStyle);
					$scope.size = $scope.$eval($scope.size);

					$scope.focus = function () {
						setTimeout(function () {
							$el.find('input[type="text"]').focus();
						}, 0);
					};

					if ('pinTitle' in $attrs) {
						$scope.pinTitle = $attrs.pinTitle;
					} else {
						$scope.pinTitle = $scs('BookingWidget.AirportInputPinTitle');
					}

					$scope.onFocus = function ($event) {
						$scope.isFocused = true;
						$timeout(function () {
							$event.target.select();
						}, 0);
						$scope.$emit('airportInputFocused', $el);
						// $log.log('focused!');
					};

					$scope.onBlur = function () {
						$scope.isFocused = false;
						// $timeout(function () {
						//     $scope.isFocused = $scope.invalid = false;
						//     $el.find('.ha-input').removeClass('invalid');
						// }, 250);
						// $timeout(function () {
						//     $scope.invalid = (input.className.indexOf('ng-invalid') > -1);
						// }, 500);
						// $log.log('blurrred!');
					};

				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haStickyBookingWidgetModule', ['haModalService']);

	module.directive('haStickyBookingWidget', ['$rootScope', '$window', 'haUtils', 'haCitiesSvc', 'haConfig', 'haDateUtils', '$timeout', 'haModal', 'haSitecoreStrings', function ($rootScope, $window, haUtils, haCitiesSvc, haConfig, haDateUtils, $timeout, haModal, $scs) {

		var HaStickyBookingWidgetLink = function ($scope, $el, $attrs) {

			// Scope functions
			$scope.openFlightSearchEdit = function () {
				haModal(haConfig.getTemplateUrl('ha-book-flight-search-edit-modal.html'), {
					id: 'fare-help-modal',
					backdrop: 'false',
					scope: $scope,
					modalLock: true
				});
			};

			// Local functions
			var stickyBookingOffsetY = function () {
				if ($scope.stickyBookingOffsetY === false) {
					var el = angular.element('[ha-sticky-booking-widget]');
					if (el != null) {
						$scope.stickyBookingOffsetY = el.offset().top;
					} else {
						return;
					}
				}
				return $scope.stickyBookingOffsetY;
			};

			// Recall search
			$scope.recallSearch = function (idx) {
				$scope.$broadcast('recallSearch', idx);
				$scope.recent = false;
			};


			// Sticky scrolling
			angular.element($window).bind('scroll', function () {
				var yOffset = this.pageYOffset;
				if ($('html').hasClass('lte-ie8')) {
					yOffset = this.document.documentElement.scrollTop;
				}
				if (yOffset >= stickyBookingOffsetY()) {
					$scope.fixed = true;
				} else {
					$scope.fixed = false;
				}
				$scope.$digest();
			});


			// WATCHERS
			//*******************
			// Watch recent open/close
			// Watch recent open/close
			$scope.recentSearches = {open: false};
			$scope.$watch('recentSearches.open', function (isOpen) {
				if (typeof isOpen === 'undefined') {
					return;
				}

				if (isOpen) {
					$timeout(function () {
						$('body').on('click.recent', function () {
							$scope.$apply(function () {
								$scope.recentSearches.open = false;
							});
						});
					}, 10);

				} else {
					$('body').off('click.recent');
				}
			});


			// INITIALIZATION
			//*******************
			function initialize() {

				// Interstitial default image
				$attrs.$observe('defaultDestinationImage', function (val) {
					$scope.defaultDestinationImage = val || '';
				});
				//	multiCity - allows multi-city trips.  Default is on.
				$attrs.$observe('multiCity', function (val) {
					$scope.multiCity = (val && val === 'false') ? false : true;
				});

				// State
				$scope.stickyBookingOffsetY = false;

				// Legs
				$scope.leg = {};

				// Cookie Processing and Defaults
				var flightQueryCookie = haUtils.getFlightQueryModelCookie();
				var dateToday = moment().format('YYYY-MM-DD');
				var recentSearchArr = haUtils.getFlightQueryModelRecentCookie();

				if (recentSearchArr) {
					//take past dates out
					for (var i = 0; i < recentSearchArr.length; i++) {

						if (recentSearchArr[i].FlightSearchSegmentList[0].DepartureDate.substr(0, 10) < dateToday) {
							recentSearchArr.splice(i, 1);
						}
						$scope.flightQueryCookieArr = recentSearchArr;
					}
				}

				if (flightQueryCookie && flightQueryCookie.FlightSearchSegmentList[0] && flightQueryCookie.FlightSearchSegmentList[0].OriginCityCode) {
					haCitiesSvc.getCityByCode(flightQueryCookie.FlightSearchSegmentList[0].OriginCityCode).then(function (airport) {
						$scope.leg.origin = airport;
					});
				} else {
					$scs.get("BookingWidget['originplaceholderstickywidget']").then(function (txt) {
						$scope.leg.origin = txt;
					});
				}
				if (flightQueryCookie && flightQueryCookie.FlightSearchSegmentList[0] && flightQueryCookie.FlightSearchSegmentList[0].DestinationCityCode) {
					haCitiesSvc.getCityByCode(flightQueryCookie.FlightSearchSegmentList[0].DestinationCityCode).then(function (airport) {
						$scope.leg.destination = airport;
					});
				} else {
					$scs.get("BookingWidget['destinationplaceholderstickywidget']").then(function (txt) {
						$scope.leg.destination = txt;
					});
				}
			}

			initialize();
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaStickyBookingWidgetLink,
			templateUrl: haConfig.getTemplateUrl('ha-sticky-booking-widget-base-template.html')
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Progress Bar Directive
	// --------------------------------------------
	//
	// * **Class:** HaProgressBar
	// * **Author:** Jake Albaugh
	//
	// a dynamic progress bar

	'use strict';

	var mod = angular.module('haProgressBarModule', []);

	mod.directive('haProgressBar', ['haConfig', function (haConfig) {

		var HaProgressBarController = function () {

		};

		HaProgressBarController.$inject = ['$scope'];

		var HaProgressBarLink = function ($scope, $el, $attrs) {

			$scope.steps = $attrs.steps;
			$scope.current = $attrs.current;
			$scope.completed = $scope.current - 1;
			$scope.empty = $scope.steps - $scope.current;

			$scope.getIterations = function (its) {
				return new Array(its);
			};

			$scope.$emit('$haProgressBarReady');
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaProgressBarLink,
			controller: HaProgressBarController,
			templateUrl: haConfig.getTemplateUrl('ha-progress-bar-base-template.html')
		};
	}]);

})(angular);;
(function (angular) {

	// Ha Error Page Directive
	// --------------------------------------------
	//
	// * **Class:** HaErrorPage
	// * **Author:** Jake Albaugh
	//
	// A customizable error page supporting a title and description, as well as room for custom html to be transcluded beneath the title.

	'use strict';

	var mod = angular.module('haErrorPageModule', []);

	mod.directive('haErrorPage', ['haConfig', function (haConfig) {

		var HaErrorPageController = function () {

		};

		HaErrorPageController.$inject = ['$scope'];

		var HaErrorPageLink = function ($scope, $el, $attrs) {

			$scope.header = $attrs.header;
			$scope.subtext = $attrs.subtext;
			$scope.hasTransclude = (angular.element('[ng-transclude]').contents().length > 0) ? true : false;

			return {
				restrict: 'A',
				scope: true,
				link: HaErrorPageLink,
				controller: HaErrorPageController,
				transclude: true,
				templateUrl: haConfig.getTemplateUrl('ha-error-page-base-template.html')
			};
		};
	}]);

})(angular);;
(function (angular) {

	// Ha Map Navigator Directive
	// --------------------------------------------
	//
	// * **Class:** HaMapNavigator
	// * **Author:** Chad Kumabe
	//
	// Map Navigation Component for Route Map

	/* global Hammer */

	'use strict';

	var mod = angular.module('haMapNavigatorModule', []);

	mod.directive('haMapNavigator', ['haConfig', function (haConfig) {

		var HaMapNavigatorController = function ($scope, $window) {
			$window.onresize = function () {
				$scope.reinitializeMap();
				$scope.$apply();
			};
		};

		HaMapNavigatorController.$inject = ['$scope', '$window'];

		var HaMapNavigatorLink = function ($scope, $el, $attr) {

			var magnifyBorder = 100;
			var previewWindowWidth = 172;
			var previewWindowHeight = 0;
			$scope.zoomLevel = 0.75;
			var maxZoomLevel = $scope.zoomLevel;
			var origMainImageWidth = 0;
			var moveRatio = 0;
			var model = $scope.$eval($attr.model);
			var magnifier = $el.find('.focus-magnifier');
			var mainImageContainer = $el.find('.main-image-container');
			var mainOffsetHeight = 0;
			var mainOffsetWidth = 0;
			var viewportHeight = $el.width() / 2;
			var viewportRatio = 0.5;
			var zoomResolution = 0.2;

			$scope.model = model;
			var calculateMoveRatio = function () {
				moveRatio = parseInt($el.css('width'), 10) / previewWindowWidth / $scope.zoomLevel;
			};

			var updateMagnifier = function () {
				var origWidth = parseInt(magnifier.width(), 10);
				var newWidth = $scope.zoomLevel * previewWindowWidth;
				var origHeight = parseInt(magnifier.height(), 10);
				var newHeight = newWidth * viewportRatio;

				$scope.magnifierStyle = {
					width: newWidth + 'px',
					height: newHeight + 'px',
					borderWidth: Math.round(magnifyBorder / $scope.zoomLevel) + 'px'
				};
				// allow for center zooming
				var magnifierTopOffset = parseInt(magnifier.css('top'), 10) +
					((origHeight - newHeight) / 2);
				var magnifierLeftOffset = parseInt(magnifier.css('left'), 10) +
					((origWidth - newWidth) / 2);

				// adjust if not in bounds
				if (magnifierLeftOffset + newWidth > previewWindowWidth) {
					magnifierLeftOffset = previewWindowWidth - newWidth;
				}
				if (magnifierTopOffset + newHeight > previewWindowHeight) {
					magnifierTopOffset = previewWindowHeight - newHeight;
				}
				if (magnifierLeftOffset < 0) {
					magnifierLeftOffset = 0;
				}
				if (magnifierTopOffset < 0) {
					magnifierTopOffset = 0;
				}
				magnifier.css('top', magnifierTopOffset + 'px');
				magnifier.css('left', magnifierLeftOffset + 'px');
			};
			var updateMapZoom = function () {
				var origMapWidth = $el.find('.main-image').width();
				$scope.mapImageStyle = {
					width: parseInt($el.css('width'), 10) / $scope.zoomLevel
				};
				var newMapImageHeight = parseInt($el.find('.main-image').height(), 10) *
					($scope.mapImageStyle.width / origMapWidth);
				mainOffsetHeight = newMapImageHeight - viewportHeight;//parseInt($el.height(),10);
				mainOffsetWidth = $scope.mapImageStyle.width - parseInt($el.css('width'), 10);
				$scope.mainImageConstraintStyle = {
					width: $scope.mapImageStyle.width * 2 - parseInt($el.css('width'), 10),
					height: newMapImageHeight * 2 - viewportHeight,//parseInt($el.height(),10),
					left: -(mainOffsetWidth),
					top: -(mainOffsetHeight)
				};

			};

			var updateFocusContainer = function () {
				var borderWidth = Math.round(magnifyBorder / $scope.zoomLevel);
				var newWidth = (borderWidth * 2) + previewWindowWidth;
				var newHeight = (borderWidth * 2) + previewWindowHeight - 1;
				$scope.focusContainerStyle = {
					top: -borderWidth + 'px',
					left: -borderWidth + 'px',
					width: newWidth + 'px',
					height: newHeight + 'px'
				};
			};


			var repositionMainMap = function () {
				var leftOffset = parseInt(magnifier.css('left'), 10) * moveRatio;
				var topOffset = parseInt(magnifier.css('top'), 10) * moveRatio;
				mainImageContainer.css('left', -(leftOffset - mainOffsetWidth) + 'px');
				mainImageContainer.css('top', -(topOffset - mainOffsetHeight) + 'px');
			};

			var repositionMagnifier = function () {
				var leftOffset = (parseInt(mainImageContainer.css('left'), 10) - mainOffsetWidth) / moveRatio;
				var topOffset = (parseInt(mainImageContainer.css('top'), 10) - mainOffsetHeight) / moveRatio;
				magnifier.css('left', -leftOffset + 'px');
				magnifier.css('top', -topOffset + 'px');
			};

			var updateNavComponents = function () {
				calculateMoveRatio();
				updateMagnifier();
				updateFocusContainer();
				updateMapZoom();
				repositionMainMap();
			};


			$scope.zoomOut = function () {
				if ($scope.zoomLevel < 1) {
					$scope.zoomLevel = $scope.zoomLevel + zoomResolution <= 1 ?
					$scope.zoomLevel + zoomResolution : 1;
					updateNavComponents();
				}
			};

			$scope.zoomIn = function () {
				if ($scope.zoomLevel > maxZoomLevel) {
					$scope.zoomLevel = $scope.zoomLevel - zoomResolution >= maxZoomLevel ?
					$scope.zoomLevel - zoomResolution : maxZoomLevel;
					updateNavComponents();
				}
			};

			var imagesSet = 0;
			var asyncSetInitPos = function () {
				// position after all images set
				// currently waiting for only 2 images set
				imagesSet++;
				if (imagesSet === 2) {
					// hardcode position to focus on hawaii
					magnifier.css('left', '28px');
					magnifier.css('top', '13px');
					repositionMainMap();
					$scope.$apply();
				}
			};

			var mainImageId = $el.find('.main-image').attr('image-id');
			var initializeMainMap = function () {
				$el.css('height', viewportHeight);
				$el.find('.ha-map-navigator').css('height', viewportHeight);
				maxZoomLevel = $el.width() / origMainImageWidth;
				if (maxZoomLevel > $scope.zoomLevel) {
					$scope.zoomLevel = maxZoomLevel;
				}
				calculateMoveRatio();
				updateMapZoom();
				asyncSetInitPos();
			};
			$scope.$on(mainImageId, function () {
				origMainImageWidth = $el.find('.main-image').width();
				initializeMainMap();
			});

			var previewBackgroundId = $el.find('.preview-background').attr('image-id');
			var initializePreviewWindow = function () {
				previewWindowWidth = $el.find('.preview-background').width();
				previewWindowHeight = $el.find('.preview-background').height();
				$el.find('.preview-container').css('height', previewWindowHeight + 1);
				$el.find('.tap-zoom-panel').css('height', previewWindowHeight + 1);
				updateMagnifier();
				updateFocusContainer();
				asyncSetInitPos();
			};

			$scope.$on(previewBackgroundId, function () {
				$el.find('.preview-background')[0].ondragstart = function () {
					return false;
				};
				initializePreviewWindow();
			});

			$el.find('[ha-draggable]').each(function () {
				var scope = angular.element(this).scope();
				scope.updateDraggableSettings({
					containment: 'parent'
				});
			});

			$scope.$on('$dragging', function (ev, ui) {
				if ($(ui.toElement).hasClass('main-image')) {
					repositionMagnifier();
				} else {
					repositionMainMap();
				}
			});

			$scope.reinitializeMap = function () {
				viewportHeight = $el.width() / 2;
				initializePreviewWindow();
				initializeMainMap();
				updateNavComponents();
			};

			// mouse states
			$scope.setGrab = function (isGrab) {
				$scope.grabbing = isGrab;
			};

			// add scroll zoom handler
			new window.Hamster($el.find('.focus-magnifier')[0]).wheel(function (ev, delta) {
				if (delta > 0) {
					$scope.zoomIn();
				}
				else {
					$scope.zoomOut();
				}
				$scope.$apply();
			});

			new Hammer($el.find('.main-image-container')).on('pinchin', function () {
				$scope.zoomOut();
				$scope.$apply();
			});

			new Hammer($el.find('.main-image-container')).on('pinchout', function () {
				$scope.zoomIn();
				$scope.$apply();
			});

			$scope.$emit('$haMapNavigatorReady');
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaMapNavigatorLink,
			templateUrl: haConfig.getTemplateUrl('ha-map-navigator-base-template.html'),
			controller: HaMapNavigatorController
		};
	}]);

	mod.directive('loadWatch', function () {
		return {
			restrict: 'A',
			link: function (scope, element, attrs) {
				element.bind('load', function (ev) {
					scope.$emit(attrs.imageId, ev);
				});
			}
		};
	});

})(angular);
;
(function (angular) {

	// Ha Global Message Directive
	// --------------------------------------------
	//
	// * **Class:** HaGlobalMessage
	// * **Author:** Chad Kumabe
	//
	// Global Message used for pages

	'use strict';

	var module = angular.module('haGlobalMessageModule', ['ngAnimate', 'haAlertModule']);

	module.directive('haGlobalMessage', ['haConfig', '$timeout', function (haConfig, $timeout) {

		var HaGlobalMessageController = function ($scope, $timeout) {
			$scope.alert = {
				type: 'success',
				header: '',
				description: null
			};
			$scope.closeMessage = function () {
				$scope.isShown = false;
			};
			$scope.setAutoFade = function () {
				$timeout($scope.closeMessage, $scope.messageCloseTime);
			};
		};

		HaGlobalMessageController.$inject = ['$scope', '$timeout'];

		var HaGlobalMessageLink = function ($scope, $el, $attrs) {
			$scope.alert.header = $attrs.header;
			$scope.alert.description = $attrs.description;
			$scope.alert.type = $attrs.type;
			$scope.isShown = !$scope.alert.header || $scope.alert.header === '' ? false : true;
			$scope.messageCloseTime = $attrs.messageCloseTime || 3000;
			$scope.setAutoFade();
			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.$emit('$haGlobalMessageReady');

			$timeout(function () {
				$el.children().find('[role="alert"]').focus();
			}, 300);
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaGlobalMessageLink,
			templateUrl: haConfig.getTemplateUrl('ha-global-message-base-template.html'),
			controller: HaGlobalMessageController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Global Alert Directive
	// --------------------------------------------
	//
	// * **Class:** HaGlobalAlert
	// * **Author:** Melissa Rota
	//
	// Global alert that can appear across the top of all pages.

	'use strict';

	var module = angular.module('haGlobalAlertModule', []);

	module.directive('haGlobalAlert', ['haConfig', function (haConfig) {

		var HaGlobalAlertController = function ($scope) {

			$scope.showAlert = true;

			$scope.$on('$showAlert', function () {
				$scope.showAlert = true;
			});

			$scope.$on('$hideAlert', function () {
				$scope.showAlert = false;
			});

			$scope.hideAlert = function () {
				$scope.showAlert = false;
			};
		};

		HaGlobalAlertController.$inject = ['$scope'];

		var HaGlobalAlertLink = function ($scope, $el, $attrs) {

			$scope.type = $attrs.type;

			$scope.noIcon = false;
			if ($attrs.noicon) {
				$scope.noIcon = true;
			}

			$scope.header = $attrs.header;
			$scope.description = $attrs.description;
			$scope.btntext = $attrs.btntext;
			$scope.btnlink = $attrs.btnlink;

			$attrs.$observe('header', function () {
				$scope.header = $attrs.header;
				$scope.showAlert = true;
			});

			$attrs.$observe('type', function () {
				$scope.type = $attrs.type;
				$scope.showAlert = true;
			});

			$attrs.$observe('description', function () {
				$scope.description = $attrs.description;
			});

			$attrs.$observe('noicon', function () {
				$scope.noIcon = false;
				if ($attrs.noicon) {
					$scope.noIcon = true;
				}
				$scope.showAlert = true;
			});

			$attrs.$observe('btntext', function () {
				$scope.btntext = $attrs.btntext;
			});

			$attrs.$observe('btnlink', function () {
				$scope.btnlink = $attrs.btnlink;
			});

			$scope.$emit('$haGlobalAlertReady');
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaGlobalAlertLink,
			templateUrl: haConfig.getTemplateUrl('ha-global-alert-base-template.html'),
			controller: HaGlobalAlertController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Tooltip Directive
	// --------------------------------------------
	// *
	// * **Class:** HaTooltip
	// * **Author:** Cory Shaw
	//
	// Shows a blue tooltip with some options next to formfields, etc

	'use strict';

	var module = angular.module('haTooltipModule', []);

	module.directive('haTooltip', ['haConfig', '$timeout', function (haConfig, $timeout) {

		var HaTooltipController = function () {

		};

		HaTooltipController.$inject = ['$scope'];

		var HaTooltipLink = function ($scope, $el, $attrs) {
			$scope.subtext = $attrs.subtext;

			$scope.position = $attrs.position;
			$scope.arrowPosition = $attrs.arrowPosition;
			var width = $attrs.width;
			if (width > window.innerWidth) {
				width = window.innerWidth- 20;
			}
			$scope.width = width;

			$scope.targetElId = $attrs.targetElId;

			$scope.setWidth = function () {
				if ($scope.width) {
					return {width: $scope.width};
				}
			};

			if ($scope.width) {
				$el.css({width: $scope.width});
			}

			var arrowPosition;
			// Dynamic positioning based on a target element
			if ($scope.targetElId && $scope.position === 'absolute' && $scope.arrowPosition) {
				arrowPosition = $scope.arrowPosition.split('-');
				var width = parseInt($scope.width) || $el.width();

				$timeout(calculatePosition);

				// if tooltip is ngShown, recalculate position when it is shown
				if ($attrs.ngShow) {
					$scope.$watch($attrs.ngShow, function(newValue) {
						if (newValue) {
							// recalculate position
							calculatePosition();
						}
					});
				}
			}

			function calculatePosition() {
				var arrowDistance = 29;
				var arrowHeight = 8;
				var arrowBase = 12;

				var styleObj = {
					position: 'absolute',
					margin: 0
				};

				var $target = $('#' + $scope.targetElId);
				var position = $target.position();
				var top = position.top;
				var left = position.left;
				var offset = $target.offset();
				var height = getTooltipHeight($el);
				width = Number($scope.width) || $el.width();
				switch(arrowPosition[0]) {
					case 'top':
						top += $target.height() + arrowHeight;
						break;
					case 'bottom':
						top -= (height + arrowHeight);
						break;
					case 'left':
						left += $target.width() + arrowHeight;
						break;
					case 'right':
						left -= width + arrowBase;
						break;
				}
				switch(arrowPosition[1]) {
					case 'left':
						left -= (arrowDistance + (arrowBase/2) - ($target.width()/2));
						break;
					case 'right':
						left -= (width - arrowDistance - (arrowBase/2) - ($target.width()/2));
						break;
					case 'top':
						top -= (arrowDistance + (arrowBase/2) - ($target.height()/2));
						break;
					case 'bottom':
						top -= (height - arrowDistance - (arrowBase/2) - ($target.height()/2));
						break;
					case 'center':
						if (arrowPosition[0] === 'top' || arrowPosition[0] === 'bottom') {
							left -= width/2 - $target.width()/2;
						} else {
							top -= height/2 - $target.height()/2;
						}
						break;
				}

				// adjust if tooltip horizontal position exceeds the screen
				if (offset.left - $el.width()/2 < 0) {
					left -= offset.left - $el.width()/2;
				} else if (offset.left + $el.width()/2 > window.innerWidth - 10) {
					left += (window.innerWidth - 10) - (offset.left + $el.width()/2);
				}

				position.top = top;
				position.left = left;
				angular.extend(styleObj, position);

				// Set the styles.
				$el.css(styleObj);
			}

			function getTooltipHeight($element) {
				var previousCss  = $element.attr("style");
				$element
				    .css({
				        position:   'absolute', // Optional if #myDiv is already absolute
				        visibility: 'hidden',
				        display:    'block'
				    });
				if ($element.hasClass('ng-hide')) {
					$element.removeClass('ng-hide');
					var wasNgHidden = true;
				}
				var optionHeight = $element.find('.ha-tooltip').outerHeight();
				$element.attr("style", previousCss ? previousCss : "");
				if (wasNgHidden) {
					$element.addClass('ng-hide');
				}
				return optionHeight;
			}

			$scope.$emit('$haTooltipReady');
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaTooltipLink,
			controller: HaTooltipController,
			transclude: true,
			templateUrl: haConfig.getTemplateUrl('ha-tooltip-base-template.html')
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Avatar Directive
	// --------------------------------------------
	//
	// * **Class:** HaAvatar
	// * **Author:** Cory Shaw
	//
	// Basic template for rendering avatars

	'use strict';

	var module = angular.module('haAvatarModule', []);

	module.directive('haAvatar', ['haConfig', function (haConfig) {

		var HaAvatarController = function () {

		};

		HaAvatarController.$inject = ['$scope'];

		var HaAvatarLink = function ($scope, $elem, $attrs) {

			$attrs.$observe('size', function () {
				$scope.setAvatarSize();
			});

			$scope.name = $attrs.name;

			$attrs.$observe('name', function () {
				$scope.name = $attrs.name;
			});

			$scope.alt = $attrs.alt;

			$scope.src = $attrs.src;

			$attrs.$observe('src', function () {
				$scope.src = $attrs.src;
			});

			$scope.initials = $attrs.initials;

			$attrs.$observe('initials', function () {
				$scope.initials = $attrs.initials;
			});

			$scope.seatSelection = $attrs.seatSelection;

			$attrs.$observe('seatSelection', function () {
				$scope.seatSelection = $attrs.seatSelection;
			});

			$scope.setAvatarSize = function () {
				$scope.size = $attrs.size;

				if (!$scope.size) {
					$scope.size = 'large';
				}

				if ($scope.size === 'small') {
					$scope.avatarWidth = 70;
					$scope.avatarHeight = 70;
				}

				if ($scope.size === 'large') {
					$scope.avatarWidth = 125;
					$scope.avatarHeight = 125;
				}
			};

			$scope.setAvatarSize();

			$scope.$emit('$haAvatarReady');
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaAvatarLink,
			controller: HaAvatarController,
			templateUrl: haConfig.getTemplateUrl('ha-avatar-base-template.html')
		};
	}]);

})(angular);;
(function (angular) {

	// Ha Avatar Selection Directive
	// --------------------------------------------
	//
	// * **Class:** HaAvatarSelection
	// * **Author:** Cory Shaw
	//
	// Widget for selecting your traveler avatar

	'use strict';

	var module = angular.module('haAvatarSelectionModule', []);

	module.directive('haAvatarSelectionWrapper', ['haGlobals', function (haGlobals) {

		var HaAvatarSelectionWrapperLink = function ($scope) {

			$scope.selectedAvatar = {};
			$scope.defaultAvatarID = '1';
			$scope.controlID = 'AvatarID';
			$scope.imageControlID = 'AvatarUrl';
			$scope.avatars = [];

			haGlobals('jsonProfileAvatarModel', function (jsonProfileAvatarModel) {
				$scope.avatars = jsonProfileAvatarModel.AvatarImages;

				if (jsonProfileAvatarModel && jsonProfileAvatarModel.MemberAvatarID != null) {
					$scope.defaultAvatarID = jsonProfileAvatarModel.MemberAvatarID;
				}
				else if (jsonProfileAvatarModel.DefaultAvatarID) {
					$scope.defaultAvatarID = jsonProfileAvatarModel.DefaultAvatarID;
				}

				if (jsonProfileAvatarModel.AvatarControlID) {
					$scope.controlID = jsonProfileAvatarModel.AvatarControlID;
				}

				if (jsonProfileAvatarModel.AvatarImageControlID) {
					$scope.imageControlID = jsonProfileAvatarModel.AvatarImageControlID;
				}
			});

			var selected = $($scope.avatars).filter(function () {
				return this.ID === $scope.defaultAvatarID;
			}).get(0);

			if (selected) {
				$scope.selectedAvatar = selected;
			}

			$scope.$on('$selectedAvatarURL', function (event, avatar) {
				$scope.selectedAvatar = avatar;
			});

			$scope.selectProfileImg = function () {
				$scope.$broadcast('$openCustomDropdown');
			};
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaAvatarSelectionWrapperLink
		};
	}]);

	// Constants for non repeating avatar selection
	var RANDOM_SEED = Math.random(); // Used to randomize starting point in avatars. Needs to be the same for all directive instances.
	var LARGE_PRIME = 49727;         // Large enough to be relatively prime to any sane amount of avatars.
	module.directive('haAvatarSelection', ['haGlobals', 'haHttpService', 'haConfig', '$rootScope', function (haGlobals, haHttpService, haConfig, $rootScope) {

		var HaAvatarSelectionLink = function ($scope, $elem, $attrs, formCtrl) {
			$scope.avatars = [];
			$scope.selectedAvatarUrl = $attrs.selectedAvatarUrl;
			$scope.dropdownLabel = $attrs.dropdownLabel;
			$scope.doneText = $attrs.doneText;
			if ($attrs.onDark === 'true' || $attrs.onDark === '') {
				$scope.onDark = true;
			}

			haGlobals('jsonProfileAvatarModel', function (jsonProfileAvatarModel) {
				$scope.avatars = jsonProfileAvatarModel.AvatarImages;
			});

			// Determine the default avatar selection based on a given index
			function selectDefault() {
				if ($attrs.defaultIndex && $scope.avatars.length) {
					// Choose a random starting avatar index between 0 and avatars.length
					var startingIndex = Math.floor(RANDOM_SEED * $scope.avatars.length);

					// Below will always pick a new avatar index without repeats until the passed defaultIndex is >= avatars.length
					var avatarIndex = ((LARGE_PRIME * parseInt($attrs.defaultIndex)) + startingIndex) % $scope.avatars.length;

					$scope.$emit('$defaultAvatarURL', $scope.avatars[avatarIndex]);
				}
			}

			if (!$scope.avatars.length) {
				haHttpService.GET('/myaccount/profileavatar/getavatars').then(
					function (response) {
						response = response.data || response;

						$scope.avatars = (response.Avatars.length) ? response.Avatars.map(function (av) {
							return {ID: av.ID, ProfileImageSrc: av.Src};
						}) : [];
						selectDefault();
					}
				);
			} else {
				selectDefault();
			}

			$attrs.$observe('selectedAvatarUrl', function () {
				$scope.selectedAvatarUrl = $attrs.selectedAvatarUrl;
			});

			$scope.selectAvatar = function (avatar) {
				$scope.$emit('$selectedAvatarURL', avatar);
				if (formCtrl && formCtrl.$setDirty) {
					formCtrl.$setDirty();
				}
				if ($rootScope.isMobile) {
					$scope.$modalClose();

					// perform an extra save step for dashboard
					if ($scope.doneText) {
						$scope.closeAvatarSelector();
					}
				}
			};

			$scope.closeAvatarSelector = function () {
				$scope.$broadcast('$closeCustomDropdown');
				$scope.$emit('$closeCustomDropdown');
			};

			$scope.openAvatarSelector = function () {
				$scope.$broadcast('$openCustomDropdown');
			};

			//$scope.$on('DropDownEnterClick', function() {
			//	$scope.closeAvatarSelector();
			//});

			$scope.showAvatarSelectionModal = function () {
				haModal(haConfig.getTemplateUrl('ha-avatar-selection-modal.html'), {
					id: 'avatar-selection-modal',
					backdrop: true,
					scope: $scope
				});
			};
		};

		return {
			restrict: 'A',
			require: '^?form',
			scope: true,
			link: HaAvatarSelectionLink,
			templateUrl: function () {
				if (isMobile) {
					return haConfig.getTemplateUrl('ha-avatar-selection-base-template-mobile.html');
				} else {
					return haConfig.getTemplateUrl('ha-avatar-selection-base-template.html');
				}
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Breadcrumb Directive
	// --------------------------------------------
	//
	// * **Class:** HaBreadcrumb
	// * **Author:** Evan Nagle
	//
	// A horizontal list of hierarchical links providing a trail back to the site root.

	'use strict';

	var module = angular.module('haBreadcrumbModule', []);

	module.directive('haBreadcrumb', ['haConfig', function (haConfig) {

		var HaBreadcrumbController = function () {

		};

		HaBreadcrumbController.$inject = ['$scope'];

		var HaBreadcrumbLink = function ($scope, $el, $attrs) {
			$scope.$emit('$haBreadcrumbReady');
			$scope.breadcrumbPath = $scope.$eval($attrs.haBreadcrumbPath);
			// $scope.$watch($attrs.haBreadcrumbPath, function(){
			//   $scope.breadcrumbPath = $scope.$eval($attrs.haBreadcrumbPath);
			// });
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaBreadcrumbLink,
			controller: HaBreadcrumbController,
			templateUrl: haConfig.getTemplateUrl('ha-breadcrumb-base-template.html')
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Credit Card Types Directive
	// --------------------------------------------
	//
	// * **Class:** HaCreditCardTypes
	// * **Author:** Cory Shaw
	//
	// Shows images for credit card types that fade out when a type is detected

	'use strict';

	var module = angular.module('haCreditCardTypesModule', []);

	module.directive('haCreditCardTypes', ['haConfig', function (haConfig) {

		var HaCreditCardTypesController = function () {

		};

		HaCreditCardTypesController.$inject = ['$scope'];

		var HaCreditCardTypesLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.$emit('$haCreditCardTypesReady');

			$scope.$on('cctype', function (data) {
				$scope.cctype = data.type;
				// console.log($scope.cctype);
			});
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCreditCardTypesLink,
			controller: HaCreditCardTypesController,
			templateUrl: haConfig.getTemplateUrl('ha-credit-card-types-base-template.html')
		};
	}]);

	// check cc type on input
	module.directive('haCheckCreditCard', function () {

		var HaCheckCreditCardLink = function ($scope, $el, $attrs) {

			$scope.$watch($attrs.ngModel, function () {
				$scope.setCreditCardType($el[0].value);
			});

			$scope.setCreditCardType = function (ccnumber) {
				
				var result = 'unknown';

			    // Master Card BIN range (510000-559999), (222100-272099) effective 10/1/2016.
				if (/^5[1-5]|^2(?:2(?:2[1-9]|[3-9]\d)|[3-6]\d\d|7(?:[01]\d|20))/.test(ccnumber)) {
					result = 'mastercard';
				}
                // Visa BIN range starts with 4.
				else if (/^4/.test(ccnumber)) {
					result = 'visa';
				}
                // American Express BIN range starts with 34, 37. 
				else if (/^3[47]/.test(ccnumber)) {
					result = 'amex';
				}
				
				$scope.$broadcast('cctype', {'type': result});
			};

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCheckCreditCardLink
		};
	});

})(angular);
;
(function (angular) {

	// Ha Dynamic Modal Directive
	// --------------------------------------------
	//
	// * **Class:** HaDynamicModal
	// * **Author:** Cory Shaw
	//
	// Modal directive to be used in cases where it's content is dynamic and needs to receive/send data to the parent page

	'use strict';

	var module = angular.module('haDynamicModalModule', []);

	module.directive('haDynamicModal', function () {

		var HaDynamicModalController = function ($scope, $document, $timeout) {

			$scope.body = angular.element(document).find('body');
			$scope.backdropEl = angular.element('<div class="modal-backdrop" ng-click="$modalCancel()">');

			$scope.handleEscPressed = function (event) {
				if (event.keyCode === 27) {
					$scope.$modalCancel();
				}
			};

			$scope.closeFn = function () {
				$scope.body.unbind('keydown', $scope.handleEscPressed);
				$scope.backdropEl.removeClass('fade in');
				//loadingSpinner.remove();
				$scope.modalEl.removeClass('in');
				$timeout(function () {
					$scope.body.removeClass('modal-active');
				}, 300);
			};

			$scope.body.bind('keydown', $scope.handleEscPressed);
		};

		HaDynamicModalController.$inject = ['$scope'];

		var HaDynamicModalLink = function ($scope) {

			$scope.exampleMethod = function () {
				return $scope;
			};

			$scope.$emit('$haDynamicModalReady');
		};

		return {
			restrict: 'A',
			scope: true,
			transclude: true,
			link: HaDynamicModalLink,
			controller: HaDynamicModalController
		};
	});

})(angular);
;
(function (angular) {

	// Ha Alert Directive
	// --------------------------------------------
	//
	// * **Class:** HaAlert
	// * **Author:** Cory Shaw
	//
	// Injects Alert html and styling where used

	'use strict';

	var module = angular.module('haAlertModule', []);

	module.directive('haAlert', ['$window', 'haConfig', function ($window, haConfig) {

		var HaAlertController = function ($scope) {

			$scope.showAlert = true;

			$scope.$on('$showAlert', function (event, alertId) {
				if (alertId === $scope.alertId) {
					$scope.showAlert = true;
				}
			});

			$scope.$on('$hideAlert', function () {
				$scope.showAlert = false;
			});

			$scope.hideAlert = function () {
				$scope.showAlert = false;
				$scope.$emit('haAlertClosed', $scope.alertId);
			};

			$scope.postTealeaf = function () {
				if ($window.TLT != null) {
					$window.TLT.logCustomEvent('HAErrorMessage', {
						ErrorHeader: $scope.header,
						ErrorMessage: $scope.description
					});
				}
			};
		};

		HaAlertController.$inject = ['$scope'];

		var HaAlertLink = function ($scope, $el, $attrs) {

			$scope.type = $attrs.type;
			$scope.alertId = $attrs.alertId;

			$scope.noClose = $attrs.noClose !== undefined;

			$scope.noIcon = !!$attrs.noicon;

			$scope.header = $attrs.header;
			$scope.description = $attrs.description;
			$scope.customIconClass = $attrs.customIconClass;

			$attrs.$observe('header', function () {
				$scope.header = $attrs.header;
				$scope.showAlert = true;
			});

			$attrs.$observe('type', function () {
				$scope.type = $attrs.type;
				$scope.showAlert = true;
				if ($scope.type === 'error') {
					$scope.noClose = true;
				}
			});

			$attrs.$observe('description', function () {
				$scope.description = $attrs.description;
			});

			$attrs.$observe('noicon', function () {
				$scope.noIcon = !!$attrs.noicon;
				$scope.showAlert = true;
			});

			$attrs.$observe('customIconClass', function () {
				$scope.customIconClass = $attrs.customIconClass;
			});

			if (typeof $el.context !== 'undefined' && $scope.header !== '') {
				if (typeof ($el.context.offsetHeight !== undefined) && $el.context.offsetHeight !== 0) {

					// if alert has transcluded material - grab text - append <p class="sr-only">text content</p> for screen readibility
					if ($el.find('.alert-content [ng-transclude]').children().length > 0) {
						var srText = $el.find('.alert-content [ng-transclude]').text().trim();
						$el.find('.alert-hidden-content.sr-only').append('<p class="sr-only">' + srText + '</p>');
					}

					if ((['promo', 'info'].indexOf($attrs.type.toLowerCase()) === -1) && ($attrs.disableAutofocus !== "true")) { // ignore autofocus for the following alert types: promo, info
						$el.find('[role="alert"]').focus();
					}

					if ($scope.description !== undefined) {
						$scope.postTealeaf();
					}
				}
			}
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaAlertLink,
			transclude: true,
			templateUrl: haConfig.getTemplateUrl('ha-alert-base-template.html'),
			controller: HaAlertController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Book Sticky Progress Bar Directive
	// --------------------------------------------
	//
	// * **Class:** HaBookStickyProgressBar
	// * **Author:** Cory Shaw
	//
	// When booking travel, this sticky bar appears fixed to the top of the screen as you scroll down

	'use strict';

	var module = angular.module('haBookStickyProgressBarModule', ['haFlightResultsAPI', 'haItineraryAPI']);

	module.directive('haBookStickyProgressBar', ['$window', 'haUtils', 'haFlightResultsAPI', 'haItineraryAPI', '$timeout', 'haGlobals', 'haConfig', 'haCustomerSelections', 'haUplift', function ($window, haUtils, haFlightResultsAPI, haItineraryAPI, $timeout, haGlobals, haConfig, haCustomerSelections, haUplift) {

		var HaBookStickyProgressBarController = function ($rootScope, $scope, haModal, $pax) {
			$scope.$pax = $pax;

			$scope.numAdults = 0;
			$scope.numChildren = 0;

			angular.forEach($pax.passengers, function (value) {
				if (value.type === 'Adult') {
					$scope.numAdults++;
				}
				if (value.type === 'Child') {
					$scope.numChildren++;
				}
			});

			haGlobals('enablePassengerTripSummary', function (enablePassengerTripSummary) {
				if (enablePassengerTripSummary != null) {
					$scope.enablePassengerTripSummary = enablePassengerTripSummary;
				}
			});

			$scope.displayEditReshopModal = function () {
				$scope.reshopEditModal = haModal(haConfig.getTemplateUrl('ha-reshop-selection-modal.html'), {
					backdrop: 'true',
					id: 'reshop-edit-selection',
					scope: $scope
				});
			};

			$rootScope.$on('grandTotal', function (e, grandTotal) {
				$scope.grandTotal = grandTotal;
			});


			// PROMOS
			// default to false
			$scope.promos = false;

			$scope.isPromoApplied = false;
			$scope.showPromoList = false;

			$scope.$on('updatePromoList', function ($event, promoList) {
				$scope.updatePromoList(promoList);
			});

			$scope.updatePromoList = function (promoList) {

				$scope.isPromoApplied = false;
				$scope.promos = false;
				angular.element('.promos-button').removeClass('applied selected');
				if (promoList && promoList.length > 0) {
					$scope.promos = [];
					angular.forEach(promoList, function (promo) {
						$scope.promos.push(promo);
						if (promo.Active) {
							$scope.isPromoApplied = true;
							angular.element('.promos-button').addClass('applied');
						}
					});
				}
				$scope.showPromoList = true;
			};

			$scope.showPromos = function () {
				if ($scope.showPromoList) {
					$scope.showPromoList = false;
				} else if ($scope.resultsLoaded) { // only show if done loading
					$scope.showPromoList = true;
				}
			};

			$scope.hidePromos = function () {
				$scope.showPromoList = false;
			};

			$scope.applyPromo = function (index) {
				$timeout(function () {
					angular.forEach($scope.promos, function (value) {
						value.Active = false;
					});
					$scope.promos[index].Active = true;
					$scope.isPromoApplied = true;
					$scope.$emit('applyPromo', $scope.promos[index].PromoCode);
					$scope.hidePromos();
				}, 0);
			};

			$scope.removePromo = function (index) {
				$timeout(function () {
					$scope.isPromoApplied = false;
					angular.element('.promos-button').removeClass('applied selected');
					$scope.promos[index].Active = false;
					$scope.$emit('removePromo', $scope.promos[index].PromoCode);
					$scope.hidePromos();
				}, 0);
			};

			$scope.body = angular.element(document).find('body');

			// if you press the escape key and the promo dropdown is open, close it
			$scope.handleEscPressed = function (event) {
				if (event.keyCode === 27) {
					if ($scope.showPromoList) {
						$timeout(function () {
							$scope.hidePromos();
						}, 0);
					}
				}
			};

			$scope.body.bind('keydown', $scope.handleEscPressed);

			// If you click outside of an open promo dropdown, close it
			$scope.body.bind('touchstart click', function (event) {
				var isClickedElementChildOfPopup = angular.element('.section.promos')
					.find(event.target)
						.length > 0;

				if (isClickedElementChildOfPopup) {
					return;
				} else if ($scope.showPromoList) {
					$timeout(function () {
						$scope.hidePromos();
					}, 0);
				}
			});

			// END PROMOS

			$scope.tripSummaryDetailsModal = function () {
				$timeout(function () {
						haUplift.refreshUplift();
					
				}, $rootScope.UpTimeout);
				$scope.summaryModel = angular.copy($scope.PassengerTripSummary);
				haModal({
					backdrop: 'true',
					id: 'trip-details-modal',
					templateUrl: '/tripSummaryDetailsModal.html',
					scope: $scope
					/*
					 extendScope: {
					 summaryModel: $scope.summaryModel,
					 TripSummary: $scope.TripSummary
					 }
					 */
				});
			};

		};

		HaBookStickyProgressBarController.$inject = ['$rootScope', '$scope', 'haModal', 'haPassengersService'];

		var HaBookStickyProgressBarLink = function ($scope, $el, $attrs) {
			$scope.seeFlightsDisabled = true;
			$scope.selectedLegs = [];
			$scope.isInvalidSearch = false;
			$scope.isFlightSearchMsg = '';
			$scope.showBookingWidget = false;
			//  $scope.isChecked = true;

			$scope.stickyBarContext = $attrs.barContext;
			$scope.stickyBarType = $attrs.barType;   //TODO Handle#######################################################################################################################
			$scope.receiptDetails = false;

			$scope.editSearch = function () {
				$scope.showBookingWidget = true;
				$timeout(function () {
					$scope.stickyBar = $el.find('[ha-book-sticky-progress-bar]');
					$scope.stickyBar.hide();
					$scope.bookingWidget = $el.parent().find('[booking-widget]');
					$scope.bookingWidgetContainer = $el.parent().find('.bookingWidgetContainer');
					$scope.bookingWidgetContainer.show();
					$scope.bookingWidget.show();
					$('body, html').animate({scrollTop: ($el.offset().top - 1)}, 'fast');
					$scope.bookingWidget.scope().activateWidget();
				}, 0);
			};   //TODO Remove#######################################################################################################################


			$scope.viewDetails = function () {
				if ($scope.receiptDetails) {
					$('body').removeClass('modal-active');
					$scope.receiptDetails = false;
					$scope.scrollFixed = false;
				} else {
					$scope.receiptDetails = true;
					$scope.scrollIfFixed();
				}
			};

			//Sticky header edit button changes
			//-------------------------------------//

			$scope.displayChangeFlightModal = function () {
				//$('.LoadingText').css('display', 'block');
				//$('#changeflight').css('display', 'none');

				$scope.modalData = JSON.parse($attrs.modalData);
				$scope.paxHide = true;
				$scope.flightSelectExpand = true;
				haFlightResultsAPI.updateChangeFlightModel().success(function (results) {
					if (results.responseBaseModel.IsSuccess) {
						angular.forEach(results.ChangeFlight, function (flight) {
							flight.OriginDateTime = new Date(parseInt(flight.OriginDateTime.match(/\d+/)[0], 10));
							flight.DestinationDateTime = new Date(parseInt(flight.DestinationDateTime.match(/\d+/)[0], 10));
							flight.isSelectedFlight = false; // Deselect upon load to allow users to go through the selection again.
						});

						angular.forEach(results.OldFlightDetails, function (flight) {
							flight.OriginDateTime = new Date(parseInt(flight.OriginDateTime.match(/\d+/)[0], 10));
							flight.DestinationDateTime = new Date(parseInt(flight.DestinationDateTime.match(/\d+/)[0], 10));
						});

						$scope.ChangeFlightModel = results;
						$scope.trips = results.ChangeFlight;
						$scope.tripsOld = results.OldFlightDetails;

						$scope.paxDetails = results.travellerInfo;
						$scope.selectedPax = [];

						$('.blockUIblockOverlay').css('display', 'none');
						$('.LoadingText').css('display', 'none');

						//fillinmg selected PAX Array
						angular.forEach(results.travellerInfo, function (pax, index) {
							if (pax.IsSelectedPax) {
								$scope.selectedPax.push(index);
							}
						});
						// $scope.enableContinueBtn($scope.selectedPax.length > 0);

						$scope.Init();

						$scope.displayEditReshopModal();

					}
				}).error(function () {
					$('.blockUIblockOverlay').css('display', 'none');
					$('.LoadingText').css('display', 'none');
					$scope.$rootScope.errorMsg = 'Session has expired';
					$scope.$rootScope.hasError = true;
				});
				setTimeout(function () {

					$('.ha-modal').animate({scrollTop: 0}, 'slow');

				}, 0);


			};


			$scope.Init = function () {
				$scope.createLegs($scope.trips.length);
				var legIndex = 0;
				angular.forEach($scope.trips, function () {
					$scope.selections.legs[legIndex].origin = $scope.trips[legIndex].OriginCityCode;
					$scope.selections.legs[legIndex].originCityData = {};
					$scope.selections.legs[legIndex].originCityData.Code = $scope.trips[legIndex].OriginCityCode;

					$scope.selections.legs[legIndex].destination = $scope.trips[legIndex].DestinationCityCode;
					$scope.selections.legs[legIndex].destinationCityData = {};
					$scope.selections.legs[legIndex].destinationCityData.Code = $scope.trips[legIndex].DestinationCityCode;

					var date = new Date($scope.trips[legIndex].OriginDateTime);

					$scope.selections.legs[legIndex].date = {
						year: date.getFullYear(),
						month: date.getMonth() + 1,
						day: date.getDate()
					};
					legIndex++;
				});
			};


			$scope.closeReshopModal = function ($event) {
				$scope.$modalCancel($event);
				//$scope.$reshopModal.hide();
				$scope.resetReshopModal();
				if ($scope.$modalClose) {
					$scope.$modalClose();
				}
			};

			$scope.resetReshopModal = function () {
				//$scope.$paxSelect.removeClass('hide');
				//$scope.$flightSelect.removeClass('expand');
				$scope.paxHide = false;
				$scope.flightSelectExpand = false;
				$('#changeflight').css('display', 'block');
				$scope.selectedLegs = [];
			};

			$scope.closeChangeFlightsBtnClick = function () {
				$scope.closeReshopModal();
			};

			$scope.showFlightControlsForLeg = function (legIndex, disabledItem) {

				if (!(disabledItem === true || disabledItem === 'true')) {
					$scope.$currentLegControls = $el.find('.leg-' + legIndex + '-controls');

					if ($scope.isLegSelected(legIndex)) {
						$scope.showLeg = undefined;
						$scope.removeLeg(legIndex);
						//$scope.$currentLegControls.removeClass('expand');
						//$scope.$currentLegControls.removeClass('active');
					}
					else {
						$scope.showLeg = legIndex;
						$scope.addLeg(legIndex);
						//$scope.$currentLegControls.addClass('expand');
						//    $scope.$currentLegControls.addClass('active');
					}

					$scope.enableSeeFlightsBtn($scope.selectedLegs.length > 0);
				}
			};

			$scope.enableSeeFlightsBtn = function (enable) {
				if (enable) {
					$scope.seeFlightsDisabled = false;
					//$scope.$seeFlightsBtn.removeClass('disabled');
				}
				else {
					$scope.seeFlightsDisabled = true;
					//$scope.$seeFlightsBtn.addClass('disabled');
				}
			};

			$scope.addLeg = function (legIndex) {
				if ($scope.selectedLegs.indexOf(legIndex) === -1) {
					$scope.selectedLegs.push(legIndex);
				}
			};

			$scope.removeLeg = function (legIndex) {
				if ($scope.selectedLegs.indexOf(legIndex) !== -1) {
					$scope.selectedLegs.splice($scope.selectedLegs.indexOf(legIndex), 1);
				}
			};

			$scope.isLegSelected = function (legIndex) {
				return $scope.selectedLegs.indexOf(legIndex) >= 0;
			};

			$scope.openCalendar = function (which) {
				$scope.trips[which].calExpanded = true;
				$scope.activeCalendar = which;
				return $scope;
			};

			$scope.closeCalendar = function () {
				//$el.find('.calendars-wrapper').removeClass('expanded');
				angular.forEach($scope.trips, function (trip) {
					trip.calExpanded = false;
				});
				return $scope;
			};

			// Used to prevent loss of focus where relevant.
			$scope.preventDefault = function ($event) {
				$event.preventDefault();
			};

			$scope.$on('HaLocationInput:focused', function () {
				$scope.activateWidget();
			});

			$scope.activateWidget = function () {
				$el.removeClass('closed');
				$el.addClass('expanded');
				setTimeout(function () {
					$el.addClass('active');

					// This repositions the carouesl to slide 2, which I have to do manually since
					// I'm hiding the carousels until after the widget expands due to performance
					// issues. If the widget ever needs to be dynamic for calendar-count per slide,
					// etc, this will need to be refactored/removed to cover the different use-cases.

					$el.find('[ha-carousel]').each(function () {
						$(this).scope().gotoSlide(2);
					});

				}, 1000);
			};

			$scope.$on('HaDateInput:focused', function (e, $el) {

				//$scope.openCalendar($el.parent().index(), $el.parents('.date-picker'));
				var targetLegIndex = parseInt($el.attr('target-leg'), 10);
				if (isNaN(targetLegIndex)) {
					return;
				}
				$scope.openCalendar(targetLegIndex, $el.parents('.date-picker'));
			});

			$scope.$on('HaDateInput:selected', function () {
				$scope.closeCalendar();
			});


			$scope.activeCalendar = 0;


			$scope.SearchNewFlights = function (e) {
				e.preventDefault();
				$('.LoadingTextReshop').css('display', 'block');
				//updating pax details
				angular.forEach($scope.ChangeFlightModel.travellerInfo, function (pax, $index) {
					if ($scope.selectedPax.indexOf($index) > -1) {
						pax.IsSelectedPax = true;
					}
					else {
						pax.IsSelectedPax = false;
					}
				});
				//updating flight details
				angular.forEach($scope.ChangeFlightModel.ChangeFlight, function (flight, $index) {
					if ($scope.selectedLegs.indexOf($index) > -1) {
						flight.isSelectedFlight = true;

						if ($scope.selections.legs[$index].date) {
							flight.OriginDate = moment(flight.OriginDateTime).format('MM/DD/YYYY');
						}
						else {
							flight.isSelectedFlight = false;
						}


					}
				});
				// Calling Flight Search
				haItineraryAPI.UpdatePaxAndFlightDetails($scope.ChangeFlightModel).success(function (results) {
					if (results.IsSuccess) {
						$('.LoadingTextReshop').css('display', 'none');
						$scope.isInvalidSearch = false;
						$scope.isFlightSearchMsg = '';
						angular.element('#changeFlightForm').submit();
					}
					else {
						$scope.isInvalidSearch = true;
						$scope.isFlightSearchMsg = results.Message;
						$('.LoadingTextReshop').css('display', 'none');
					}
				}).error(function () {
					console.log('Error in Flight Search');
				});

			};

			$scope.createLegs = function (howMany) {
				haCustomerSelections.createLegs(howMany, false);
			};

			//----------------------------------//

			$scope.scrollIfFixed = function () {
				$scope.windowHeight = angular.element($window).height();
				$scope.scrollFixed = false;
				$('body').removeClass('modal-active');
				if ($scope.receiptDetails) {
					$scope.expandedDetailsHeight = angular.element('.receipt-details').height() + 65;
					// console.log('expanded Details height:'+$scope.expandedDetailsHeight);
					// console.log('window height:'+$scope.windowHeight);
					if ($scope.expandedDetailsHeight > $scope.windowHeight && $scope.$root.fixed) {
						$('body').addClass('modal-active');
						$scope.scrollFixed = true;
					}
				}
			};


			var hasScrolled = false;
			angular.element($window).bind('scroll', function () {
				if (!hasScrolled) {
					$scope.progressBarOffsetY = angular.element('[ha-book-sticky-progress-bar]').offset().top;
					hasScrolled = true;
				}
				if (this.pageYOffset >= $scope.progressBarOffsetY) {
					$scope.$root.fixed = true;
					if ($scope.receiptDetails) {
						$scope.scrollIfFixed();
					}
				} else {
					$scope.$root.fixed = false;
				}
				$scope.$digest();
			});


			$scope.Initialize = function () {

				$scope.bookingWidget = $el.parent().find('[booking-widget]');                      //TODO Remove#######################################################################################################################
				$scope.bookingWidgetContainer = $el.parent().find('.bookingWidgetContainer');      //TODO Remove#######################################################################################################################
				$scope.bookingWidgetContainer.show();                                              //TODO Remove#######################################################################################################################
				$scope.bookingWidget.show();                                                       //TODO Remove#######################################################################################################################

				$scope.$emit('$haBookStickyProgressBarReady');

				setTimeout(function () {
					$scope.$root.fixed = false;
					$scope.bookingWidgetContainer.hide();
					$scope.bookingWidget.hide();
				}, 500);                                                  //TODO Remove#######################################################################################################################

				var flightSearchCookie = haUtils.getFlightQueryModelCookie();
				var reshopflightSearchCookie = haUtils.getReshopFlightQueryModelCookie();


				if (reshopflightSearchCookie !== null && reshopflightSearchCookie.FlightQueryTypeId === 3) {
					$.extend($scope, reshopflightSearchCookie);
					$scope.SelectedPassengersCount = reshopflightSearchCookie.SelectedPassengers.length;

				}

				if (flightSearchCookie == null) {
					return;
				}

				if (document.referrer.indexOf('deals-and-offers') != -1) {
				    //flightSearchCookie.FlightQueryTypeId = 2;
				    $scope.TripTypeID = flightSearchCookie.FlightQueryTypeId;
				    $scope.TripType = 1;
				    $scope.isValidLowFareDuration = false;
				    $scope.enableTCR = false;
				}
				else {
				    $scope.TripTypeID = flightSearchCookie.FlightQueryTypeId;
				};

				$scope.PassengerCount = flightSearchCookie.PassengerCount;

				if (($scope.PassengerTripSummary != null) &&
					($scope.PassengerTripSummary.SelectedHotel != null) &&
					($scope.PassengerTripSummary.SelectedHotel.SelectedRooms != null)) {
					$scope.HotelRoomCount = $scope.PassengerTripSummary.SelectedHotel.SelectedRooms.length;
					$scope.NumberOfNights = $scope.PassengerTripSummary.SelectedHotel.Nights;
				} else {
					haGlobals('itineraryDetails', function (itineraryDetails) {
						if ((itineraryDetails.PassengerTripSummary != null) &&
							(itineraryDetails.PassengerTripSummary.SelectedHotel != null) &&
							(itineraryDetails.PassengerTripSummary.SelectedHotel.SelectedRooms != null)) {
							$scope.HotelRoomCount = itineraryDetails.PassengerTripSummary.SelectedHotel.SelectedRooms.length;
							$scope.NumberOfNights = itineraryDetails.PassengerTripSummary.SelectedHotel.Nights;
						}
					});
				}

				var segment0 = flightSearchCookie.FlightSearchSegmentList[0];
				$scope.TripOrigin = segment0.OriginCityCode;
				$scope.TripDestination = segment0.DestinationCityCode;
				$scope.DepartureDate = segment0.DepartureDate;

				if (flightSearchCookie.FlightQueryTypeId === 2) {
					$scope.ReturnDate = flightSearchCookie.FlightSearchSegmentList[1].DepartureDate;
				}
				else if (flightSearchCookie.FlightQueryTypeId !== 1) {
					$scope.ReturnDate = flightSearchCookie.FlightSearchSegmentList[flightSearchCookie.FlightSearchSegmentList.length - 1].DepartureDate;
					$scope.TripTypeID = flightSearchCookie.FlightQueryTypeId = 0;
				}

			};                              //TODO Removes/Updates#######################################################################################################################

			//  $scope.assignFlightQueryModelCookie();

			$scope.UpdateBar = function (tripId) {

				var flightSearchCookie = haUtils.getFlightQueryModelCookie();
				var reshopflightSearchCookie = haUtils.getReshopFlightQueryModelCookie();

				if (reshopflightSearchCookie !== null && +reshopflightSearchCookie.FlightQueryTypeId === 3) {
					$.extend($scope, reshopflightSearchCookie);
					if (!$scope.$$phase) {
						$scope.$apply();
					}
				}
				if (flightSearchCookie == null) {
					return;
				}

				$scope.TripOrigin = flightSearchCookie.FlightSearchSegmentList[0].OriginCityCode;
				$scope.TripDestination = flightSearchCookie.FlightSearchSegmentList[0].DestinationCityCode;
				$scope.PassengerCount = flightSearchCookie.PassengerCount;
				$scope.DepartureDate = flightSearchCookie.FlightSearchSegmentList[0].DepartureDate;
				if (tripId === flightSearchCookie.FlightSearchSegmentList.length) {
					$scope.ReturnDate = flightSearchCookie.FlightSearchSegmentList[flightSearchCookie.FlightSearchSegmentList.length - 1].DepartureDate;
				}
				$scope.TripTypeID = flightSearchCookie.FlightQueryTypeId;

			};
			$scope.Initialize();

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaBookStickyProgressBarLink,
			controller: HaBookStickyProgressBarController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Help And Tips Directive
	// --------------------------------------------
	//
	// * **Class:** HaHelpAndTips
	// * **Author:** George Pantazis
	//
	// Widget for displaying information about a destination city in a given month.

	'use strict';

	var module = angular.module('haHelpAndTipsModule', []);

	module.directive('haHelpAndTips', ['haConfig', function (haConfig) {

		var HaHelpAndTipsController = function ($scope, haHelpAndTips) {

			// Month is 1-based (Jan=1, Dec=12).
			$scope.getData = function (city, month) {
				haHelpAndTips.getData(city, function (data) {
					$scope.cityName = data.cityName;
					$scope.month = data.months[(month - 1) % 12];
					$scope.monthDate = new Date(2000, month - 1);
					$scope.$emit('$haHelpAndTips:dataLoaded');
				});
			};
		};

		HaHelpAndTipsController.$inject = ['$scope', 'haHelpAndTips'];

		var HaHelpAndTipsLink = function ($scope, $el, $attrs) {

			$attrs.$observe('city', function () {
				$scope.getData($attrs.city, $attrs.month);
			});

			$scope.$on('$haHelpAndTips:dataLoaded', function () {
				$scope.$emit('$haHelpAndTips:Ready');
			});
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaHelpAndTipsLink,
			templateUrl: haConfig.getTemplateUrl('ha-help-and-tips-base-template.html'),
			controller: HaHelpAndTipsController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Recent Searches Directive
	// --------------------------------------------
	//
	// * **Class:** HaRecentSearches
	// * **Author:** George Pantazis
	//
	// Allows quick access to the user's most recent searches, which, when selected, should overwrite the current search.

	'use strict';

	var module = angular.module('haRecentSearchesModule', []);

	module.directive('haRecentSearches', ['haConfig', function (haConfig) {

		var HaRecentSearchesController = function ($scope, haSearchCache) {

			$scope.restoreSearch = function (which) {
				haSearchCache.restoreSearch(which);
				$scope.deactivate();
			};

			$scope.activate = function () {
				$scope.active = true;
				return $scope;
			};

			$scope.deactivate = function () {
				$scope.active = false;
				return $scope;
			};

			$scope.active = false;
		};

		HaRecentSearchesController.$inject = ['$scope', 'haSearchCache', 'haCustomerSelections'];

		var HaRecentSearchesLink = function ($scope, $el) {

			$scope.toggleActive = function () {
				if ($scope.active) {
					$scope.deactivate();
				} else {
					$el.find('.toggle').focus();
					$scope.activate();
				}
				return $scope;
			};

			// Used to prevent loss of focus where relevant.
			$scope.preventDefault = function ($event) {
				$event.preventDefault();
			};

			$scope.$emit('$haRecentSearchesReady');
		};

		return {
			restrict: 'A',
			scope: true,
			templateUrl: haConfig.getTemplateUrl('ha-recent-searches-base-template.html'),
			link: HaRecentSearchesLink,
			controller: HaRecentSearchesController
		};
	}]);

})(angular);
;
(function (angular) {

	/*jshint strict:false */

	// Ha Location Input Directive
	// --------------------------------------------
	//
	// * **Class:** HaLocationInput
	// * **Author:** George Pantazis
	//
	// Search and select possible origins or destinations.

	// 'use strict'; is removed here due to a bug in Safari 6, iOS webkit.
	// https://github.com/jrburke/requirejs/issues/392

	var module = angular.module('haLocationInputModule',
		['haUtilsModule', 'haTemplateCache', 'haCitiesAPI', 'ui.bootstrap.typeahead', 'haAdvanceToNextInputService']);

	module.directive('haLocationInput', ['$compile', '$timeout', 'haTemplateCache', 'haConfig',
		function ($compile, $timeout, haTemplateCache, haConfig) {

			// Pre-Load template contents on initial load to the templateCache for faster rendering of modal popup
			$timeout(function () {
				haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-hawaii'));
				haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-na'));
				haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-asia'));
				//haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-everywhere'));
				haTemplateCache.get(haConfig.getRazorTemplateUrl('destination-modal-base'));
			}, 1000);

			var link = function ($scope, $el, $attrs) {
				var scope = $scope.haLocationInput = $scope.haLocationInput || {};

				// Bindings
				// --------

				$attrs.$observe('required', function (newVal) {
					$scope.required = newVal;
				});

				// Scope Variable Assignments
				// --------------------------

				// NEP: Because 'errorMessage is used the ha-input parent scope. Wrong, I know.'
				$scope.errorMessage = $attrs.errorMessage;

				scope.placeholder = $attrs.placeholder;
				scope.label = $attrs.label;
				scope.required = $attrs.required;
				scope.tagname = $attrs.tagname;
				scope.tagPrefix = $attrs.tagPrefix !== undefined ? $attrs.tagPrefix : '';
				scope.tagname2 = $attrs.tagname2;  //for RT query (additional Parameter)
				scope.tagPrefix2 = $attrs.tagPrefix2 !== undefined ? $attrs.tagPrefix2 : '';  //for RT query (additional Parameter)
				scope.disabledInput = $attrs.disabledInput === 'true';
				scope.selectedCity = scope.selectedCity || $attrs.haLocationSelectedCity;
			};

			return {
				restrict: 'A',
				scope: {
					preset: '=',
					disabledInput: '=',
					originLeg: '=',
					destinationLeg: '=',
					cityFilterIndex: '=',
					cityFilterIsDeparture: '='
				},
				link: link,
				templateUrl: haConfig.getRazorTemplateUrl('ha-location-input-base-template'),
				controller: 'haLocationInputController'
			};
		}
	]);

	module.controller('haLocationInputController', ['$scope', '$log', 'haCitiesAPI',
		function ($scope, $log, haCitiesAPI) {
			var scope = $scope.haLocationInput = $scope.haLocationInput || {};

			scope.CityList = [];
			scope.preset = $scope.preset;
			scope.disabled = $scope.disabledInput;
			scope.originLeg = $scope.originLeg;
			scope.destinationLeg = $scope.destinationLeg;
			scope.cityFilterIndex = $scope.cityFilterIndex;
			scope.cityFilterIsDeparture = !!$scope.cityFilterIsDeparture;

			function init() {
				if ($scope.$root.DefaultCity != null && !scope.selectedCity) {
					scope.selectedCity = $scope.$root.DefaultCity;
				}

				var handleCityList = function (cityList) {
					$log.debug('SEND: haLocationInput:ready:CityList');
					scope.CityList = cityList;
					$scope.$broadcast('haLocationInput:ready:CityList');
				};

				if (scope.cityFilterIndex != null) {
					haCitiesAPI.getFilteredCityList(scope.cityFilterIndex, scope.cityFilterIsDeparture)
					.then(handleCityList);
				} else {
					haCitiesAPI.getCityList()
					.then(handleCityList);
				}
			}

			init();
		}]);
})(angular);
;
(function (angular) {

	'use strict';

	// Aiport or Address Type-ahead input
	// ===================================
	//
	// * **Class:** haGenericLocationInputModule
	// Type-ahead input which autocompletes airports and cities

	var module = angular.module('haGenericLocationInputModule', ['haCitiesModule', 'ui.bootstrap.typeahead.ha']);

	module.directive('haGenericLocationInput', ['haConfig', 'haUtils', 'haCitiesSvc', '$q', '$log', '$timeout',
		function (haConfig, haUtils, haCitiesSvc, $q, $log, $timeout) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-generic-location-input-template.html'),
				restrict: 'A',
				scope: {
					ngModel: '=',
					name: '@',
					label: '@',
					placeholder: '@',
					required: '@',
					classes: '@',
					disabled: '@',
					countries: '='
				},
				link: function ($scope, $el) {
					// Add the ID from the CT data into the airport data
					// handling results
					$scope.getComboResultsForInput = function (userInput) {

						return $q.all([
							haCitiesSvc.getMatchingCities(userInput),
							haCitiesSvc.searchCarLocations(userInput, $scope.countries ),
							haCitiesSvc.getAllCarLocations()
						])
						.then(function (resolved) {

							var results = [];
							var airports = resolved[0];
							var carLocations = resolved[1];
							var allCarLocations = resolved[2];

							angular.forEach(airports, function(airport) {
								// only allow airport locations in the specified countries
								if (!_.includes($scope.countries, airport.CountryCode)) {
									return;
								}

								var location = _.find(allCarLocations[airport.CountryCode], 'airport', airport.Code);
								if (location) {
									results.push({
										DisplayName: airport.DisplayName,
										IsHACity: airport.IsHACity,
										Country: airport.CountryCode,
										Code: airport.Code,
										Id: location.id
									});
								}
							});
							results = haUtils.removeDuplicatesFromArray(results, 'DisplayName').slice(0, 5)
							if (results.length) {
								results[0].firstAirportResult = true; // for section header
							}

							if (carLocations && carLocations.length) {
								var cleanPlaces = carLocations
									.filter(function (place) {
										// make sure an airport in the top list isn't repeated at the bottom
										return !place.airport || !_.some(results, 'Code', place.airport);
									})
									.map(function (place) {
										return {
											DisplayName: place.name,
											isAddress: true,
											Country: place.country,
											Id: place.id
										};
									});
								if (cleanPlaces.length) {
									cleanPlaces[0].firstPlacesResult = true; // for section header
									results = results.concat(cleanPlaces);
								}
							}
							return results;
						})
						.catch(function(error) {
							$log.error(error);
							$scope.Error = error;
							return $q.reject(error);
						});
					};

					// fires when coming from modal window
					$scope.onSelected = function (city) {
						$scope.ngModel = city;
						$timeout(function () {
							$scope.focus();
						}, 0);
					};

					// fires when clicking or tabbing a highlighted result
					$scope.onSelect = function () {
						$timeout(function () {
							$scope.focus();
						}, 0);
					};

					$scope.onClick = function ($event) {
						// select input value on click
						$event.target.select();
					};

				$scope.onFocus = function() {
					// scroll input to top on mobile
					if ($scope.$root.isMobile) {
						$('html, body').animate({scrollTop: $el.offset().top - 60});
					}
				};

					$scope.pinClicked = function () {
						$scope.$emit('haWhereWeFlyPinClicked');
					};

					$scope.focus = function () {
						setTimeout(function () {
							$el.find('input[type="text"]').focus();
						}, 0);
					};
				}
			};
		}]);

})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haLocationInputModule');

	module.directive('haLocationTypeahead', [
		'$compile', '$log', '$filter', '$timeout', 'haAdvanceToNextInput', 'haConfig',
		function ($compile, $log, $filter, $timeout, haAdvanceTo, haConfig) {
			var link = function ($scope, $element /*, $attrs */) {
				var scope = $scope.haLocationData;

				// Directive link function sets up the twitter typeahead component for location
				var onFocus = function (/* $event */) {
					$scope.$apply(function () {
						scope.focused = true;

						$scope.$emit('HaLocationInput:focused', $element);

						if ($scope.$root.DefaultCity != null && !scope.selectedCity) {
							scope.selectedCity = $scope.$root.DefaultCity;
						}

						// $element.find('input').not('.tt-hint').select();
					});
				};

				var onBlur = function ($event) {
					$timeout(function () {
						// $scope.select($scope.activeIdx);

						scope.focused = false;

						if ($event != null) {
							var input = $event.target;
							if ((input != null) && (input.value == null) || (input.value === '')) {
								$scope.clearSelection();
							}
						}

						$scope.$emit('HaLocationInput:blurred');
					}, 350);
				};

				$element.find('input[typeahead]')
				.on('focus', onFocus)
				.on('blur', onBlur);

				var initSelection = function () {
					if ((scope.CityList == null) || (scope.CityList.length === 0)) {
						return;
					}

					$log.debug('haLocationTypeahead._onCityListReady');

					var match;
					var filter = $filter('filter');
					// Pre-select city by code
					if (scope.preset) {
						match = filter(scope.CityList, {Code: scope.preset});
						if (match.length > 0) {
							$log.debug('haLocationTypeahead: Matched "preset" (' + scope.preset + ').');
							$scope.processSelection(match[0]);
						} else {
							$log.debug('haLocationTypeahead: Could not set "preset" (' + scope.preset + '). No match in CityList.');
						}
					} else {
						var code;
						if ((scope.originLeg != null) && (scope.originLeg.origin != null)) {
							code = scope.originLeg.origin;
						} else if ((scope.destinationLeg != null) && (scope.destinationLeg.destination != null)) {
							code = scope.destinationLeg.destination;
						}
						if (code != null) {
							match = filter(scope.CityList, {Code: code});
							if (match.length > 0) {
								$log.debug('haLocationTypeahead: Matched "code" (' + code + ').');
								$scope.processSelection(match[0]);
							} else {
								$log.debug('haLocationTypeahead: Could not set "code" (' + code + '). No match in CityList.');
							}
						} else {
							$log.debug('haLocationTypeahead: No legs specified.');
						}
					}
				};

				$log.debug('ON: haLocationInput:ready:CityList');
				$scope.$on('haLocationInput:ready:CityList', function () {
					$log.debug('RCVD: haLocationInput:ready:CityList');
					initSelection();
				});

				$log.debug('ON: $root.selections.legs:ready');
				$scope.$on('$root.selections.legs:ready', function () {
					$log.debug('RCVD: $root.selections.legs:ready');
					initSelection();
				});

				initSelection();

				// NEP: Hack to allow pre-filled fields to match in the typeahead
				$scope.$watch('currentCityDisplayName', function (newVal) {
					if (newVal === undefined) {
						return;
					}

					var inputScope = $element.parents('[ha-input]').scope();
					if ((inputScope != null) && (inputScope.$model != null)) {
						inputScope.$model.$setViewValue(newVal);
						inputScope.$model.$render();
					}
				});

				$scope.advanceToNextInput = function () {
					haAdvanceTo.next($element);
				};

			};

			return {
				restrict: 'AE',
				link: link,
				scope: {
					haLocationData: '=haLocationData'
				},
				replace: true,
				controller: 'haLocationTypeaheadController',
				templateUrl: haConfig.getRazorTemplateUrl('ha-location-typeahead')
			};
		}]);

	module.controller('haLocationTypeaheadController', ['$scope', '$timeout', 'haUtils', function ($scope, $timeout, haUtils) {
		var scope = $scope.haLocationData;

		scope.focused = false;

		// Make token pairs to handle searches like "San Fr"
		var makeTokenPairs = function (tokens) {
			var pairs = [];

			for (var i = 0; i < tokens.length - 1; i++) {
				pairs.push(tokens[i] + ' ' + tokens[i + 1]);  // Pair with space
				pairs.push(tokens[i] + tokens[i + 1]);        // Pair without space
			}

			return tokens.concat(pairs);
		};

		$scope.haAirportSort = function ($viewValue) {
			return function (city) {
				var sum = 0;

				if ($viewValue == null) {
					return sum;
				}

				// Perfect match
				if (city.DisplayName === $viewValue) {
					sum = 100;
				}

				var concatValue = $viewValue.split(' ').join('');

				if ((city.Code != null) && (city.Code.toUpperCase() === $viewValue.toUpperCase())) {
					sum += 1;
				}

				if (city.DisplayName != null) {
					var displayNameTokens = makeTokenPairs(city.DisplayName.split(' '));

					angular.forEach(displayNameTokens, function (token) {
						if (token.toLowerCase().indexOf($viewValue.toLowerCase()) === 0) {
							sum += 0.3;
						} else if (token.toLowerCase().indexOf(concatValue.toLowerCase()) === 0) {
							sum += 0.15;
						}
					});
				}

				if (city.LinkedAirportCodes != null) {
					var linkedAirportCodes = city.LinkedAirportCodes.split(',');

					angular.forEach(linkedAirportCodes, function (code) {
						if (code.toUpperCase() === $viewValue.toUpperCase()) {
							sum += 0.2;
						}
					});
				}

				if (city.SearchTags != null) {
					var searchTagTokens = makeTokenPairs(city.SearchTags.split(' '));

					angular.forEach(searchTagTokens, function (token) {
						if ((token.toLowerCase().indexOf($viewValue.toLowerCase()) === 0) || (token.toLowerCase().indexOf(concatValue.toLowerCase()) === 0)) {
							sum += 0.1;
						}
					});
				}

				// Only boost weight for HA Cities that aleady match!
				if ((sum > 0) && (city.IsHACity)) {
					sum += 0.5;
				}

				city._weight = sum; // jscs:ignore disallowDanglingUnderscores

				return -sum;    // correct sort order
			};
		};

		$scope.haAirportFilter = function (city) {
			return city._weight > 0; // jscs:ignore disallowDanglingUnderscores
		};

		// All Asian langs match on first 2 chars, English (default) match on first 3 chars.
		var isAsianLang = haUtils.isJP() || haUtils.isKR() || haUtils.isCN() || haUtils.isTW();
		$scope.typeaheadMinLength = function () {
			return (isAsianLang) ? 2 : 3;
		};

		var setLegs = function (cityData) {
			if (scope.originLeg != null) {
				scope.originLeg.origin = cityData.Code;
				scope.originLeg.originCityData = cityData;
			}
			if (scope.destinationLeg != null) {
				scope.destinationLeg.destination = cityData.Code;
				scope.destinationLeg.destinationCityData = cityData;
			}
		};

		$scope.processSelection = function (cityData) {
			$scope.$root.DefaultCity = null;
			$scope.currentCityDisplayName = cityData.DisplayName;

			scope.selectedCity = cityData;
			scope.currentCityCode = cityData.Code;
			scope.linkedCityCode = cityData.LinkedAirportCodes;
			setLegs(cityData);
		};

		$scope.clearSelection = function () {
			scope.currentCityCode = null;
			scope.linkedCityCode = null;
		};

		$scope.onSelected = function ($item) {
			$timeout(function () {
				scope.focused = false;
				// $scope.$item = cityData;
				// $scope.$model = cityData.DisplayName;
				// $scope.$label = cityData.DisplayName;

				$scope.processSelection($item);
				$scope.advanceToNextInput();
			}, 200);
		};

	}]);
}(angular));
;
(function (angular) {
	'use strict';

	var module = angular.module('haLocationInputModule');

	module.directive('haLocationModal', ['haModal', 'haCitiesSvc', 'haConfig', '$timeout', function (haModal, haCitiesSvc, haConfig, $timeout) {
		function link($scope, $element, $attrs) {
			// Directive link function handles wiring up to specified event name and launching model dialog

			var eventName = $attrs.eventName || 'click';
			$scope.filterName = $attrs.filterFn !== '' ? $attrs.filterFn : null;
			$scope.cityListType = $attrs.cityListType || 'normal';

			var onOpen = function ($event) {
				$scope.updateAvailableCities($scope.region);

				var sender = $event.currentTarget;

				// Open modal dialog
				haModal(haConfig.getRazorTemplateUrl('destination-modal-base'), {
					id: 'locationModal',
					backdrop: 'true',
					scope: $scope,
					cancel: {
						fn: function () {
							$timeout(function () {
								$(sender).prevAll('[ha-typeahead]').focus();
							}, 300);
						}
					}
				});

			};

			$element.on(eventName, onOpen);

			// use different source for city list based on cityListType
			if ($scope.cityListType.match('^reshop')) {
				var reshopParam = $scope.cityListType.split('-');
				if (reshopParam.length === 3) {
					var leg = reshopParam[1];
					var isOrigin = reshopParam[2] === 'origin';
					haCitiesSvc.getFilteredCities(leg, isOrigin).then(function (data) {
						$scope.cities = data;
					});
				}
			} else if ($scope.cityListType.match('^expertbooking')) {
				haCitiesSvc.getAllCitiesExpertBooking().then(function (data) {
					$scope.cities = data;
				});
			} else {
				haCitiesSvc.getAllCities().then(function (data) {
					$scope.cities = data;
				});
			}
		}

		return {
			restrict: 'A',
			link: link,
			controller: 'haLocationModalController',
			scope: {
				region: '@haLocationModal',
				selectedCity: '=haLocationSelectedCity'
			}
		};
	}]);

	module.controller('haLocationModalController', ['$scope', '$filter', function ($scope, $filter) {
		// Initializes the controller
		$scope.regionalCities = [];

		function getMarketCode(region) {
			// Converts a specified market code into its well known numeric value

			var marketCode = 0;

			switch (region) {
				case 'hawaii':
					marketCode = 1;
					break;
				case 'na':
					marketCode = 2;
					break;
				case 'asia':
					marketCode = 3;
					break;
			}

			return marketCode;
		}

		function calculateColumn(region) {
			switch (region) {
				case 'hawaii':
					if ($scope.$root.isMobile) buildColumnList($scope.regionalCities, 'IslandName', 1);
					break;
				case 'na':
					buildColumnList($scope.regionalCities, 'State', 4);
					break;
				case 'asia':
					buildColumnList($scope.regionalCities, 'CountryName', 4);
					break;
			}
		}

		function buildColumnList(cityList, groupFieldName, columnCount) {
			var count = 0;
			var columnContent = [];
			$scope.regionalCities = [];
			cityList = $filter('citiesOrderBy')(cityList, groupFieldName, false);
			var itemCount = cityList.length;
			cityList = _.groupBy(cityList, groupFieldName);
			var average = Math.round((itemCount + Object.keys(cityList).length) / columnCount);

			angular.forEach(cityList, function (group, key) {
				group = $filter('citiesOrderBy')(group, 'NonLocalisedCityName', false);
				if (count + group.length > average * 1.5 ) {
					// if not much slots left, just continue to next column
					if (average - count < average / 3) {
						$scope.regionalCities.push(columnContent);
						count = 0;
						columnContent =[];
					}
					// break group if too long
					if (group.length > average * 1.5) {
						var cut = average - count + 3;
						columnContent.push({ Name: key, Cities: group.slice(0, cut)});
						$scope.regionalCities.push(columnContent);
						columnContent = [];
						columnContent.push({ Name: key, Cities: group.slice(cut)});
						count = group.length - cut;
					} else {
						columnContent.push({ Name: key, Cities: group});
						count = group.length + 1;
					}
				} else {
					count += group.length + 1;
					columnContent.push({ Name: key, Cities: group});
				}
				if (count >= average && $scope.regionalCities.length < columnCount - 1) {
					count = 0;
					$scope.regionalCities.push(columnContent);
					columnContent = [];
				}
			});
			
			// Sort islands according to priority assigned in Sitecore
			if (groupFieldName === 'IslandName') {
				angular.forEach(columnContent, function(group) {
					group.islandSortOrder = !!group.Cities ? group.Cities[0].IslandSortOrder : 9;
				});
			}

			$scope.regionalCities.push(columnContent);
		}

		$scope.$watch('region', function (current, old) {
			if (current === old) {
				return;
			}

			$scope.updateAvailableCities(current);
		});

		$scope.selectCity = function (city) {
			// Handles selection of a new city, called from view

			// HACK!
			var parent;
			do {
				parent = $scope.$parent;
				if (parent.onSelected != null) {
					parent.onSelected(city);
					break;
				}
			} while (parent != null);
			$scope.$modalClose(); // Added to scope from haModelService
		};

		$scope.selectRegion = function (region) {
			// Handles selection of a new region (tab), called from view
			$scope.region = region;
		};

		$scope.isRegion = function (region) {
			return this.region === region;
		};

		$scope.getCitiesByMarket = function (marketId) {
			// Filters the available cities by the specified marketId

			if (!$scope.cities) {
				return [];
			}

			if ($scope.filterName === 'isHACity') {
				return $scope.cities.filter(function (city) {
					return city.Market === marketId && city.IsHACity === true;
				});

			} else {
				return $scope.cities.filter(function (city) {
					return city.Market === marketId;
				});
			}
		};

		$scope.regionHasCities = function(region) {
			var marketCode = getMarketCode(region);
			var cities = $scope.getCitiesByMarket(marketCode);
			return !!cities.length;
		}

		$scope.updateAvailableCities = function (region) {
			// Updates the available regional cities by filtering by the specified region
			// ex) hawaii, na, asia

			var marketCode = getMarketCode(region);
			$scope.regionalCities = $scope.getCitiesByMarket(marketCode);
			calculateColumn(region);
		};

	}]);

	module.filter('citiesOrderBy', [function () {
	    return function (items, field, reverse) {
	        var filtered = [];
	        angular.forEach(items, function (item) {
	            filtered.push(item);
	        });
	        filtered.sort(function (a, b) {
	            return (a[field] > b[field] ? 1 : -1);
	        });
	        if (reverse) filtered.reverse();
	        return filtered;
		};
	}]);

}(angular));
;
(function (angular) {

	// Ha Date Input Directive
	// --------------------------------------------
	//
	// * **Class:** HaDateInput
	// * **Author:** George Pantazis
	//
	// Validated date input for a given flight leg.

	'use strict';

	var module = angular.module('haDateInputModule', ['haAdvanceToNextInputService']);

	module.directive('haDateInput', ['haUtils', 'haConfig', 'haAdvanceToNextInput', function (haUtils, haConfig, haAdvanceTo) {

		var HaDateInputController = function ($scope, haCustomerSelections) {

			$scope.updateTimestamp = function (date) {

				if (date) {
					$scope.date = haUtils.leftPad(date.month, 2) + haUtils.leftPad(date.day, 2) + date.year.toString();
					$scope.timestamp = new Date(date.year, date.month - 1, date.day);
					$scope.updated = date.updated;
				} else {
					$scope.date = $scope.timestamp = undefined;
				}
			};

			$scope.setDate = haCustomerSelections.setLegDate;

			$scope.getLegData = function (which) {
				return haCustomerSelections.getLeg(which);
			};
		};

		HaDateInputController.$inject = ['$scope', 'haCustomerSelections'];

		var HaDateInputLink = function ($scope, $el, $attrs) {

			$scope.onFocus = function () {
				$el.addClass('focused');
				$scope.$emit('HaDateInput:focused', $el);
			};

			$scope.onBlur = function () {
				$el.removeClass('focused');
				$scope.$emit('HaDateInput:selected', $el);
			};


			$scope.advanceToNextInput = function () {
				haAdvanceTo.next($el);
			};


			// $scope.autoprogress = function () {
			//     if (!$scope.$root.$progressionTimeout) {

			//         $scope.$root.$progressionTimeout = setTimeout(function () {

			//             delete $scope.$root.$progressionTimeout;

			//             var $selectable = $('[ha-progress-on-select] :tabbable'),
			//               currentIndex = $selectable.index($el.find(':tabbable')),
			//               $nextElement = $selectable.eq(currentIndex + 1);

			//             if ($nextElement.length > 0 && currentIndex + 1 > 0) {
			//                 $nextElement.focus();
			//                 return;
			//             }

			//             $scope.$emit('HaDateInput:selected', $el);

			//         }, 0);
			//     }
			// };

			$scope.legData = $scope.getLegData($attrs.targetLeg);

			$scope.$watch('$root.selections.legs', function () {

				var previousUpdate = $scope.updated;

				$scope.legData = $scope.getLegData($attrs.targetLeg);

				$scope.updateTimestamp($scope.legData.date);

				if ($scope.updated !== previousUpdate) {
					$scope.advanceToNextInput();
				}

			}, true);

			$scope.$watch('date', function (nv, ov) {
				if (nv === ov) {
					return;
				}
				if ($scope.date && $scope.legData) {
					$scope.setDate($attrs.targetLeg, {
						month: $scope.date.substr(0, 2),
						day: $scope.date.substr(2, 2),
						year: $scope.date.substr(4, 4)
					});
				}

				// setTimeout(function() {

				//   var $selectable = $('[ha-progress-on-select] :tabbable'),
				//     currentIndex = $selectable.index($el.find(':tabbable')),
				//     $nextElement = $selectable.eq(currentIndex + 1);

				//   if ($nextElement.length > 0 && currentIndex + 1 > 0) {
				//     $nextElement.focus();
				//     return;
				//   }

				//   $scope.$emit('HaDateInput:selected', $el);

				// }, 0);

			});

			$scope.label = $attrs.label;

			$scope.tagname = $attrs.tagname;
			$scope.tagPrefix = $attrs.tagPrefix !== undefined ? $attrs.tagPrefix : '';
			$scope.placeholder = $attrs.placeholder;
			$scope.required = $attrs.required;
			$scope.errorMessage = $attrs.errorMessage;
			$scope.$emit('$haDateInputReady');
		};

		return {
			restrict: 'A',
			scope: true,
			templateUrl: haConfig.getTemplateUrl('ha-date-input-base-template.html'),
			link: HaDateInputLink,
			controller: HaDateInputController
		};
	}]);

	module.directive('haDateFormatter', ['$filter', '$locale', function ($filter, $locale) {
		// Wrap 'shortDate' format to allow customization
		function shortDate() {
			// if (!haUtils.isEN()) {
			return $locale.DATETIME_FORMATS.shortDate;
			// } else {
			//     return 'M/d/yyyy';
			// }
		}

		return {
			require: 'ngModel',
			link: function (scope, element, attrs, ngModelCtrl) {
				ngModelCtrl.$formatters.push(function (value) {
					if (value == null || typeof value !== 'string') {
						return '';
					}

					var model = {
						month: parseInt(value.substr(0, 2), 10) - 1,    // Yeah, months are 0-11 in JS...
						day: parseInt(value.substr(2, 2), 10),
						year: parseInt(value.substr(4, 4), 10)
					};
					var date = new Date(model.year, model.month, model.day, 0, 0, 0, 0);
					return $filter('date')(date, shortDate());
				});
			}
		};
	}]);

	module.run(['$locale', 'haUtils', function ($locale, haUtils) {
		// Override the EN 'shortDate' to use 4-digit years
		if (haUtils.isEN()) {
			$locale.DATETIME_FORMATS.shortDate = 'M/d/yyyy';
		}
	}]);

})(angular);;
(function (angular) {

	// Share Widget Directive
	// --------------------------------------------
	//
	// * **Class:** ShareWidget
	// * **Author:** Cory Shaw
	//
	// Allows the user to share the current content on social networks.

	'use strict';

	var module = angular.module('shareWidgetModule', []);

	module.directive('shareWidget', ['haConfig', function (haConfig) {

		var ShareWidgetController = function ($scope) {
			$scope.shareServices = ['Twitter', 'LinkedIn', 'Facebook', 'Google+'];
		};

		var ShareWidgetLink = function ($scope) {
			$scope.$emit('$methodsBound');
		};

		ShareWidgetController.$inject = ['$scope'];

		return {
			restrict: 'A',
			scope: true,
			link: ShareWidgetLink,
			controller: ShareWidgetController,
			templateUrl: haConfig.getTemplateUrl('share-widget-base-template')
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Calendar Events Directive
	// --------------------------------------------
	//
	// * **Class:** HaCalendarEvents
	// * **Author:** George Pantazis
	//

	'use strict';

	var module = angular.module('haCalendarEventsModule', []);

	module.directive('haCalendarEvents', [function () {

		var HaCalendarEventsController = function ($scope, haCalendarEventsService) {

			$scope.getCalendarEvents = function (id, cb) {
				haCalendarEventsService.getEvents(id, function (data) {
					cb(data);
				});
			};
		};

		HaCalendarEventsController.$inject = ['$scope', 'haCalendarEventsService'];

		var HaCalendarEventsLink = function ($scope, $el, $attrs) {

			$scope.getCalendarEvents($attrs.haCalendarEvents, function (data) {
				$.extend($scope, data);
				$scope.$broadcast('$haCalendarEventsLoaded');
			});
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaCalendarEventsLink,
			controller: HaCalendarEventsController
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haCalendarModule', []);

	function YMD(date) {
		return date.format('YYYY-MM-DD');
	}

	function YM(date) {
		return date.format('YYYY-MM');
	}

	module.directive('haCalendar', ['$compile', '$locale', 'haConfig', 'haUnavailableDays', 'haAriaLiveService',
		function ($compile, $locale, haConfig, haUnavailableDays, ariaLiveSvc) {
			return {
				templateUrl: haConfig.getTemplateUrl('ha-calendar.html'),
				restrict: 'A',
				controller: ['$timeout','$rootScope', '$scope', '$element', '$attrs', '$parse', function ($timeout, $rootScope, scope, $el, attrs, $parse) {

					var options = $.extend(true, {
						calendars: 1,
						range_start: moment([1900]),
						range_end: moment([3000]),
                        double_wide: false,
						render: {
							calendar: function ($cal, isReturn) {

								if (options.rangeRender) {
									options.rangeRender($cal);
								}

                                if (options.double_wide) {
                                    $cal.addClass('double-wide');
                                }

								var idx = options.idx || 0;
								// if an airport pair is involved
								if (scope.legs && scope.legs[idx]) {
									var pair = getPairString(scope.legs[idx], isReturn);
									if (pair && pair.length) {
										haUnavailableDays.get(pair).then(function (data) {
											$cal.find('td[date]:not(.disabled)').each(function () {
												var $td = $(this);
												if (data.contains(moment($td.attr('date'), 'YYYY-MM-DD').toDate())) {
													$td.addClass('disabled');
												}
											});
										});
									}
									if (scope.tripType === 2) {
										$cal.addClass('double-wide');
									} else {
										$cal.removeClass('double-wide');
									}
								}
								// messages
								if (scope.messages) {
									var $wrap = $cal.closest('.datepicker');
									$wrap.find('header > .calendar-messages').html(scope.messages[idx]);
									ariaLiveSvc.updateMessage(scope.messages[idx]);
								}
								// optional onRender function
								if (options.onRender) {
									options.onRender($cal);
								}
							},
                            caption: function ($caption, date) {
                                var month = date.month();
                                var monthNames = $locale.DATETIME_FORMATS.MONTH;
                                var year = date.year();

                                $caption.html(monthNames[month] + ' ' + year);
                            },
							day: function ($td, date) {
								$td.html(date.date());
							}
						}
					}, scope.$eval(attrs.haCalendar));

					adjustNumCalendars(scope.tripType);

					scope.$on('trip type changed', function (event, data) {
						adjustNumCalendars(data);
						render();
					});

					scope.$on('airport changed', function () {
						render();
					});

					function adjustNumCalendars(tripType) {
						if ($rootScope.isMobile) {
							options.calendars = 1;
						} else if (typeof tripType === 'undefined') {
							return;
						} else if (tripType === 2) {
							options.calendars = 2;
						} else {
							options.calendars = 1;
						}
					}

					//get an airport pair string from a leg. e.g. "LAX-HNL"
					function getPairString(leg, reverse) {
						if (!leg || !leg.origin || !leg.origin.Code || !leg.destination || !leg.destination.Code) {
							return;
						}
						return reverse ? (leg.destination.Code + '-' + leg.origin.Code) : (leg.origin.Code + '-' + leg.destination.Code);
					}

					$el.children('header').children('h2').text(options.calendarHeading);

					var now = moment().startOf('day');
					var month_template = $el.find('li').remove()[0].outerHTML;
					var createMonth = $compile(month_template);
					var $month_list = $el.find('ol.months');

					if (options.hasOwnProperty("watch")) {
						scope.$watch(options.watch, render);
					}

					scope.$watch(function () {
						return options.range_start.valueOf();
					}, render);
					scope.$watch(function () {
						return options.range_end.valueOf();
					}, render);
					//The `viewing` date is the month of the leftmost calendar.
					//You can have a date `selected` out of view- or, for multi-calendar displays,
					//on a calendar that is visible but not within the leftmost one.
					scope.$watch(function () {
						return options.viewing.valueOf();
					}, render);

					var model_getter = $parse(attrs.haCalendarModel || 'date');
					var model = model_getter.bind(model_getter, scope);
					this.select = function (value) {
						$timeout(function() {
							model_getter.assign(scope, value);
						}, 0);
						scope.$emit('select', value);
					};

					scope.$watch(model, function (date) {

						$month_list.find('.selected').removeClass('selected');

						if (!date) {
							date = Math.max(new Date(), options.range_start.valueOf());
						}
						date = moment(date);
						if (!date.isValid() || !inRange(date)) {
							return;
						}

						// Force the viewer to display the currently selected month if sticky_viewer is set.
						if (options.sticky_viewer) {
							set_viewing(date);
						}

						var $li = $month_list.find('li[date=' + YM(date) + ']');
						if ($li.length) {
							return render();
						}
					});

					function get_viewing() {
						return options.viewing && moment(options.viewing).startOf('month');
					}

					function set_viewing(date) {
						options.viewing.year(date.year()).month(date.month());
					}

					this.render = render;
					function render() {
						var viewing = get_viewing();
						var month = moment(viewing);//create clone because we mutate it below
						$month_list.empty();
						//iterate over # of displayed months
						for (var i = 0; i < options.calendars; i++) {
							addMonthFor(month);
							month.add(1, 'months');
						}

						var selected = moment(model() || NaN);
						if (selected.isValid()) {
							$month_list.find('td[date=' + YMD(selected) + ']').addClass('selected');
						}
						options.render.calendar($el, options.isReturn);
					}

					scope.prev = function () {
						var date = dateOf('li:first').add(-1, 'month');
						set_viewing(date);
						scope.$emit('prev', date);
					};
					scope.next = function () {
						var date = dateOf('li:first');
						set_viewing(date.add(1, 'month'));
						scope.$emit('next', date);
					};

                    // scope.prev = this.prev;
                    // scope.next = this.next;

					/* not used
				function edgeMonth(date) {
					return options.range_start.isSame(date, 'month') || options.range_end.isSame(date, 'month');
					 } */

					this.inRange = inRange;
					function inRange(date) {
						return YMD(date).valueOf() >= YMD(options.range_start) && YMD(date).valueOf() < YMD(options.range_end);
					}

					this.dateOf = dateOf;
					function dateOf(selector) {
						return moment($month_list.find(selector).attr('date'), 'YYYY-MM-DD');
					}

					function addMonthFor(date) {
						var s = scope.$new(true);
						var first = moment(date).startOf('month');
						var days_in_month = moment(first).endOf('month').date();
						var offset = first.day();

						createMonth(s, function ($li) {
                            options.render.caption($li.find('caption'), first);
							$li
								.attr('date', YM(date))
								.find('tbody>tr>td')
								.each(function (i) {
									var day = i - offset + 1;
									if (i < offset || day > days_in_month) {
										return;
									}

									var date = moment(first).date(day);
									var diff = date.diff(now, 'days');
									var $this = $(this)
										.attr('date', YMD(date))
									.addClass(!diff ? ' today' : diff < 0 ? ' past' : ' future')
										.toggleClass('disabled', !inRange(date));

									options.render.day($this, date);
								});

							$month_list.append($li);
						});
					}

					// If selected is specified, it will render for that date. If unknown,
					// we default to showing today's month- with nothing selected.
					if (!options.viewing) {
						options.viewing = moment(model() || moment()).startOf('month');
					}
					scope.$emit('ready', this);
				}],

				compile: function ($el) {

					$el.find('th').each(function (i, th) {
						th.innerText = $locale.DATETIME_FORMATS.SHORTDAY[i];
					});

					return function (scope, $el, attrs, controller) {
						$el.find('ol.months').on('click', 'td[date]:not(.disabled)', function (e) {
							scope.$apply(function () {
								controller.select(controller.dateOf(e.target).toDate());
							});
						});
						$el.find('.prev').click(function () {
							scope.$apply(scope.prev);
						});
						$el.find('.next').click(function (e) {
							e.preventDefault();
							scope.$apply(scope.next);
						});
					};
				}
			};
		}]);

})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haCalendar2Module', []);

	module.directive('haCalendar2', ['haDateUtils', '$timeout', '$rootScope', '$parse', 'haConfig', function (haDateUtils, $timeout, $rootScope, $parse, haConfig) {
		return {
			templateUrl: haConfig.getTemplateUrl('ha-calendar2.html'),
			restrict: 'A',
			link: function (scope, el, attrs) {

				// Initialize stuff
				var departDate;
				var returnDate;
				if (attrs.mode) {
					scope.mode = attrs.mode;
				}
				if (attrs.monthsBackward) {
					scope.monthsBackward = parseInt(attrs.monthsBackward, 10);
				}
				if (attrs.monthsForward) {
					scope.monthsForward = parseInt(attrs.monthsForward, 10);
				}
				if (attrs.monthsToShow) {
					scope.monthsToShow = parseInt(attrs.monthsToShow, 10);
				}
				if (attrs.dayTemplate) {
					scope.dayTemplate = attrs.dayTemplate;
				}
				if (attrs.departDate) {
					departDate = $parse(attrs.departDate)(scope);
				}
				if (attrs.returnDate) {
					returnDate = $parse(attrs.returnDate)(scope);
				}
				if (attrs.getIsUnavailable) {
					scope.getIsUnavailable = $parse(attrs.getIsUnavailable)(scope);
				}

				if (attrs.departDate) {
					scope.$watch(attrs.departDate, function (value) {
						departDate = value;
					});
				}

				if (attrs.returnDate) {
					scope.$watch(attrs.returnDate, function (value) {
						returnDate = value;
					});
				}

				scope.mode = scope.mode || 'single';
				scope.monthsBackward = scope.monthsBackward || 0;
				scope.monthsForward = (typeof scope.monthsForward !== 'undefined') ? scope.monthsForward : 11;
				scope.monthsToShow = scope.monthsToShow || 1;
				scope.dayTemplate = scope.dayTemplate || 'ha-datepicker-day-template';
				scope.getIsUnavailable = scope.getIsUnavailable || function () {
						return false;
					};

				// Override months back and forward if future or past disabled
				scope.pastDisabled = typeof scope.pastDisabled !== 'undefined';
				scope.futureDisabled = typeof scope.futureDisabled !== 'undefined';
				scope.monthsBackward = (scope.pastDisabled) ? 0 : scope.monthsBackward;
				scope.monthsForward = (scope.futureDisabled) ? 0 : scope.monthsForward;

				var zero = '0';
				var halfsLeft = (scope.monthsToShow === 2) ? '-50%' : '-100%';
				var $ol = $('#months', el);
				var transitionEndEvent = transitionEnd();
				var animating = false;
				var dateToJumpTo;
				var singleDatePicker = scope.mode === 'single';

				scope.months = haDateUtils.getVisibleMonths(scope.monthsBackward, scope.monthsForward);
				scope.weekdays = scope.weekdays || haDateUtils.getDaysOfWeek();

				scope.limitIndex = (scope.monthsBackward > 0) ? scope.monthsBackward + 1 : scope.monthsToShow;
				scope.limitTrailer = -1 * scope.monthsToShow;

				if (singleDatePicker) {
					dateToJumpTo = scope.inputModel;
				} else if (departDate) {
					dateToJumpTo = departDate;
				} else if (!departDate && returnDate) {
					dateToJumpTo = returnDate;
				}

				if (dateToJumpTo && angular.isDate(dateToJumpTo)) {
					scope.limitIndex += moment(dateToJumpTo).diff(moment().startOf('month'), 'months');
				}

				$ol.on(transitionEndEvent, onTransitionEnd);

				scope.next = function (increment) {

					increment = increment || 1;

					if (animating || scope.limitIndex === (scope.monthsBackward + scope.monthsForward + 1)) {
						return;
					}

					scope.$emit('calendarForward');

					scope.limitIndex += increment;
					scope.limitTrailer = (scope.mode === 'stack') ? scope.limitTrailer -= increment : -1 * (scope.monthsToShow + 1);

					if (scope.mode !== 'stack') {
						$ol.addClass('animating');
						$timeout(function () {
							animating = true;
							$ol.css('left', halfsLeft);

							if (!transitionEndEvent) {
								onTransitionEnd();
							}
						});
					}
				};

				scope.prev = function () {

					if (animating || scope.limitIndex === scope.monthsToShow) {
						return;
					}

					scope.$emit('calendarBackward');

					scope.limitTrailer = (scope.mode === 'stack') ? scope.limitIndex * -1 : -1 * (scope.monthsToShow + 1);

					if (scope.mode !== 'stack') {
						$ol.css('left', halfsLeft);
						$timeout(function () {
							animating = true;
							$ol.addClass('animating');
							$ol.css('left', zero);

							if (!transitionEndEvent) {
								onTransitionEnd();
							}
						});
					}
				};

				scope.$on('calendarGoForward', function(event, data) {
					scope.next(data)
				});
				scope.$on('calendarGoBackward', scope.prev);

				scope.setDate = function (e, data) {
					$rootScope.$broadcast('setDate', new Date(data.day), e.target);
				};

				scope.displayDay = function (d, f, l) {
					return isEdgeDay(d, f, l) ? '-' : d.getDate();
				};

				scope.isEdgeDay = function (d, f, l, idx) {
					return isEdgeDay(d, f, l, idx);
				};

				scope.isDepart = function (date) {
					return (singleDatePicker) ? haDateUtils.isSameDay(scope.inputModel, date) : (departDate) ? haDateUtils.isSameDay(departDate, date) : false;
				};

				scope.isReturn = function (date) {
					return (singleDatePicker) ? haDateUtils.isSameDay(scope.inputModel, date) : (returnDate) ? haDateUtils.isSameDay(returnDate, date) : false;
				};

				scope.isSelected = function (date) {
					return !!departDate && !!returnDate && haDateUtils.isBetween(date, departDate, returnDate);
				};

				scope.isUnavailable = function (date) {
					return scope.getIsUnavailable(date) || pastFutureDisallowed(date);
				};

				scope.dayPlus = function (date, duration) {
					return (angular.isDate(date)) ? (new Date(+date)).dateAdd('day', duration) : new Date();
				};

				function pastFutureDisallowed(date) {
					var today = new Date();
					if (scope.pastDisabled || scope.futureDisabled) {
						return (scope.pastDisabled && (haDateUtils.isBefore(date, today) && !haDateUtils.isSameDay(date, today))) || (scope.futureDisabled && haDateUtils.isAfter(date, today));
					}
					return false;
				}

				function onTransitionEnd() {
					animating = false;
					$ol.removeClass('animating');

					scope.$apply(function () {
						scope.limitTrailer = -1 * scope.monthsToShow;
						if ($ol.css('left') === '0px') {
							scope.limitIndex--;
						} else {
							$ol.css('left', zero);
						}
					});
				}

				function transitionEnd() {
					var el = document.createElement('div');

					var transEndEventNames = {
						transition: 'transitionend',
						WebkitTransition: 'webkitTransitionEnd',
						MozTransition: 'transitionend',
						OTransition: 'oTransitionEnd otransitionend'
					};

					for (var name in transEndEventNames) {
						if (el.style[name] !== undefined) {
							return transEndEventNames[name];
						}
					}

					return false;
				}

				function isEdgeDay(d, f, l, idx) {
					return (f && d.getDate() > 7) || ((idx >= 4) && (d.getDate() >= 1 && d.getDate() < 15)); // TODO: Memoize
				}
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Hawaiian Carousel Directive
	// ===========================
	//
	// * **Class:** haCarousel
	// * **Author:** George Pantazis
	//
	// A directive for applying carousel behaviors to DOM wrappers.

	'use strict';

	var module = angular.module('haCarouselModule', []);

	module.directive('haCarousel', ['$timeout', '$compile', 'haUtils', 'haConfig', '$rootScope', function ($timeout, $compile, haUtils, haConfig, $rootScope) {

		return {
			restrict: 'A',
			scope: true,
			priority: 20,
			transclude: true,
			templateUrl: haConfig.getTemplateUrl('ha-carousel-template.html'),
			link: function ($scope, $el, $attr, $ctrl, $transcludeFn) {

				// options
				var onlyPage = $scope.$eval($attr.onlyPage);
				var noTransition = $scope.$eval($attr.noTransition);
				var allowWrap = ($attr.allowWrap) ? $scope.$eval($attr.allowWrap) : true;
				var mobileSlide = $scope.$eval($attr.mobileSlide);
				var isMobile = $rootScope.isMobile;

				// pagination types: "dots, paddled-dots, overlay-dots, overlay-paddled-dots"
				var paginationType = $attr.paginationType;
				var largePaddles = $scope.$eval($attr.largePaddles);
				var paginationAriaHidden = ($attr.paginationAriaHidden) ? $attr.paginationAriaHidden : false;

				// Custom transclusion to keep template scope
				$el.find('.transclude').append($transcludeFn($scope, function () {
				}));

				// If carousel has a draggable directive, restrict dragging to x-axis,
				// bind dragend to this directive's methods.
				$el.find('[ha-draggable]').each(function () {
					$(this).scope().updateDraggableSettings({
						distance: 40,
						axis: 'x'
					});

					$(this).scope().$on('$dragEnd', function (event, e, ui) {
						if (onlyPage) {
							$scope.gotoNextSlideInDirection(ui.position.left - ui.originalPosition.left);
						}
						$scope.gotoNearestSlide();
					});
				});

				$scope.slideWrapper = $el.find('.ha-carousel-slide-wrapper').first();

				// initialize with slides timeout to wait for ng-repeat linking
				$timeout(function () {
					$scope.slides = $scope.slideWrapper.children('.ha-carousel-slide');
					$scope.currentSlide = 0;
					$scope.paginationType = paginationType;
					$scope.largePaddles = largePaddles;
					$scope.paginationAriaHidden = paginationAriaHidden;
					$scope.allowWrap = allowWrap;
					$scope.slideCount = $scope.slides.length;
					$scope.slideWrapperWidth = ($scope.slideCount * 100) + '%';
					$scope.slideWidth = (100 / $scope.slideCount) + '%';
				});


				// Scope Methods
				// -------------

				// It goes to a given slide in the carousel.
				$scope.gotoSlide = function (which, speed, cb) {
					$timeout(function () {
						var targetSlide = $scope.slides[which];
						var slideRect;
						var viewportRect;

						if ($scope.isAnimating && targetSlide || !targetSlide) {
							return;
						}

						$scope.isAnimating = true;

						if (typeof speed === 'undefined') {
							speed = 350;
						}

						slideRect = targetSlide.getBoundingClientRect();
						viewportRect = $el.get(0).getBoundingClientRect();

						var afterChange = function () {
							$scope.isAnimating = false;
							if (speed > 0) {
								$el.trigger('haCarouselAtSlide', which);
							}
							if (cb) {
								cb();
							}
						};
						var animateTransition = function () {
							$scope.slideWrapper.animate({
								'left': (slideRect.right - slideRect.left) / (viewportRect.right - viewportRect.left) * -100 * which + '%'
							}, speed, afterChange);
						};
						if (noTransition) {
							if (!mobileSlide || (mobileSlide && !isMobile)) {
								$scope.slideWrapper.css('left', (slideRect.right - slideRect.left) /
									(viewportRect.right - viewportRect.left) * -100 * which + '%');
								afterChange();
							} else {
								animateTransition();
							}
						}
						else {
							animateTransition();
						}


						$scope.currentSlide = which;
					}, 0);

					return $scope;
				};

				// It moves the carousel to the next slide.
				$scope.gotoNextSlide = function () {
					var $slides = this.slideWrapper.children('.ha-carousel-slide');
					var whichSlide = !allowWrap || this.currentSlide + 1 < $slides.length ? this.currentSlide + 1 : 0;
					this.gotoSlide(whichSlide);
					return this;
				};

				// It moves the carousel to the previous slide.
				$scope.gotoPreviousSlide = function () {
					var $slides = this.slideWrapper.children('.ha-carousel-slide');
					var whichSlide = !allowWrap || this.currentSlide - 1 >= 0 ?
					this.currentSlide - 1 : $slides.length - 1;
					this.gotoSlide(whichSlide);
					return this;
				};

				$scope.gotoNextSlideInDirection = function (offsetMade, offsetNeeded) {
					var $slides = this.slideWrapper.children('.ha-carousel-slide');
					if ($slides.length === 0) {
						return;
					}
					offsetNeeded = offsetNeeded || $($slides[0]).width() / 4;
					if (offsetMade > 0 && offsetMade > offsetNeeded &&
						this.currentSlide > 0) {
						this.gotoPreviousSlide();
					} else if (offsetMade < 0 && offsetMade < -offsetNeeded &&
						this.currentSlide < $slides.length - 1) {
						this.gotoNextSlide();
					} else {
						this.gotoSlide(this.currentSlide);
					}
					this.$apply();

					return this;
				};

				// It moves the carousel to the nearest slide.
				$scope.gotoNearestSlide = function () {
					var $slides = this.slideWrapper.children('.ha-carousel-slide');
					var slidePositions = haUtils.getChildrenPositions($el, $slides);
					var closestPosition = haUtils.closestInArray(slidePositions, 0);

					this.gotoSlide(slidePositions.indexOf(closestPosition));
					this.$apply();

					return this;
				};

				// Grab the alt text from the slide content
				$scope.getAltText = function () {
					var altText = '';
					var image = $(this.slide).children('.main-image');
					if (image.length > 0) {
						altText = image[0].dataset.alt;
					}
					return altText;
				};

				// ### End: Scope Methods

				// Initialization
				// --------------
				$scope.currentSlide = 0;
				$scope.$emit('$haCarouselReady');
			}
		};
	}]);

	module.directive('haCarouselPaddles', ['haConfig', function (haConfig) {
		return {
			restrict: 'A',
			templateUrl: haConfig.getTemplateUrl('ha-carousel-paddles-template.html')
		};
	}]);

	module.directive('haCarouselPagination', ['haConfig', function (haConfig) {
		return {
			restrict: 'A',
			templateUrl: haConfig.getTemplateUrl('ha-carousel-pagination-template.html')
		};
	}]);

	module.directive('haCarouselPaginationWithPaddles', ['haConfig', function (haConfig) {
		return {
			restrict: 'A',
			templateUrl: haConfig.getTemplateUrl('ha-carousel-pagination-with-paddles-template.html')
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Passenger Count Directive
	// --------------------------------------------
	//
	// * **Class:** HaPassengerCount
	// * **Author:** Steven Tate
	//
	// ensures validity of 2 passenger type dropdowns

	'use strict';

	var mod = angular.module('haPassengerCountModule', []);

	mod.directive('haPassengerCount', [function () {

		return {
			restrict: 'A',
			require: 'ngModel',
			scope: {otherDropdownCount: '='},
			link: function ($scope, $el, $attrs, ngModel) {

				var validate = function (viewValue) {

					var otherDropdownCount = typeof $scope.otherDropdownCount === 'undefined' ? 0 : parseInt($scope.otherDropdownCount, 10);
					var paxCount = parseInt(viewValue, 10) + otherDropdownCount;
					var isValid = paxCount > 0 && paxCount < 8;

					ngModel.$setValidity('haPassengerCount', isValid);

					return viewValue;
				};

				ngModel.$parsers.unshift(validate);
				ngModel.$formatters.push(validate);

				$scope.$watch('otherDropdownCount', function () {
					return validate(ngModel.$viewValue);
				});
			}
		};

	}]);

})(angular);
;
(function (angular) {

	// Hawaiian Carousel Directive
	// ===========================
	//
	// * **Class:** haThumbGallery
	// * **Author:** Jake Albaugh
	//
	// A directive for a simple Thumbnail-driven Gallery

	'use strict';

	var module = angular.module('haThumbGalleryModule', []);

	module.directive('haThumbGallery', ['$log', 'haConfig', function ($log, haConfig) {

		return {
			restrict: 'A',
			scope: {
				images: '=',
				fallbackImage: '='
			},
			priority: 20,
			templateUrl: haConfig.getTemplateUrl('ha-thumb-gallery-template.html'),
			controller: ['$scope', '$rootScope', function ($scope, $rootScope) {

				//Listen for IMG load errors...
				$scope.$on('haEmitError', function ($event, e) {
					$event.stopPropagation();

					// $log.debug('<< haEmitError', e);

					var target = e.target;
					if (target != null && target.tagName === 'IMG') {
						var i = $scope.images.indexOf(target.src);
						$scope.images.splice(i, 1);
						$scope.updateImage($scope.images[0], 0);

						// If it is safe to do so, perform a scope-local digest
						if (!$rootScope.$$phase) {
							// var then;
							// if (performance != null) {
							//   then = performance.now();
							// }
							$scope.$digest();
							// if (performance != null) {
							//     $log.debug('$scope.$digest():',
							//         Math.round(performance.now()-then)+'ms');
							// }
						}
					}
				});
				// Scope Methods
				// -------------

				// It updates the main image and toggles state on thumbnail
				$scope.updateImage = function (which, index) {
					$scope.displayImage = which;
					$scope.currentImageIndex = index;
				};

				// It moves the carousel to the next slide.
				$scope.gotoNextSlide = function ($event) {
					if ($event != null) {
						$event.preventDefault();
					}

					var index = $scope.currentImageIndex + 1 < $scope.images.length ?
					$scope.currentImageIndex + 1 : 0;

					$scope.updateImage($scope.images[index], index);
				};

				// It moves the carousel to the previous slide.
				$scope.gotoPreviousSlide = function ($event) {
					if ($event != null) {
						$event.preventDefault();
					}

					var index = $scope.currentImageIndex - 1 >= 0 ?
					$scope.currentImageIndex - 1 : $scope.images.length - 1;

					$scope.updateImage($scope.images[index], index);
				};

				// ### End: Scope Methods

				// initial image
				$scope.updateImage($scope.images[0], 0);
			}]
		};
	}]);

})(angular);
;
(function (ng) {

	// Price Chart Carousel Direvtive
	// --------------------------------------------
	//
	// * **Class:** haPriceChart
	// * **Author:** Michael Toymil, Jake Albaugh
	//
	// A 25 day calendar carousel to compare ticket fares by date.

	'use strict';

	var module;
	try {
		module = ng.module('haLowFareSearchModule');
	} catch (e) {
		module = ng.module('haLowFareSearchModule', ['haPriceApiModule', 'ngAnimate', 'ngTouch']);
	}

	// Flag to keep track of animation status
	var animating = false;

	module.directive('haPriceChart', ['haPriceApiService', '$animate', 'haConfig', function (priceApi, $animate, haConfig) {
		var CEILING_FACTOR = 0.33;             // Proportion of max price to pad the top of the chart with
		var MAX_DAYS = 331;                   // How far into the future we can scroll
		var VISIBLE_DAYS_COUNT = 25;          // How many days are shown at a time.
		var PADDING_DAYS_COUNT = 15;          // How many days should we pad the visible range by on either side
		var MOVE_TIMEOUT_MILLESECONDS = 1000; // How long to wait before assuming the user is done moving.
		var moveTimeout;                      // Holds the timeout object
		var searchURL = '/Book/home?searchDetails=';

		var controller = function ($scope) {
			$scope.minPrice = Number.POSITIVE_INFINITY;
			$scope.maxPrice = Number.NEGATIVE_INFINITY;

			function resetDates() {
				$scope.selectedPrice = {
					date: new Date($scope.tripData.departureDate),
					price: 0
				};

				$scope.currentDate = new Date($scope.selectedPrice.date);
				$scope.currentDate.setDate($scope.currentDate.getDate() - parseInt(VISIBLE_DAYS_COUNT / 2));
			}

			resetDates();

			$scope.buffer = {};    // The days we've loaded so far
			$scope.visibleMonths = [];
			$scope.endOfMonthIndex = undefined;

			$scope.moving = false;

			// NOTE: resultsArray: [{Price: <number>, DepartDate: <String> }]
			function addResultsToBuffer(resultsArray) {
				for (var i = 0; i < resultsArray.length; i++) {
					var result = resultsArray[i];
					var date = new Date(result.DepartDate);
					if (date.getTime() === $scope.selectedPrice.date.getTime()) {
						$scope.selectedPrice.price = result.Price;
					}
					$scope.buffer[date.getTime()] = {
						price: result.Price,
						originalDate: result.date
					};
				}
			}

			// Repopulate array containing visible results
			function refreshVisible() {
				var newVisible = [];

				// Reset min/max
				$scope.minPrice = Number.POSITIVE_INFINITY;
				$scope.maxPrice = Number.NEGATIVE_INFINITY;

				for (var i = 0; i < VISIBLE_DAYS_COUNT; i++) {
					var tempDate = new Date($scope.currentDate);
					tempDate.setDate($scope.currentDate.getDate() + i);

					var returnDate = new Date(tempDate);
					returnDate.setDate(tempDate.getDate() + parseInt($scope.tripData.tripLength));
					// Create an object for every visible day slot
					var dayObject = {
						date: tempDate,
						returnDate: returnDate
					};
					// If we have buffer data for that day, use it
					if ($scope.buffer[tempDate.valueOf()]) {
						var price = $scope.buffer[tempDate.valueOf()].price;
						if (price) {
							// Calculate min/max price in this pass
							$scope.minPrice = Math.min($scope.minPrice, price);
							$scope.maxPrice = Math.max($scope.maxPrice, price);
						}
						dayObject.price = price;
					}

					newVisible.push(dayObject);
				}

				// Include the selected price in the calculation, for proper scaling
				$scope.minPrice = Math.min($scope.minPrice, $scope.selectedPrice.price);
				$scope.maxPrice = Math.max($scope.maxPrice, $scope.selectedPrice.price);

				// Raise the ceiling for the max price in order to keep some padding at the top of the chart.
				$scope.maxPrice += Math.round($scope.maxPrice * CEILING_FACTOR);

				$scope.visible = newVisible;
				calculatePriceRatios();
				window.setTimeout(function () {
					$animate.enabled(true);
				}, 0);
			}

			// Caluclate and set the price ratios for the results
			function calculatePriceRatios() {
				for (var i = 0; i < $scope.visible.length; i++) {
					if ($scope.visible[i].price) {
						if ($scope.visible[i].price === $scope.minPrice && $scope.visible[i].price === $scope.maxPrice) {
							$scope.visible[i].ratio = 1;
						} else {
							$scope.visible[i].ratio = $scope.visible[i].price / $scope.maxPrice;
						}
					}
				}

				$scope.selectedPrice.ratio = Math.min($scope.selectedPrice.price / $scope.maxPrice, 1);
			}

			// Using the currentDate, determine what we need to query the API for, if at all.
			function findBufferGap(direction) {
				var gap = {
					startDate: undefined,
					days: undefined
				};
				var inGap = false;
				for (var i = -PADDING_DAYS_COUNT; i < VISIBLE_DAYS_COUNT + PADDING_DAYS_COUNT; i++) {
					var tempDate = new Date($scope.currentDate);
					if (direction < 0) {
						tempDate.setDate(tempDate.getDate() + VISIBLE_DAYS_COUNT);
					}
					tempDate.setDate(tempDate.getDate() + (i * direction));

					if (!$scope.buffer[tempDate.valueOf()]) {
						if (!inGap) {
							if (gap.startDate) {
								// We've found a second gap in the buffer, call it quits and query the entire interval
								gap.startDate = new Date($scope.currentDate);
								gap.startDate.setDate($scope.currentDate.getDate() - PADDING_DAYS_COUNT);
								gap.days = VISIBLE_DAYS_COUNT + 2 * PADDING_DAYS_COUNT;
								break;
							} else {
								// This is our first gap, save the startDate
								gap.startDate = tempDate;
								gap.days = 1;
								inGap = true;
							}
						} else {
							gap.days++;
						}
					} else {
						if (inGap) {
							inGap = false;
						}
					}
				}

				return gap;
			}


			function moveCallback(direction) {
				// Determine what to query the API for
				var gap = findBufferGap(direction);
				$scope.moving = false;
				if (gap.startDate) {
					$scope.loading = true;
					priceApi.fetchPrices(formatDateForQuery(gap.startDate), direction).success(function (response) {
						if (response.IsSuccess) {
							addResultsToBuffer(response.LowFareTripResponseList);
							refreshVisible();
						}
						$scope.loading = false;
					});
				}
				$scope.$apply();
			}

			// Calculate how many days for each visible month should be shown
			function updateMonths() {
				$scope.preventLeft = false;
				$scope.preventRight = false;
				$scope.endOfMonthIndex = null;
				var visibleMonths = [];
				var tempDate = $scope.visible[0].date;
				var month = {
					month: tempDate.getMonth(),
					year: tempDate.getFullYear(),
					days: 1
				};
				for (var i = 1; i < $scope.visible.length; i++) {
					tempDate = $scope.visible[i].date;
					if (tempDate.getMonth() !== month.month) {
						visibleMonths.push(month);
						$scope.endOfMonthIndex = i - 1;
						month = {
							month: tempDate.getMonth(),
							year: tempDate.getFullYear(),
							days: 1
						};
					} else {
						month.days++;
					}

					if (tempDate <= Date.now()) {
						$scope.preventLeft = true;
					}
					if (daysBetween(new Date(), tempDate) >= MAX_DAYS) {
						$scope.preventRight = true;
					}
				}
				visibleMonths.push(month);
				$scope.visibleMonths = visibleMonths;
			}

			// Move a specified number of days
			$scope.move = function (days) {
				if (animating) {
					return;
				}
				$scope.moving = true;
				animating = true;
				$scope.currentDate.setDate($scope.currentDate.getDate() + days);
				var direction = days / Math.abs(days); // 1 or -1
				window.clearTimeout(moveTimeout);
				moveTimeout = window.setTimeout(function () {
					moveCallback(direction);
				}, MOVE_TIMEOUT_MILLESECONDS);
				refreshVisible();
				updateMonths();
			};

			var monthKeys = ['januarytext', 'februarytext', 'marchtext', 'apriltext', 'maytext', 'junetext', 'julytext', 'augusttext', 'septembertext', 'octobertext', 'novembertext', 'decembertext'];
			$scope.monthFilter = function (value) {
				return $scope.sitecoreStrings[monthKeys[value]];
			};

			$scope.barClicked = function (result) {

				var queryString = [
					'o=' + $scope.tripData.origin,
					'd=' + $scope.tripData.destination,
					'dd=' + formatDateForQuery(result.date),
					'a=' + $scope.tripData.adultCount,
					'c=' + $scope.tripData.childCount,
					'rd=' + formatDateForQuery(result.returnDate),
					'tt=' + $scope.tripData.tripType
				];

				window.location.href = searchURL + queryString.join(';');

			};

			function formatDateForQuery(date) {
				var s = '';
				s += ('0' + (date.getMonth() + 1)).slice(-2) + '/';
				s += ('0' + date.getDate()).slice(-2) + '/';
				s += date.getFullYear();
				return s;
			}

			function daysBetween(date1, date2) {
				//Get 1 day in milliseconds
				var one_day = 1000 * 60 * 60 * 24;

				// Convert both dates to milliseconds
				var date1_ms = date1.getTime();
				var date2_ms = date2.getTime();

				// Calculate the difference in milliseconds
				var difference_ms = date2_ms - date1_ms;

				// Convert back to days and return
				return Math.round(difference_ms / one_day);
			}

			$scope.$watch('results', function (nv) {
				if (nv) {
					$animate.enabled(false);
					$scope.buffer = {};
					resetDates();
					addResultsToBuffer(nv);
					refreshVisible();
					updateMonths();
				}
			});
		};

		controller.$inject = ['$scope'];

		return {
			restrict: 'A',
			scope: {
				tripData: '=',
				sitecoreStrings: '=',
				sitecoreStringsLoaded: '=',
				results: '=',
				loading: '=',
				error: '='
			},
			controller: controller,
			templateUrl: haConfig.getTemplateUrl('ha-price-chart-base-template.html')
		};
	}]);


	// Animations
	module.animation('.month-animation', function () {
		return {
			enter: function (element, done) {
				element.css({'opacity': 0});
				element.delay(500).animate({'opacity': 1}, {
					complete: done
				});
			}
		};
	});
	module.animation('.price-item-animation', function () {
		return {
			enter: function (element, done) {
				if (element.hasClass('first-item')) {
					// The very first element
					var oldMargin = element.css('margin-left');
					element.css({
						'margin-left': 0
					});
					element.animate({
						'margin-left': oldMargin
					}, {
						easing: 'linear',
						duration: 500,
						complete: done
					});
					return function () {
						element.css({
							'margin-left': ''
						});
						animating = false;
					};
				} else {
					element.animate({
						'background-color': 'transparent'
					}, {
						duration: 500,
						complete: done
					});
				}
			},
			leave: function (element, done) {
				element.animate({
					'margin-left': 0
				}, {
					easing: 'linear',
					duration: 500,
					complete: done
				});

				if (element.hasClass('first-item')) {
					var newFirst = element.nextAll('.first-item').first();
					newFirst.css({
						'margin-left': 0
					});
					return function () {
						newFirst.css({
							'margin-left': ''
						});
						animating = false;
					};
				}
			}
		};

	});

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haDatepickerModule', []);

	module.directive('haDatepicker2', ['$rootScope', 'haTemplateCache', 'haConfig', '$filter', '$locale', '$compile', function ($rootScope, haTemplateCache, haConfig, $filter, $locale, $compile) {

		return {
			require: 'ngModel',
			restrict: 'A',
			scope: true,

			link: function (scope, $el, attrs, ctrl) {
				var config = $el.attr('autocomplete', 'off').attr('ha-datepicker2');
				var options = scope.$eval(config) || {};
				options.format = options.format || 'MM/DD/YYYY'; //$locale.DATETIME_FORMATS.shortDate.toUpperCase();//this is used by moment, so we need to uppercase it
				options.display = options.display || ('EEEE, ' + $locale.DATETIME_FORMATS.shortDate);

				var $parent_form = $($el[0].form);
				var cal_scope = scope.$new();
				var template = '<div ha-calendar="' + config + '" ha-calendar-model="' + attrs.ngModel + '"></div>';
				var $cal = $compile(template)(cal_scope);
				var $datepicker = $('<div class="datepicker">').click(focus).insertAfter($el).append($cal);

				// ngModel requires a name- but if we are posting the form, we don't want one. (Because the date's format is not ISO8610)
				// so, as a work around, we can use a name with an underscore and then remove it.
				if ($el[0].name === '_') {
					$el.attr('name', null);
				}

				if ($rootScope.isMobile) {
					$el.attr('readonly','readonly');
				}

				var closer, down;
				$el.on('focus', function () {
					$datepicker.css('top', $el.position().top + $el.outerHeight() + 'px');
					$datepicker.toggle(true);
					$el.addClass('open');
					clearTimeout(closer);
					$el.val(ctrl.$viewValue || '').select();
				});

				$el.on('blur', function () {
					if (down) {
						return;
					}
					closer = setTimeout(function () {
						$datepicker.toggle(false);
						$el.removeClass('open');
						//do not format_pretty if invalid
						if (ctrl.$valid) {
							$el.val(ctrl.$modelValue ? format_pretty(ctrl.$modelValue) : '');
						}
					}, 200);
				});

				function mouseup() {
					down = false;
					$(document.body).off('mouseup blur', mouseup);
				}

				$datepicker.on('mousedown', function () {
					down = true;
					$(document.body).on('mouseup blur', mouseup);
				});

				function focus() {
					// On the mobile version we can't have the screen scrolling around with focus
					if ($rootScope.isMobile) {
						$el.select();
						return;
					}
					if (document.activeElement !== $el[0]) {
						$el.focus().select();
					}
				}

				scope.$on('prev', focus);
				scope.$on('next', focus);

				// Calendar clicked
				scope.$on('select', function () {
					if (options.next === false) {
						return;
					}
					setTimeout(function () {
						if (options.next) {
							return $(options.next).focus();
						}

						//auto advance to the next input
						var $inputs = $parent_form.find(':input:visible:enabled:not(button.prev,button.next)');
						var $nextInput = $inputs.eq($inputs.index($el) + 1);

						// on mobile, we want blur manually
						if ($rootScope.isMobile) {
							$el.blur();
						}
						$nextInput.focus();
					}, 10);
				});

				function inRange(date) {
					return date.valueOf() >= options.range_start && date.valueOf() <= options.range_end;
				}

				ctrl.$parsers.push(function (value) {
					// check if value is in the MM/DD/YYYY format, if not, try to convert it
					if (value && typeof value === 'string' && !value.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
						var pieces = value.match(/\d+/g);
						for (var i = 0; i < 2 && i < pieces.length; i++) {
							if (pieces[i].length === 1) {
								pieces[i] = '0' + pieces[i];
							}
						}
						value = pieces.join('/');
					}

					var date = value instanceof Date ? moment(value) : moment(value, options.format, true);
					var valid = !value || (date.isValid() && inRange(date));
					ctrl.$setValidity('date', valid);

					return valid && value ? date.toDate() : undefined;
				});

				ctrl.$formatters.push(function (value) {
					var valid = !value || (!invalidDate(value) && inRange(value));
					ctrl.$setValidity('date', valid);

					if (valid && value && !$el.is(':focus')) {
						setTimeout(function () {
							$el.val(format_pretty(value));
						}, 50);
					}
					return valid && value ? moment(value).format(options.format) : '';
				});

				function format_pretty(value) {
					return invalidDate(value) ? '' : $filter('date')(value, options.display);
				}

				function invalidDate(date) {
					return !date || (!moment.isMoment(date) && !(date instanceof Date)) || isNaN(date);
				}
			}
		};
	}]);
	module.directive('haRangeDatepicker', ['$parse', 'haSitecoreStrings', function ($parse, $scs) {
		return {
			restrict: 'A',
			link: {
				pre: function (scope, $el, attrs) {
					var departdateval = $scs('BookingWidget.departdate');
					var returndateval = $scs('BookingWidget.returndate');
					if ($el.context.textContent && $el.context.textContent.length > 0) {
						departdateval = $el.context.textContent.split('\n')[4].replace(',', '');
						returndateval = $el.context.textContent.split('\n')[13].replace(',', '');
					}
					
					var options = $.extend(true, {
						calendars: 2,
						viewing: moment().startOf('month'),
						namespace: '',
						format: 'MM/DD/YYYY', //$scs('Forms.DateFormat').replace(/\(|\)/g,'').toUpperCase(),
						range_start: moment().startOf('day'),
						range_end: moment().add(331, 'days'),
						depart_cal_title: departdateval,
						return_cal_title: returndateval,
						rangeRender: function ($cal) {
							$cal.find('td[date]').removeClass('start end between').each(function () {
								var $td = $(this);
								var date = moment($td.attr('date'), 'YYYY-MM-DD');
								var type = getClass(date);
								if (type) {
									$td.addClass(type);
								}
							});
						}
					}, scope.$eval(attrs.haRangeDatepicker));

					function clone_config(isReturn) {
						return $.extend({}, options, {
							range_start: moment(options.range_start),
							range_end: moment(options.range_end),
							calendarHeading: (isReturn) ? options.return_cal_title : options.depart_cal_title,
							isReturn: isReturn
						});
					}

					scope[options.namespace + '_configs'] = {
						start: clone_config(false),
						end: clone_config(true)
					};

					function prop(expression) {
						var getter = $parse(expression);
						return {
							get: getter.bind(getter, scope),
							set: getter.assign.bind(getter, scope)
						};
					}

					var start_model = $el.find(options.start).attr('ha-datepicker2', options.namespace + '_configs.start').attr('ng-model');
					var end_model = $el.find(options.end).attr('ha-datepicker2', options.namespace + '_configs.end').attr('ng-model');

					var model = {
						start: prop(start_model),
						end: prop(end_model)
					};

					scope.$watch(start_model, function (start_value) {
						if (start_value) {
							start_value = moment(start_value);
							var end = model.end.get();
							if (end && start_value.isAfter(end)) {
								model.end.set(undefined);
							}

							/* as date changes, preserve calendar view based on chosen month/day, but don't
							shift calendar position if a date on the right side of the panel is selected */
							var startMonth = start_value.month();
							if (start_value.year() > options.viewing.year()) {
								startMonth += 12;
							}

							if (startMonth > options.viewing.month()) {
								options.viewing.year(start_value.year()).month(start_value.month() - 1);
							} else {
								options.viewing.year(start_value.year()).month(start_value.month());
							}
						}
						options.viewing.add(1, 'ms');//hack to make it re-render
					});
					scope.$watch(end_model, function (end_value) {
						if (end_value) {
							end_value = moment(end_value);
							var start = model.start.get();
							if (start && end_value.isBefore(start)) {
								model.start.set(undefined);
							}
						}
						options.viewing.add(1, 'ms');//hack to make it re-render
					});

					function getClass(date) {
						var end = model.end.get();
						var start = model.start.get();
						if (end && start && scope.tripType === 2 && (date.isBefore(end) && date.isAfter(start))) {
							return 'between';
						}
						var isDepart = start && date.isSame(start);
						var isReturn = end && date.isSame(end);
						if (isDepart && isReturn) {
							return;
						}//no classes. The `selected` class will provide styling
						if (isDepart) {
							return 'start';
						}
						if (isReturn && scope.tripType === 2) {
							return 'end';
						}
					}
				}
			}
		};
	}]);


	//THIS IS DEPRECATED!
	module.directive('haDatepicker', ['haTemplateCache', 'haConfig', function (haTemplateCache, haConfig) {

		haTemplateCache.get(haConfig.getTemplateUrl('ha-calendar2.html'));
		haTemplateCache.get(haConfig.getTemplateUrl('ha-booking-day-template.html'));
		haTemplateCache.get(haConfig.getTemplateUrl('ha-datepicker-day-template.html'));

		return {
			templateUrl: haConfig.getTemplateUrl('ha-datepicker.html'),
			restrict: 'A',
			scope: {
				label: '@',
				eyebrow: '@',
				placeholder: '@',
				name: '@',
				inputModel: '=',
				monthsBackward: '=',
				monthsForward: '=',
				monthsToShow: '=',
				dayTemplate: '@',
				errorMessage: '@',
				pastDisabled: '@',
				futureDisabled: '@'
			},
			link: function (scope, elem, attrs) {
				scope.calendarOpen = false;
				scope.monthsToShow = scope.monthsToShow || 1;
				scope.pastDisabled = attrs.hasOwnProperty('pastDisabled');
				scope.futureDisabled = attrs.hasOwnProperty('futureDisabled');
				scope.dayTemplate = scope.dayTemplate || 'ha-datepicker-day-template.html';
				scope.required = false;

				attrs.$observe('required', function () {
					if (attrs.hasOwnProperty('required')) {
						scope.required = true;
					}
				});
				//scope.ngRequired = scope.ngRequired || attrs.hasOwnProperty('required');

				// Date Input focused
				scope.$on('dateInputFocused', function () {
					scope.calendarOpen = true;
					wireCalendarCloseEvent();
				});

				// Calendar clicked
				scope.$on('setDate', function (e, date) {
					if (scope.calendarOpen) {
						scope.inputModel = date || '';
						scope.$apply();
					}
				});

				scope.$watch('inputModel', function () {
					scope.calendarOpen = false;
				});

				attrs.$observe('eyebrow', function (newVal, oldVal) {
					if (newVal !== oldVal) {
						scope.eyebrow = newVal;
					}
				});

				attrs.$observe('label', function (newVal, oldVal) {
					if (newVal !== oldVal) {
						scope.label = newVal;
					}
				});

				attrs.$observe('placeholder', function (newVal, oldVal) {
					if (newVal !== oldVal) {
						scope.placeholder = newVal;
					}
				});

				function wireCalendarCloseEvent() {
					$('body').off('click.closeCalendar');
					$('body').on('click.closeCalendar', function (e) {
						var $targ = $(e.target);

						if (!scope.calendarOpen || (scope.calendarOpen && $targ.closest('[ha-datepicker]').length)) {
							return;
						}

						if ($targ.closest('.calendarPopupWrap').length > 0 || $targ.is('.calendarPopupWrap')) {
							return;
						}

						$('body').off('click.closeCalendar');
						scope.currentDateChoice = '';
						scope.calendarOpen = false;
						scope.$digest();
					});
				}
			}
		};
	}]);

	//THIS IS DEPRECATED!
	// DATE INPUT FIELDS
	module.directive('haDatepickerInput', ['haTemplateCache', 'haConfig', function (haTemplateCache, haConfig) {

		haTemplateCache.get(haConfig.getTemplateUrl('ha-calendar2.html'));
		haTemplateCache.get(haConfig.getTemplateUrl('ha-booking-day-template.html'));

		return {
			templateUrl: haConfig.getTemplateUrl('ha-datepicker-input.html'),
			scope: {
				inputModel: '=',
				calendarOpen: '=',
				currentDateChoice: '=',
				inputName: '@name',
				label: '@',
				eyebrow: '@',
				placeholder: '@',
				disabled: '=',
				ngRequired: '=',
				errorMessage: '='
			},
			link: function (scope, element, attrs) {

				scope.onFocus = function () {
					scope.$emit('dateInputFocused', element);
				};

				scope.isActive = function () {
					return scope.calendarOpen;
				};

				attrs.$observe('eyebrow', function (newVal, oldVal) {
					if (newVal !== oldVal) {
						scope.eyebrow = newVal;
					}
				});

				attrs.$observe('label', function (newVal, oldVal) {
					if (newVal !== oldVal) {
						scope.label = newVal;
					}
				});

				attrs.$observe('placeholder', function (newVal, oldVal) {
					if (newVal !== oldVal) {
						scope.placeholder = newVal;
					}
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Travel Goal Directive
	// ===============================
	//
	// * **Class:** HaTravelGoal
	// * **Author:** Jamie Perkins
	//
	// Allows user to set up and see progress on travel goals

	var module = angular.module('haTravelGoalModule', []);

	module.directive('haTravelGoal', ['haHttpService', '$filter', '$window', 'haConfig', '$document', 'haCitiesSvc', '$log', 'haSitecoreStrings', '$timeout',
		function (haHttpService, $filter, $window, haConfig, $document, haCitiesSvc, $log, $scs, $timeout) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-travel-goal.html'),
				restrict: 'A',
				scope: {},
				link: function ($scope) {

					var travelGoalEndpoint = '/myaccount/travelgoals/GetDefaultGoal';
					var saveTravelGoalEndpoint = '/myaccount/travelgoals/SaveTravelGoal';
					$scope.noModelError = false;
					$scope.currencyCode = 'USD';
					$scope.error = '';
					$scope.getImgUrl = haConfig.getImgUrl;

					$scope.indexes = {
						tripTypeIndex: 0,
						last_tripTypeIndex: 0,
						cabinTypeIndex: 0,
						last_cabinTypeIndex: 0,
						paxCountIndex: 0,
						last_paxCountIndex: 0
					};

					function grayOutPins() {
						$timeout(function() {
							if (document.getElementById('travel-goal-plane')) {
								document.getElementById('travel-goal-plane').style.display = 'none';
							}
							if (document.getElementById('leftPin')) {
								document.getElementById('leftPin').setAttribute('fill', '#6F6F6F');
								document.getElementById('rightPin').setAttribute('fill', '#6F6F6F');
								document.getElementById('leftPinMobile').setAttribute('fill', '#6F6F6F');
								document.getElementById('rightPinMobile').setAttribute('fill', '#6F6F6F');
								// hide and show flight progress bc of IE bug
								document.getElementById('flight-progress').setAttribute('stroke', 'none');
							}
						});
					}
					function colorizePins() {
						$timeout(function() {
							if (document.getElementById('travel-goal-plane')) {
								document.getElementById('travel-goal-plane').style.display = 'block';
							}
							if (document.getElementById('leftPin')) {
								document.getElementById('leftPin').setAttribute('fill', '#4D2E91');
								document.getElementById('rightPin').setAttribute('fill', '#FFC324');
								document.getElementById('leftPinMobile').setAttribute('fill', '#4D2E91');
								document.getElementById('rightPinMobile').setAttribute('fill', '#FFC324');
								// hide and show flight progress bc of IE bug
								document.getElementById('flight-progress').setAttribute('stroke', '#F4846B');
							}
						});
					}

					$scs.get('TRAVEL_GOAL_DATA').then(function (data) {
						$scope.strings = data;
					});

					$scope.determinePaxLabel = function (num) {
						num = Number(num);
						var p = (num === 1) ? $scope.strings.personText : $scope.strings.personsText;
						return num + ' ' + p;
					};

					$scope.animatePlane = function () {
						if ($scope.model.isTravelGoalEnabled) {
							var waitForFlight = setInterval(function () {
								if ($scope.flight) {
									clearInterval(waitForFlight);
									$scope.flight.start($scope.startPercent, $scope.percentComplete);
								}
							}, 50);
						}
					};

					$scope.processTripInfo = function () {
						// $log.debug('process trip info');
						if ($scope.model && $scope.model.TripInformation) {

							if ($scope.percentComplete == null) {
								$scope.startPercent = 0;
							} else {
								$scope.startPercent = $scope.percentComplete;
							}
							// determine miles remaining and if goal is met
							$scope.milesRemaining = Number($scope.model.MilesRequired) - Number($scope.model.MilesAvailable);
							// $log.debug('miles remaining: '+$scope.milesRemaining);
							$scope.goalMet = false;
							if ($scope.model.isTravelGoalEnabled && $scope.milesRemaining <= 0 && $scope.model.MilesAvailable > 0) {
								$scope.goalMet = true;
								$scope.percentComplete = 1;
							} else {
								$scope.percentComplete = ($scope.model.MilesRequired - $scope.milesRemaining) / $scope.model.MilesRequired;
								if ($scope.percentComplete < 0) {
									$scope.percentComplete = 0;
								}
							}
							if ($scope.model.isTravelGoalEnabled) {
								colorizePins();
							} else {
								grayOutPins();
							}

							// get cities from airport code
							haCitiesSvc.getCityByCode($scope.model.TripInformation.OriginCode).then(function (airport) {
								// $log.debug(airport);
								$scope.model.originCity = airport;
							});
							haCitiesSvc.getCityByCode($scope.model.TripInformation.DestinationCode).then(function (airport) {
								$scope.model.destinationCity = airport;
							});

							// set origin and destination airport codes
							// $scope.originAirportCode = $scope.model.TripInformation.OriginCode;
							// $scope.destinationAirportCode = $scope.model.TripInformation.DestinationCode;

							// determine selected indexes
							for (var i = 0; i < $scope.model.TripTypeDropdown.length; i++) {
								if ($scope.model.TripTypeDropdown[i].Value === $scope.model.TripInformation.SelectedTripType) {
									$scope.indexes.tripTypeIndex = i;
									$scope.indexes.last_tripTypeIndex = i;
								}
							}

							for (var i = 0; i < $scope.model.CabinTypeDropdown.length; i++) {
								if ($scope.model.CabinTypeDropdown[i].Value === $scope.model.TripInformation.SelectedCabinType) {
									$scope.indexes.cabinTypeIndex = i;
									$scope.indexes.last_cabinTypeIndex = i;
								}
							}

							for (var i = 0; i < $scope.model.PaxCountDropdown.length; i++) {
								if ($scope.model.PaxCountDropdown[i].Value === $scope.model.TripInformation.SelectedPaxCount) {
									$scope.indexes.paxCountIndex = i;
									$scope.indexes.last_paxCountIndex = i;
								}
							}

							// construct plane animation
							// if (Number($scope.model.MilesAvailable) === 0) {
							// 	document.getElementById('miles-remaining').innerHTML = $scope.model.MilesRequired;

							if (!$scope.flight) {
								if (angular.element('html.ie8').length > 0) {
									// no svg in IE8, avoid polling svg size
									$scope.flight = new PathAnimator('travel-goal-plane', 'miles-remaining');
									$scope.flight.reset();
									$scope.animatePlane();
								} else {
									var itrvl = setInterval(function () {
										// $log.debug(document.getElementById('svg-contain').offsetWidth);
										// plane animation path depends on size of svg, which can scale slightly, depending
										// on page width. Must make sure container element is rendered before we initialize animation
										if (document.getElementById('svg-contain') && document.getElementById('svg-contain').offsetWidth > 0) {
											clearInterval(itrvl);
											$scope.flight = new PathAnimator('travel-goal-plane', 'miles-remaining');
											// $scope.flight.reset();
										}
									}, 50);
								}
							} else {
								// start animation
								$scope.animatePlane();
							}
							$scope.tripInfoProcessed = true;
						}
					};

					// GET TRAVEL GOAL VIEW MODEL
					haHttpService.GET(travelGoalEndpoint).then(function (response) {
						// $log.debug('get travel goal');
						// $log.debug(response.data);
						if (response.data.IsSuccess) {
							$scope.noModelError = false;
							$scope.model = response.data;
							//$scope.model.MilesAvailable = 5000; // for dev
							$scope.processTripInfo();
						} else {
							$scope.noModelError = true;
							$scope.error = response.data.TranslateServiceError;
							// $scope.error = "We're sorry, there was an error retrieving your travel goal."
						}
					});

					$scope.saveTravelGoal = function (noSpinner) {

						$('#travel-goal input').blur(); // dismiss ha-airport-input dropdown

						if (!$scope.invalidRoute) {
							// show spinner
							if (!noSpinner) {
								$scope.working = true;
							}
							// hide banner
							$scope.model.DisplayBannerMessage = false;
							// post
							var postObject = {
								isTravelGoalEnabled: String($scope.model.isTravelGoalEnabled),
								OriginCode: $scope.model.originCity.Code,
								DestinationCode: $scope.model.destinationCity.Code,
								SelectedTripType: $scope.model.TripTypeDropdown[$scope.indexes.tripTypeIndex].Value,
								SelectedCabinType: $scope.model.CabinTypeDropdown[$scope.indexes.cabinTypeIndex].Value,
								SelectedPaxCount: $scope.model.PaxCountDropdown[$scope.indexes.paxCountIndex].Value,
								DisplayBannerMessage: 'false'
							};
							// keep jibberish from displaying
							$scope.model.TripInformation.DestinationCityName = '';
							// $log.debug('save travel goal');
							// $log.debug(postObject);
							$scope.tripInfoProcessed = false;

							haHttpService.POST(saveTravelGoalEndpoint, postObject).success(function (data) {
								//$log.debug(data);
								$scope.working = false;
								if (data.IsSuccess) {
									//$log.debug('success saving travel goal');
									//$log.debug(data.SaveTravelGoalResponse);
									$scope.model.MilesRequired = data.SaveTravelGoalResponse.MilesRequired;
									$scope.model.MilesAvailable = data.SaveTravelGoalResponse.MilesAvailable;
									$scope.model.TripInformation = data.SaveTravelGoalResponse.TripInformation;
									$scope.model.IsPurchaseMilesRedirect = data.SaveTravelGoalResponse.IsPurchaseMilesRedirect;
									$scope.model.RedirectionCaptionForButton = data.SaveTravelGoalResponse.RedirectionCaptionForButton;
									$scope.model.DisplayImageButton = data.SaveTravelGoalResponse.DisplayImageButton;
									//$scope.model = data.SaveTravelGoalResponse;
									$scope.processTripInfo();
								} else {
									$log.error('error saving travel goal');
									$log.debug(data);
									$scope.error = data.TranslateServiceError;
								}
							}).error(function (data, status) {
								$scope.working = false;
								$log.error('error saving travel goal: ' + status);
							});
						}
					};
					// choose dropdown option and conditionally post for changed values
					$scope.chooseOption = function () {

						if (+$scope.indexes.tripTypeIndex !== +$scope.indexes.last_tripTypeIndex ||
							+$scope.indexes.cabinTypeIndex !== +$scope.indexes.last_cabinTypeIndex ||
							+$scope.indexes.paxCountIndex !== +$scope.indexes.last_paxCountIndex) {
							// close dd
							$scope.$broadcast('$closeCustomDropdown');
							$scope.$broadcast('$modalCancel');
							// set 'last' values
							$scope.indexes.last_tripTypeIndex = $scope.indexes.tripTypeIndex;
							$scope.indexes.last_cabinTypeIndex = $scope.indexes.cabinTypeIndex;
							$scope.indexes.last_paxCountIndex = $scope.indexes.paxCountIndex;
							// post
							$log.debug('save goal for new option chosen');
							$scope.saveTravelGoal();
						}
					};

					$scope.checkForInvalidRoute = function () {
						$scope.invalidRoute = false;
						if ($scope.model &&
							$scope.model.destinationCity &&
							$scope.model.originCity &&
							$scope.model.destinationCity.Code === $scope.model.originCity.Code) {
							$scope.invalidRoute = true;
							$scope.flight.reset();
						}
					};

					// watch origin and destination for changes so new goal can be saved
					var originCode;
					var destinationCode;
					$scope.$watch(function (scope) {
						if (scope.model) {
							return scope.model.originCity;
						}
					}, function (newValue) {
						if (!originCode && newValue && newValue.Code) {
							originCode = newValue.Code;
						}
						// have to check against a separate var because oldValue is always undefined
						else if (newValue != null && newValue.Code && newValue.Code !== originCode) {
							originCode = newValue.Code;
							$scope.checkForInvalidRoute();
							$scope.saveTravelGoal();
						}
					});
					$scope.$watch(function (scope) {
						if (scope.model) {
							return scope.model.destinationCity;
						}
					}, function (newValue) {
						if (!destinationCode && newValue && newValue.Code) {
							destinationCode = newValue;
						}
						else if (newValue != null && newValue.Code && newValue.Code !== destinationCode) {
							destinationCode = newValue.Code;
							$scope.invalidRoute = false;
							$scope.checkForInvalidRoute();
							$scope.saveTravelGoal();
						}
					});

					// watch for invalid route
					$scope.$watch('invalidRoute', function (newValue) {
						if (newValue) {
							grayOutPins();
						}
						else {
							colorizePins();
						}
					});

					// watch for travel goal being turned on/off
					$scope.$watch(function (scope) {
						if (scope.model) {
							return scope.model.isTravelGoalEnabled;
						}
					}, function (newValue) {
						// $log.debug('isTravelGoalEnabled changed from '+oldValue+' to '+newValue);
						if (!$scope.travelGoalEnabledInitialized) {
							if ($scope.model != null) {
								$scope.travelGoalEnabledInitialized = true;
							}
						}
						else if (typeof newValue === 'boolean') {
							if ($scope.flight) {
								$scope.flight.reset();
							}
							//$log.debug('save goal for goal enabled');
							$scope.percentComplete = 0; // reset startPercent
							$scope.saveTravelGoal(true);
							// set pin colors
							if (newValue) {
								colorizePins();
							} else {
								grayOutPins();
							}
						}
					});

					// listen for alert being closed
					$scope.$on('haAlertClosed', function (event, data) {
						if (data === 'migrationAlert') {
							// $log.debug('migration alert closed');
							$scope.model.DisplayBannerMessage = false;
							$scope.saveTravelGoal(true);
						}
					});
					// listen for scroll spy event
					$scope.$on('elementFirstScrolledIntoView', function (event, data) {
						if (data === 'travel-goal') {
							// $log.debug('scroll spy event received');
							$scope.animatePlane();
						}
					});

					$scope.openTripTypeModal = function() {
						haModal({
							id: 'travelGoal-tripTypeModal',
							backdrop: 'true',
							templateUrl: '/travelGoalTripTypeModal.html',
							scope: $scope
						});
					};
					$scope.openCabinTypeModal = function() {
						haModal({
							id: 'travelGoal-cabinTypeModal',
							backdrop: 'true',
							templateUrl: '/travelGoalCabinTypeModal.html',
							scope: $scope
						});
					};
					$scope.openPaxCountModal = function() {
						haModal({
							id: 'travelGoal-paxCountModal',
							backdrop: 'true',
							templateUrl: '/travelGoalPaxCountModal.html',
							scope: $scope
						});
					};

					// PathAnimator forked from https://github.com/yairEO/pathAnimator
					// refactored to use rAF, heavily modified to also animate path progress and miles
					function PathAnimator(target, milesEl) {

						// scope vars
						var self = this;
						self.plane = (typeof target === 'string') ? document.getElementById(target) : target;
						self.milesEl = (typeof milesEl === 'string') ? document.getElementById(milesEl) : milesEl;
						// check if its IE8 so we can avoid svg functions
						if (angular.element('html.ie8').length > 0) {
							self.IE8 = true;
						}

						this.setPath = function () {
							self.path = document.getElementById('flight-path');
							self.len = self.path.getTotalLength();
							var svgContain = document.getElementById('svg-contain'),
								travelArcSvg = document.getElementById('travel-arc'),
								travelArcWidth = parseFloat(travelArcSvg.getAttribute('width'));
							self.scaleRatio = svgContain.offsetWidth / travelArcWidth;

							self.planeTopDist = 0;
							if ($scope.$root.isMobile) {
								self.planeTopDist = 22;
							}
							$log.debug('travel goal scale: '+self.scaleRatio);

						};

						self.resetProgressPath = function () {

							self.pp = document.getElementById('flight-progress');
							self.ppLength = self.pp.getTotalLength();
							self.pp.style.strokeDasharray = self.ppLength + ' ' + self.ppLength;
							self.pp.style.strokeDashoffset = -self.ppLength;

							if ($scope.$root.isMobile) {
								self.pp.setAttribute('stroke-width', 40);
								document.getElementById('flight-path-bkgd').setAttribute('stroke-width', 40);
							}
						};

						// set up countUp
						this.resetMilesCount = function () {
							self.milesStartVal = $scope.model.MilesRequired;
							self.milesEndVal = $scope.milesRemaining;
						};

						this.start = function (startPercent, endPercent) {
							//$log.debug('start: '+startPercent+', '+endPercent);
							self.stop();
							self.startTime = null;
							self.reverse = (startPercent > endPercent);
							var startPos = 0.02;
							if (endPercent === 0) {
								endPercent = startPos;
							}
							if (startPercent === endPercent) {
								if (endPercent > startPos) {
									startPercent = startPos;
								}
								else {
									return false;
								}

							}
							if (endPercent > 0 && self.IE) {
								self.pp.style.opacity = 1;
							}
							// defaults
							self.startPercent = startPercent || 0;
							self.endPercent = endPercent || 1;
							// miles
							self.milesReq = $scope.model.MilesRequired;
							self.milesEl.innerHTML = $filter('number')(self.milesStartVal);
							self.milesEndVal = $scope.milesRemaining;
							// $log.debug('miles start: '+self.milesStartVal+', end: '+self.milesEndVal+', required: '+self.milesReq);
							// auto duration
							self.duration = (Math.abs(endPercent - startPercent) / 0.1) * 500;
							if (self.duration < 500) {
								self.duration = 500;
							}
							if (self.duration > 5000) {
								self.duration = 5000;
							}
							// $log.debug('auto-duration: '+self.duration);
							// animate
							self.rAF = requestAnimationFrame(self.step);
						};

						this.stop = function () {
							if (self.rAF) {
								cancelAnimationFrame(self.rAF);
							}
						};
						this.reset = function () {
							self.stop();
							self.startTime = null;
							self.resetMilesCount();
							if (!self.IE8) {
								self.setPath();
								self.resetProgressPath();
							}
						};

						self.reset();

						// (progress, startVal, endVal, duration)
						this.easeOutQuart = function (t, b, c, d) {
							return -c * ((t = t / d - 1) * t * t * t - 1) * 1024 / 1023 + b;
						};
						this.step = function (timestamp) {

							if (self.startTime === null) {
								self.startTime = timestamp;
							}
							self.timestamp = timestamp;
							var progress = timestamp - self.startTime;
							self.remaining = self.duration - progress;
							var decimalPercent = progress / self.duration;

							// easing
							if (self.reverse) {
								var i = self.easeOutQuart(progress, 0, self.startPercent - self.endPercent, self.duration);
								decimalPercent = self.startPercent - i;
								if (decimalPercent < self.endPercent) {
									decimalPercent = self.endPercent;
								}
							} else {
								decimalPercent = self.easeOutQuart(progress, self.startPercent, self.endPercent, self.duration);
								// $log.debug(decimalPercent);
								if (decimalPercent > self.endPercent) {
									decimalPercent = self.endPercent;
								}
							}

							//$log.debug(Math.round(decimalPercent * 100)/100);

							// draw next frame or execute callback
							if (progress < self.duration) {

								// countUp (or down) on miles remaining
								var frameVal = Math.round(self.milesReq - (decimalPercent * self.milesReq));
								if (self.reverse) {
									if (frameVal > self.milesEndVal) {
										frameVal = self.milesEndVal;
									}
								} else {
									if (frameVal < self.milesEndVal) {
										frameVal = self.milesEndVal;
									}
								}
								var miles = $filter('number')(frameVal);
								// $log.debug(miles);
								self.milesEl.innerHTML = miles;

								if (!self.IE8) {
									// animate progress path
									self.pp.style.strokeDashoffset = (decimalPercent - 1) * self.ppLength;

									// animate plane
									var ppFrameLength = self.len * decimalPercent;

									var p = [
										self.path.getPointAtLength(ppFrameLength - 1),
										self.path.getPointAtLength(ppFrameLength + 1)
									];
									var angle = Math.atan2(p[1].y - p[0].y, p[1].x - p[0].x) * 180 / Math.PI;
									var point = self.path.getPointAtLength(ppFrameLength);

									// keep from pointing straight down or straight across
									if (angle > -25 && angle < 25 && angle !== 0) {
										// adjust for scale
										var xPos = point.x * self.scaleRatio;
										var yPos = (point.y * self.scaleRatio) + self.planeTopDist;
										// move plane if it's not at the end of the flight path
										if (point.x !== self.lastPointX) {
											if (self.IE) {
												point.y += 1;
											}
											var planeStyle = 'top:' + yPos + 'px;' +
												'left:' + xPos + 'px;' +
												'transform:rotate(' + angle + 'deg);' +
												'-ms-transform:rotate(' + angle + 'deg);' +
												'-webkit-transform:rotate(' + angle + 'deg); display:block';
											//$log.debug(planeStyle);
											self.plane.style.cssText = planeStyle;
											self.lastPointX = point.x;
										}
									}
								}

								// continue
								self.rAF = requestAnimationFrame(self.step);
							} else {
								// $log.debug('complete');
								// on complete
								self.milesEl.innerHTML = $filter('number')(self.milesEndVal);
								self.milesStartVal = self.milesEndVal;
								// for sad pathetic IE8
								cancelAnimationFrame(self.rAF);
							}
						};

						// make sure requestAnimationFrame and cancelAnimationFrame are defined
						// polyfill for browsers without native support
						// by Opera engineer Erik Möller
						var lastTime = 0;
						var vendors = ['webkit', 'moz', 'ms', 'o'];
						for (var x = 0; x < vendors.length && !$window.requestAnimationFrame; ++x) {
							$window.requestAnimationFrame = $window[vendors[x] + 'RequestAnimationFrame'];
							$window.cancelAnimationFrame =
								$window[vendors[x] + 'CancelAnimationFrame'] || $window[vendors[x] + 'CancelRequestAnimationFrame'];
						}
						if (!$window.requestAnimationFrame) {
							$window.requestAnimationFrame = function (callback) {
								var currTime = new Date().getTime();
								var timeToCall = Math.max(0, 16 - (currTime - lastTime));
								var id = $window.setTimeout(function () {
									callback(currTime + timeToCall);
								},
									timeToCall);
								lastTime = currTime + timeToCall;
								return id;
							};
						}
						if (!$window.cancelAnimationFrame) {
							$window.cancelAnimationFrame = function (id) {
								clearTimeout(id);
							};
						}
					}
				}
			};
		}]);


	module.filter('isHACity', function () {
		return function filterFn(city) {
			//console.log(city);
			return (city != null) && (city.IsHACity === true);
		};
	});

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Neat Forms
	// ===============================
	//
	// * **Class:** HaNeatForms
	// * **Author:** Jamie Perkins
	//
	// Forms to hand user off to Neat for Hotels & Packages

	var module = angular.module('haNeatFormsModule', []);

	module.directive('haNeatForms', ['haUtils', 'haDateUtils', '$window', 'haCitiesSvc', '$filter', 'localShortDateFilter', '$timeout', 'haConfig', 'haSitecoreStrings', '$log',
		function (haUtils, haDateUtils, $window, haCitiesSvc, $filter, localShortDateFilter, $timeout, haConfig, $scs, $log) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-neat-forms-template.html'),
				restrict: 'A',
				scope: {
					formType: '@',
                    onMauve: '@'
				},
				link: function ($scope, $el, $attrs) {
					// form stuff
					$scope.language = $language;
					$scope.Error = '';
					$scope.passengerOptions = [1, 2, 3, 4, 5, 6];
					$scope.childPassengerOptions = [0, 1, 2, 3, 4, 5, 6];
					$scope.childPassengerAge = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
					$scope.roomOptions = [];
					$scope.packageTimes = [
						{
							name: $scs('BookingWidget.anytime'),
							val: 'Anytime'
						}, {
							name: $scs('BookingWidget.morning'),
							val: 'Morning'
						}, {
							name: $scs('BookingWidget.noon'),
							val: 'Noon'
						}, {
							name: $scs('BookingWidget.evening'),
							val: 'Evening'
						}, {
							name: '12:00 am',
							val: 'TwelveAm'
						}, {
							name: '1:00 am',
							val: 'OneAm'
						}, {
							name: '2:00 am',
							val: 'TwoAm'
						}, {
							name: '3:00 am',
							val: 'ThreeAm'
						}, {
							name: '4:00 am',
							val: 'FourAm'
						}, {
							name: '5:00 am',
							val: 'FiveAm'
						}, {
							name: '6:00 am',
							val: 'SixAm'
						}, {
							name: '7:00 am',
							val: 'SevenAm'
						}, {
							name: '8:00 am',
							val: 'EightAm'
						}, {
							name: '9:00 am',
							val: 'NineAm'
						}, {
							name: '10:00 am',
							val: 'TenAm'
						}, {
							name: '11:00 am',
							val: 'ElevenAm'
						}, {
							name: '12 Noon',
							val: 'Noon'
						}, {
							name: '1:00 pm',
							val: 'OnePm'
						}, {
							name: '2:00 pm',
							val: 'TwoPm'
						}, {
							name: '3:00 pm',
							val: 'ThreePm'
						}, {
							name: '4:00 pm',
							val: 'FourPm'
						}, {
							name: '5:00 pm',
							val: 'FivePm'
						}, {
							name: '6:00 pm',
							val: 'SixPm'
						}, {
							name: '7:00 pm',
							val: 'SevenPm'
						}, {
							name: '8:00 pm',
							val: 'EightPm'
						}, {
							name: '9:00 pm',
							val: 'NinePm'
						}, {
							name: '10:00 pm',
							val: 'TenPm'
						}, {
							name: '11:00 pm',
							val: 'ElevenPm'
						}
                    ];
                    $scope.getMediaImage = haUtils.getImageFromSiteCoreString;
					//Fn to update Scope.packageTimes for Expedia booking on US site.
					if ($scope.language === 'en') {
						$scope.packageTimes = $scope.packageTimes.map(function (x) {
							if (x.val === 'Anytime') {
								x.val = 362;
							} else if (x.val === 'Morning') {
								x.val = 361;
							} else if (x.val === 'Noon') {
								x.val = 721;
							} else if (x.val === 'Evening') {
								x.val = 1081;
							} else {
								var first = 2;
								if (x.name.indexOf(':') === 1) {
									first = 1;
								}
								x.val = x.name.substr(0, first) + x.name.substr(-2).toUpperCase();
							}
							return x;
						});
					}

					// for compatability with ha-datepicker2
					$scope.tripType = 2;
					$scope.legs = [];

					$scope.strings = {};
					$scs.get('BookingWidget').then(function (data) {
						$scope.strings = data;
						//$log.debug('strings', $scope.strings);						
						if ($attrs.formType === 'hotels') {
							$scope.maxRooms = 8;
							$scope.model.neatformbuttontext = $scope.strings.bookhotelbuttontext;
							$scope.model.dynamicDestinationText = $scope.strings.destinationtext;
							$scope.model.dynamicDepartText = $scope.strings.checkintext;
							$scope.model.dynamicReturnText = $scope.strings.checkouttext;
							$scope.model.thirdPartyPartnerImage = ($scope.onMauve === 'true')
								? $scope.strings.bghotelsthirdpartypartnerimage
								: $scope.strings.nobghotelsthirdpartypartnerimage;
							for (var i = 0; i < $scope.maxRooms; i++) {
								$scope.roomOptions[i] = i + 1;
							}
							$scope.model.enableHelpLink = true;
							if (!$scope.strings.vacationrentalshelpurl) {
								$scope.model.enableHelpLink = false;
							}
						} else if ($attrs.formType === 'packages') {
							$scope.maxRooms = 3;
							$scope.model.neatformbuttontext = $scope.strings.continuebuttontext;
							$scope.model.dynamicDestinationText = $scope.strings.to;
							$scope.model.dynamicDepartText = $scope.strings.departtext;
							$scope.model.dynamicReturnText = $scope.strings.returntext;
							$scope.model.thirdPartyPartnerImage = ($scope.onMauve === 'true')
								? $scope.strings.bgpackagesthirdpartypartnerimage
								: $scope.strings.nobgpackagesthirdpartypartnerimage;
							for (var j = 0; j < $scope.maxRooms; j++) {
								$scope.roomOptions[j] = j + 1;
							}
						}
					});

					// new datepicker fns

					// Get calendar heading
					function getCalendarHeading(legType) {
						if (!$scope.strings.departdate) {
							return '';
						}
						var departText = $scope.strings.departdate;
						var returnText = $scope.strings.returndate;
						if ($scope.model.packageType.indexOf('A') < 0 && $attrs.formType === 'hotels') {
							departText = $scope.strings.checkintext;
							returnText = $scope.strings.checkouttext;
						}
						return (legType !== 'returnDate') ? departText : returnText;
					}

					// defaults
					$scope.model = {
						adultCount: 1,
						childCount: 0,
						roomCount: 1,
						rooms: [
						{
							adultCount: 1,
							childrenCount: 0,
							child: []
						}],
						departDate: '',
						returnDate: '',
						originCity: '',
						destinationCity: '',
						packageType: '',
						airClass: 'EconomyClass',
						fromTime: $scope.packageTimes[0],
						toTime: $scope.packageTimes[0],
						formType: $scope.formType,
						onMauve: $scope.onMauve,
						neatforms_datepicker_config: {
							start: '[name=_FromDate]',
							end: '[name=_ToDate]',
							depart_cal_title: getCalendarHeading('departDate'),
							return_cal_title: getCalendarHeading('returnDate'),
							namespace: 'neatforms',
							onRender: function ($cal) {
								$cal.addClass('double-wide');
							}
						}
					};

					// watch so that unavailable days can be updated
					$scope.$on('airportChanged', function () {

						if ($scope.model.packageType.indexOf('A') >= 0 &&
							$scope.model.originCity &&
							$scope.model.originCity.Code &&
							$scope.model.destinationCity &&
							$scope.model.destinationCity.Code) {

							$scope.legs[0] = {
								origin: { Code: $scope.model.originCity.Code },
								destination: { Code: $scope.model.destinationCity.Code }
							};
							$scope.$broadcast('airport changed');
						}
					});

					$scope.$watch('model.rooms', function () {
						if ($scope.Error !== '') {
							$scope.Error = '';
						}
					}, true);


					// form type
					if ($attrs.formType === 'hotels') {
						$scope.model.packageType = 'H';
					} else if ($attrs.formType === 'packages') {
						$scope.model.packageType = 'AH';

						$scope.$watch(function (scope) {
							if (scope.model) {
								return scope.model.packageType;
							}
						}, function (newValue) {
							if (newValue && newValue.indexOf('A') < 0) { // if it does not contain a flight
								$scope.model.dynamicDestinationText = $scope.strings.destinationtext;
								$scope.model.dynamicDepartText = $scope.strings.checkintext;
								$scope.model.dynamicReturnText = $scope.strings.checkouttext;
							} else {
								$scope.model.dynamicDestinationText = $scope.strings.to;
								$scope.model.dynamicDepartText = $scope.strings.departtext;
								$scope.model.dynamicReturnText = $scope.strings.returntext;
							}
						});

					};

					// pre-select flightQuery cookie values
					var flightQueryCookie = haUtils.getFlightQueryModelCookie();
					if (flightQueryCookie) {
						// $log.debug('flight query model cookie');
						// $log.debug(flightQueryCookie);

						$scope.model.adultCount = flightQueryCookie.AdultCount || 1;
						$scope.model.childCount = flightQueryCookie.ChildCount || 0;

						if (flightQueryCookie.FlightSearchSegmentList[0]) {

							var originCode = flightQueryCookie.FlightSearchSegmentList[0].OriginCityCode;
							var destinationCode = flightQueryCookie.FlightSearchSegmentList[0].DestinationCityCode;
							// get cities from airport code
							haCitiesSvc.getCityByCode(originCode).then(function (airport) {
								// $log.debug(airport);
								$scope.model.originCity = airport;
							});
							haCitiesSvc.getCityByCode(destinationCode).then(function (airport) {
								$scope.model.destinationCity = airport;
							});

							// dates
							$scope.model.departDate = processDate(flightQueryCookie.FlightSearchSegmentList[0].DepartureDate);
							// $log.debug('set depart date: '+$scope.model.departDate);
						}
						if (flightQueryCookie.FlightSearchSegmentList[1]) {
							$scope.model.returnDate = processDate(flightQueryCookie.FlightSearchSegmentList[1].DepartureDate);
							// $log.debug('set return date: '+$scope.model.returnDate);
						}

					}

					$scope.updateChildren = function (i, n) {
						var num;
						if (n > $scope.model.rooms[i].child.length) {
							num = n - $scope.model.rooms[i].child.length;
							for (var r = 0; r < num; r++) {
								$scope.model.rooms[i].child.push({});
							}
						} else {
							num = $scope.model.rooms[i].child.length - n;
							if (num !== 0) {
								for (var c = 0; c < num; c++) {
									$scope.model.rooms[i].child.pop();
								}

							}
						}
					};
					$scope.updateRooms = function (n) {
						var num;
						if (n > $scope.model.rooms.length) {
							num = n - $scope.model.rooms.length;
							for (var r = 0; r < num; r++) {
								$scope.model.rooms.push({ adultCount: 1, childrenCount: 0, child: [] });
							}
						} else {
							num = $scope.model.rooms.length - n;
							if (num !== 0) {
								for (var i = 0; i < num; i++) {
									$scope.model.rooms.pop();
								}

							}
						}
					};

					$scope.clearRooms = function () {
						$scope.model.roomCount = 1;
						$scope.model.rooms = [
							{
								adultCount: $scope.model.rooms[0].adultCount,
								childrenCount: $scope.model.rooms[0].childrenCount,
								child: $scope.model.rooms[0].child
							}
						];
					}
					$scope.formatString = function (string, arr) {
						return haUtils.formatDynamicString(string, arr);
					};

					$scope.setForm = function (form) {
						$scope.form = form;
					};

					$scope.bizClassAvailable = function () {
						if (typeof $scope.model.destinationCity === 'object' && +$scope.model.destinationCity.Market === 3) {
							return true;
						} else if (typeof $scope.model.originCity === 'object' && +$scope.model.originCity.Market === 3) {
							return true;
						} else {
							return false;
						}
					};

					$scope.submitForm = function (e) {
						$scope.Error = '';
						if ($scope.language === 'cn'||$scope.language === 'kr' ) {
							$scope.submitNeatForm(e);
						} else {
							$scope.submitExpediaForm(e);
						}
					}

					$scope.enableHotelAndCarTab = function () {
						if ($scs('BookingWidget.hotelandcartext') == null || $scs('BookingWidget.hotelandcartext') === "" || $scs('BookingWidget.hotelandcartext') === "[BookingWidget.hotelandcartext]") {
							return false;
						} else {
							return true;
						}
					}
					$scope.enableHotelTab = function () {
						if ($scs('BookingWidget.hotelstext') == null || $scs('BookingWidget.hotelstext') === "" || $scs('BookingWidget.hotelstext') === "[BookingWidget.hotelstext]") {
							return false;
						} else {
							return true;
						}
					}

					$scope.enableVacationTab = false;
					if ($scs('BookingWidget.enablevacationrentals') === "1") {
						$scope.enableVacationTab = true;
					}

					$scope.submitNeatForm = function (e) {
						e.preventDefault();
						$scope.neatFormSubmitAttempt = true;

						// from/to
						var destinationCode;
						var destinationCityString = $scope.model.destinationCity;
						if (typeof $scope.model.destinationCity === 'object') {
							destinationCityString = $scope.model.destinationCity.LongDescription;
							destinationCode = $scope.model.destinationCity.Code;
						}
						var originCode;
						if (typeof $scope.model.originCity === 'object') {
							originCode = $scope.model.originCity.Code;
						}

						// format dates as shortdates
						var fromDate = $filter('localShortDate')($scope.model.departDate, 'en'); // neat always wants US formatted date
						var toDate = $filter('localShortDate')($scope.model.returnDate, 'en');

						var postObject = {
							DD: ($scope.model.packageType !== 'H') ? $scs('BookingWidget.neatgroupddparam') || 'HAWAIIANAIR' : $scs('BookingWidget.hotelneatgroupddparam') || 'HAWAIIANAIR',
							combinationType: $scope.model.packageType,
							adultsNum: $scope.model.adultCount,
							doSearch: 'T'
						};
						// create param for each child (ugh)
						var kids = Number($scope.model.childCount);
						if (kids > 0) {
							for (var i = 0; i < kids; i++) {
								var key = 'minorAge' + (i + 1);
								postObject[key] = 6;
							}
						}
						// dates
						if ($attrs.formType === 'hotels') {
							postObject.toLocation = $scope.isJP() ? $scope.model.destinationCity.SearchTags.split(',').slice(0, 2).join(',') : destinationCityString;
							postObject.hotelFromDate = fromDate;
							postObject.hotelToDate = toDate;
							postObject['WT.mc_id'] = haUtils.webtrends.token('HotelWidget');
						} else {
							postObject.toLocation = destinationCode;
							postObject.fromLocation = originCode;
							postObject.fromDate = fromDate;
							postObject.toDate = toDate;
							postObject.fromTime = $scope.model.fromTime.val;
							postObject.toTime = $scope.model.toTime.val;
							postObject['WT.mc_id'] = haUtils.webtrends.token('PackageWidget');
						}
						// hotel
						if ($scope.model.packageType.indexOf('H') >= 0) {
							postObject.roomsNum = $scope.model.roomCount;
						}
						// flight
						if ($scope.model.packageType.indexOf('A') >= 0) {
							postObject.airClass = $scope.model.airClass;
							postObject.fromLocation = originCode;
						}
						// fix for 154151
						if ($attrs.formType === 'packages' && ($language === 'en-au' || $language === 'en-nz')) {
							postObject.referrerId = 'Intl';
						}

						if ($scope.form.$valid) {
							var loc;
							if ($attrs.formType === 'packages') {
								postObject.DD = $scs('BookingWidget.neatgroupddparam') || 'HAWAIIANAIR';
								loc = $scs('BookingWidget.searchpackagesurl') + haUtils.createQueryString(postObject);
							} else {
								loc = $scs('BookingWidget.bookhotelurl') + haUtils.createQueryString(postObject);
							}

							$log.debug(loc);
							location.href = loc;
						} else {
							// $log.debug('error');
							$scope.Error = $scs('BookingWidget.pleasecorrecttheerrorsbelow');
						}
					};


					$scope.submitExpediaForm = function (e) {
						if ($attrs.formType === 'hotels' && $scope.model.packageType === 'HC') {
							$attrs.formType = 'packages';
						}
						e.preventDefault();
						$scope.neatFormSubmitAttempt = true;

						var validPassengerCount = verifyPassengerCount();
						var validChildrenInLap = verifyChildInLap();
						var postObject = {};

						// format dates as YYYY-MM-DD
						var fromDate = !!$scope.model.departDate ? $scope.model.departDate.YYYY_MM_DD() : undefined; // neat always wants US formatted date
						var toDate = !!$scope.model.returnDate ? $scope.model.returnDate.YYYY_MM_DD() : undefined;

						// from/to
						var destinationCode = !!$scope.model.destinationCity ? $scope.model.destinationCity.Code : undefined;
						var originCode = !!$scope.model.originCity ? $scope.model.originCity.Code : undefined;

						// dates

						if ($attrs.formType === 'hotels') {
							postObject.cityName = destinationCode || !!$scope.model.destinationCity && !!$scope.model.destinationCity.DisplayName ? $scope.model.destinationCity.DisplayName : '';
						} else {
							postObject.FromAirport = originCode;
							postObject.Destination = destinationCode;
							postObject.ToTime = $scope.model.toTime.val;
							postObject.FromTime = $scope.model.fromTime.val;
						}
						// hotel
						if ($scope.model.packageType.indexOf('H') >= 0) {
							postObject.NumRoom = $scope.model.roomCount;
						}
						// flight
						if ($scope.model.packageType.indexOf('A') >= 0) {
							// Taking first letter of class for expedia update.
							postObject.cabinClass = $scope.model.airClass.substr(0, 1).toLowerCase();
						}
						// dynamically create postObject

						var childCount = 0;
						var adultCount = 0;
						var infantInSeat = false;
						for (var r = 0; r < $scope.model.rooms.length; r++) {
							childCount += $scope.model.rooms[r].child.length;
							adultCount += $scope.model.rooms[r].adultCount;
							var adultKey;
							var childCountKey;
							if ($attrs.formType === 'hotels') {
								adultKey = 'NumAdult' + (r + 1);
							} else if ($scope.model.packageType === 'AC') {
								adultKey = 'NumAdult';
								childCountKey = 'NumChild';
							} else {
								adultKey = 'NumAdult-Room' + (r + 1);
								childCountKey = 'NumChild-room' + (r + 1);
							}
							postObject[adultKey] = $scope.model.rooms[r].adultCount;

							if ($attrs.formType === 'packages') {
								postObject[childCountKey] = $scope.model.rooms[r].child.length;
							}
							for (var c = 0; c < $scope.model.rooms[r].child.length; c++) {
								var childKey;
								if ($attrs.formType === 'hotels') {
									childKey = 'Rm' + (r + 1) + 'Child' + (c + 1) + 'Age';
								} else if ($scope.model.packageType === 'AC') {
									childKey = 'Child' + (c + 1) + 'Age';
								} else {
									childKey = 'Room' + (r + 1) + '-Child' + (c + 1) + 'Age';
								}

								if ($scope.model.rooms[r].child[c].seat === 'Seat') {
									infantInSeat = true;
								}
								if (infantInSeat && $scope.model.rooms[r].child[c].age < 2 && $scope.model.rooms[r].child[c].seat === 'Seat'
								) {
									postObject[childKey] = 2;
								} else {
									postObject[childKey] = $scope.model.rooms[r].child[c].age;

								}

							}
						}
						if ($attrs.formType === 'packages') {
							postObject.NumAdult = adultCount;
							postObject.NumChild = childCount;
						}



						var packageType;
						switch ($scope.model.packageType) {
							case 'AH':
								packageType = 'FlightHotel';
								break;
							case 'AHC':
								packageType = 'FlightHotelCar';
								break;
							case 'AC':
								packageType = 'FlightCar';
								break;
							case 'HC':
								packageType = 'HotelCar';
								break;
							case 'VR':
								packageType = 'VacationRental';
								break;
							default:
								packageType = 'FlightHotel';
						}

						if ($attrs.formType === 'hotels') {
							postObject['mdpcid'] = haUtils.webtrends.tokenV2("HTLWIDGET.HOTEL");
						} else if ($attrs.formType === 'packages') {

							// TODO: once Target 0030 is complete, restore this block to only:
							// postObject['mdpcid'] = haUtils.webtrends.tokenV2("PKGWIDGET.PACKAGE");
							if (typeof campaignid_0030 !== 'undefined') {
								postObject['mdpcid'] = campaignid_0030;
							} else {
								postObject['mdpcid'] = haUtils.webtrends.tokenV2("PKGWIDGET.PACKAGE");
							}

						} else {
							postObject['mdpcid'] = haUtils.webtrends.tokenV2("FLTWIDGET.PACKAGE");
						}

						if ($scope.form.$valid && validPassengerCount && validChildrenInLap) {
							var loc, url;
							var queryString = haUtils.createQueryString(postObject);
							if ($attrs.formType === 'packages') {
								url = $scs('BookingWidget.searchpackagesurl'); //2017-03-01
								loc = url + packageType + '/' + fromDate + '/' + toDate + queryString;
							} else {
								url = $scs('BookingWidget.bookhotelurl'); //2017-03-01
								loc = url + fromDate + '/' + toDate + queryString;
							}
							$log.debug(loc);
							location.href = loc;
						} else {
							// $log.debug('error');
							$scope.Error = $scs('BookingWidget.pleasecorrecttheerrorsbelow');
							if (!validPassengerCount) {
								$scope.Error = $scs('BookingWidget.InvalidPackagePassengerCount');
							}
							if (!validChildrenInLap) {
								$scope.Error = $scs('BookingWidget.LapChildErrorText');
							}
						}
					};

					function verifyPassengerCount() {
						var totalPassengers = 0;
						var valid = false;
						if ($attrs.formType === 'hotels') {
							valid = true;
						} else {
							for (var i = 0; i < $scope.model.rooms.length; i++) {
								totalPassengers += $scope.model.rooms[i].adultCount;
								totalPassengers += $scope.model.rooms[i].child.length;
							}
							if (totalPassengers >= 1 && totalPassengers <= 6) {
								valid = true;
							}
						}
						return valid;
					};

					function verifyChildInLap() {
						var lapChildren = 0;
						var adults = 0;
						var valid = false;
						for (var i = 0; i < $scope.model.rooms.length; i++) {
							adults += $scope.model.rooms[i].adultCount;
							for (var j = 0; j < $scope.model.rooms[i].child.length; j++) {
								if ($scope.model.rooms[i].child[j].age >= 12) {
									adults += 1;
								} else if ($scope.model.rooms[i].child[j].seat === 'Lap') {
									lapChildren += 1;
								}
							}
						}
						if (lapChildren <= adults) {
							valid = true;
						}
						return valid;
					}

					function processDate(date) {
						var dateString = date.substr(0, 10);
						var parts = dateString.split('-');
						return new Date(parts[0], parts[1] - 1, parts[2]);
					}

				}
			};

		}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Cartrawler Forms
	// ===============================
	//
	// * **Class:** HaCartrawlerForms
	// * **Author:** Jamie Perkins
	//
	// Forms to hand user off to Cartrawler for cars or shuttles

	var module = angular.module('haCartrawlerFormsModule', []);

	module.directive('haCartrawlerForms', ['haUtils', 'haDateUtils', '$window', 'haCitiesSvc', '$timeout', 'haConfig', 'haSitecoreStrings', '$log', 'haFeatureFlags', 'haEncryptionService', '$q',
		function (haUtils, haDateUtils, $window, haCitiesSvc, $timeout, haConfig, $scs, $log, flags, encryptionSvc, $q) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-cartrawler-forms-template.html'),
				restrict: 'A',
				scope: {
					onMauve: '@'
				},
				link: function ($scope, $el, $attrs) {

					var pleaseCorrectErrorsMsg,
						googlePlacesScript = 'https://maps.googleapis.com/maps/api/js?',
						MAX_PASSENGERS = 7;
					googlePlacesScript += 'libraries=places&key=';
					googlePlacesScript += flags.get('GoogleMapApiKey');

					// lazy load Google Places dependency
					haUtils.injectScriptDependency($el, googlePlacesScript);

					$scope.scContent = {};
					$scope.scDiscountContent = {};
					$scope.scBookingContent = {};
					$scope.Error = '';
					$scope.showTabGroup = false;
					// defaults
					$scope.model = {
						transportType: 'car',
						departDate: '',
						returnDate: '',
						originCity: '',
						destinationCity: '',
						onMauve: $scope.onMauve,
						transp_datepicker_config: {
							start: '[name=_TranspFromDate]',
							end: '[name=_TranspToDate]',
							depart_cal_title: '',
							return_cal_title: '',
							namespace: 'cartrawler',
							onRender: function ($cal) {
								if ($scope.tripType === 2) {
									$cal.addClass('double-wide');
								} else {
									$cal.removeClass('double-wide');
								}
							}
						},
						dropOffLocation: '',
						shuttlePassengers: 1,
						pickupTime: '24',
						dropoffTime: '24',
						carPricing: 'dollars'
					};

					// for compatability with ha-datepicker2
					$scope.tripType = 2;
					$scope.legs = [];

					// passengers
					$scope.shuttlePassengers = [];
					for (var i = 1; i <= MAX_PASSENGERS; i++) {
						$scope.shuttlePassengers.push(i);
					}

					// strings
					$scope.encryptionEnabled = false;

					$scs.get('CarTrawler').then(function (data) {
						$scope.scContent = data;
						$scope.carPickUpTimes = $scope.scContent.cartimes.slice();
						$scope.carDropOffTimes = $scope.scContent.cartimes.slice();
						// tab group
						$scope.showTabGroup = ($scope.scContent.enablervs || $scope.scContent.enableshuttles);
						$scope.model.thirdPartyPartnerImage = ($scope.onMauve === 'true')
							? $scope.scContent.bgcarsthirdpartypartnerimage
							: $scope.scContent.nobgcarsthirdpartypartnerimage;

						if ($scope.scContent.enableencryptionforvendorloyalty === "1") {
							$scope.encryptionEnabled = true;
						}
					});
					$scs.get('BookingWidget').then(function (data) {
						$scope.scBookingContent = data;
					 	pleaseCorrectErrorsMsg = data.pleasecorrecttheerrorsbelow;
					});

					//abg discount offer flags
					$scope.showDiscountBanners = false;

					$scs.get('CarRentalDiscountCode').then(function (data) {
						$scope.scDiscountContent = data;

						var banners = $scope.scDiscountContent.discountbanners;

						if (banners) {
							$scope.discountBanners = banners.slice();

							if ($scope.discountBanners && $scope.discountBanners.length > 0) {
								$scope.showDiscountBanners = true;
							}
						}
					});

					// we can stop the spinner on homepage
					$scope.$emit('cartrawlerFormsLoaded');

					$scope.setCarPricing = function (carPricing) {
						$scope.model.carPricing = carPricing;
						$scope.$broadcast('car pricing changed', $scope.model.carPricing);
					}

					// broadcast tripType so calendar can update
					$scope.setTripType = function(tripType) {
						$scope.tripType = tripType;
						$scope.$broadcast('trip type changed', $scope.tripType);
					};

					// need to change tripType back to 2 if not shuttle, and vice versa
					$scope.$watch('model.transportType', function (newValue) {
						if (newValue) {
							if (newValue === 'shuttle') {
								$scope.setTripType(1);
								$scope.model.thirdPartyPartnerImage = $scope.onMauve === 'true'
									? $scope.scContent.bgshuttlesthirdpartypartnerimage
									: $scope.scContent.nobgshuttlesthirdpartypartnerimage;
							} else {
								$scope.setTripType(2);

								if (newValue === 'rv') {
									$scope.model.thirdPartyPartnerImage = $scope.onMauve === 'true'
										? $scope.scContent.bgrvsthirdpartypartnerimage
										: $scope.scContent.nobgrvsthirdpartypartnerimage;
								} else {
									$scope.model.thirdPartyPartnerImage = $scope.onMauve === 'true'
										? $scope.scContent.bgcarsthirdpartypartnerimage
										: $scope.scContent.nobgcarsthirdpartypartnerimage;
								}
								
							}
						}
					});

					// watch for MKK, LNY, or ITO locations on shuttle form
					$scope.$on('airportOrAddressInputChanged', function (event, data) {
						if ($scope.model.transportType === 'shuttle') {

							var valid = true;
							if (data.model.Code && data.model.Code === 'MKK' || data.model.Code === 'LNY' || data.model.Code === 'ITO') {
								valid = false;
							}
							if (data.model.isAddress && data.model.DisplayName.match(/\sLanai\,\s|Lanai\sCity\,|Molokai\,?\s|\sHoolehua\,\s|Ho\'olehua\,?\s|Kaunakakai\,?\s|\sHilo\,?\s/g)) {
								valid = false;
							}

							$scope.form[data.name].$setValidity('location', valid);
							if (!valid) {
								$scope.Error = $scope.scContent.shuttleservicenotavailabletext;
							} else {
								$scope.Error = '';
							}

						}
					});

					$scope.submitButtonText = function() {
						if ($scope.scContent && $scope.scContent.searchcarrentalstext) {
							switch ($scope.model.transportType) {
								case 'shuttle':
									return $scope.scContent.searchshuttlestext;
									break;
								default:
									return $scope.scContent.searchcarrentalstext;
									break;
							}
						}
						else {
							return '';
						}
					};


					// for cars, if days are the same, limit dropoff times to be after pickup time
					$scope.updateDropOffTimes = function () {
						//$log.debug('update drop off times');
						if ($scope.scContent.cartimes && $scope.model.departDate && $scope.model.returnDate) {
							if ($scope.model.departDate.getTime() === $scope.model.returnDate.getTime()) {
								var pickupHour = +$scope.model.pickupTime;
								if (pickupHour !== 24) {
									$scope.carDropOffTimes = $scope.scContent.cartimes.slice(pickupHour + 1);
									if (+$scope.model.dropoffTime <= pickupHour) {
										$scope.model.dropoffTime = String(pickupHour + 1);
									}
								} else {
									$scope.carDropOffTimes = $scope.scContent.cartimes.slice();
								}
							} else {
								$scope.carDropOffTimes = $scope.scContent.cartimes.slice();
							}
						}
					};

					// watches
					$scope.$watch(function (scope) {
						if (scope.model) {
							return scope.model.departDate;
						}
					}, $scope.updateDropOffTimes);

					$scope.$watch(function (scope) {
						if (scope.model) {
							return scope.model.returnDate;
						}
					}, $scope.updateDropOffTimes);

					$scope.$watch(function (scope) {
						if (scope.model) {
							return scope.model.pickupTime;
						}
					}, $scope.updateDropOffTimes);

					// Shuttle time validation when the depart and return dates are the same.
					var shuttleTimeValid = function() {
						// make sure we have dates and that they are the same
						if ($scope.model.departDate && $scope.model.returnDate && $scope.model.departDate.getTime() === $scope.model.returnDate.getTime()) {
							// make sure we have both hours set
							if ($scope.model.pickUpHour && $scope.model.returnHour) {
								if (parseInt($scope.model.pickUpHour) > parseInt($scope.model.returnHour)) {
									// pickUpHour is after returnHour
									return false;
								} else if ($scope.model.pickUpHour === $scope.model.returnHour) {
									// hours are the same
									if ($scope.model.pickUpMinute && $scope.model.returnMinute && parseInt($scope.model.pickUpMinute) >= parseInt($scope.model.returnMinute)) {
										// pickUpMinute is after or the same as returnMinute
										return false;
									}
								}
							}
						}
						return true;
					}
					$scope.$watchGroup(['model.pickUpHour','model.pickUpMinute','model.departDate'], function() {
						if (!shuttleTimeValid()) {
							$scope.model.returnHour = $scope.model.returnMinute = undefined;
						}
					});
					$scope.$watchGroup(['model.returnHour','model.returnMinute','model.returnDate'], function() {
						if (!shuttleTimeValid()) {
							$scope.model.pickUpHour = $scope.model.pickUpMinute = undefined;
						}
					});

					// Prevent duplicate addresses for shuttles
					$scope.$watch('model.pickUpLocation', function(nv) {
						// Check if both values exist
						if (nv && $scope.model.dropOffLocation) {
							// New value is a place, not an airport, so compare placeIds.
							if (nv.placeId && (nv.placeId === $scope.model.dropOffLocation.placeId)) {
								$scope.model.dropOffLocation = undefined;
							// Otherwise compare airport names
							} else if (nv.Name && nv.Name === $scope.model.dropOffLocation.Name) {
								$scope.model.dropOffLocation = undefined;
							}
						}
					});
					$scope.$watch('model.dropOffLocation', function(nv) {
						// Check if both values exist
						if (nv && $scope.model.pickUpLocation) {
							// New value is a place, not an airport, so compare placeIds.
							if (nv.placeId && (nv.placeId === $scope.model.pickUpLocation.placeId)) {
								$scope.model.pickUpLocation = undefined;
							// Otherwise compare airport names
							} else if (nv.Name && nv.Name === $scope.model.pickUpLocation.Name) {
								$scope.model.pickUpLocation = undefined;
							}
						}
					});

					// pre-select flightQuery cookie values
					var flightQueryCookie = haUtils.getFlightQueryModelCookie();
					if (flightQueryCookie) {
						// $log.debug('flight query model cookie');
						// $log.debug(flightQueryCookie);

						if (flightQueryCookie.FlightSearchSegmentList[0]) {

							var originCode = flightQueryCookie.FlightSearchSegmentList[0].OriginCityCode;
							var destinationCode = flightQueryCookie.FlightSearchSegmentList[0].DestinationCityCode;
							// get cities from airport code
							haCitiesSvc.getCityByCode(originCode).then(function (airport) {
								// $log.debug(airport);
								$scope.model.originCity = airport;
							});
							haCitiesSvc.getCityByCode(destinationCode).then(function (airport) {
								$scope.model.destinationCity = airport;
							});

							// dates
							$scope.model.departDate = processDate(flightQueryCookie.FlightSearchSegmentList[0].DepartureDate);
							//$log.debug('set depart date: '+$scope.model.departDate);
						}
						if (flightQueryCookie.FlightSearchSegmentList[1]) {
							$scope.model.returnDate = processDate(flightQueryCookie.FlightSearchSegmentList[1].DepartureDate);
							//$log.debug('set return date: '+$scope.model.returnDate);
						}

					}
					$scope.setForm = function (form) {
						$scope.form = form;
					};

					$scope.submitCarForm = function (e) {
						e.preventDefault();

						if (!$scope.form.$valid) {
							$scope.Error = pleaseCorrectErrorsMsg;
							return;
						}
						//$scope.model.submitted = true;
						$scope.Error = '';

						// submit shuttle form
						if ($scope.model.transportType === 'shuttle') {
							submitShuttleForm();
						}
						// submit car form
						else if ($scope.model.transportType === 'car') {
							submitCarForm();							
						}

					};

					function submitShuttleForm() {
						/* expected params:
							clientID
							residenceID
							curr (currency)
							countryID
							pickupIATACode (if airport was entered in the  form)
							pickupName
							pkLat
							pkLng
							returnIATACode (if airport was entered in the  form)
							returnName (Drop off)
							rtLat
							rtLng
							pickupDateTime (Pickup Date + Pickup Hour + Pickup Minute)
							returnDateTime (Return Date + Return Hour + Return Minute)
							adults (# of passengers)
							oneway (true/false)
							*/
						
						var queryObject;

						var formGetUrl = $scope.scContent.searchshuttlesurl;
						var location = window.location.href.toLowerCase(),
							clientId;
						// determine location for which client ID to use
						if (location.match(/book/i)) {
							clientId = $scope.scContent.cartrawlerclientidbookpage;
						} else {
							clientId = $scope.scContent.cartrawlerclientidhomepage;
						}

						queryObject = {
							clientID: clientId,
							residenceID: $scope.scContent.cartrawlerresidencyidparam,
							curr: $scope.scContent.cartrawlercurrencycodeparam,
							adults: $scope.model.shuttlePassengers,
							oneway: Boolean($scope.tripType === 1),
							pickupName: $scope.model.pickUpLocation.DisplayName
						}
						// pick up params
						var pickUpDate = moment($scope.model.departDate).format('YYYY-MM-DD');
						var pickUpTime = 'T' + timestampFromHourAndMinute($scope.model.pickUpHour, $scope.model.pickUpMinute);
						queryObject.pickupDateTime = pickUpDate + pickUpTime;

						if ($scope.model.pickUpLocation.Code) {
							queryObject.countryID = $scope.model.pickUpLocation.CountryCode;
							queryObject.pickupIATACode = $scope.model.pickUpLocation.Code;
						}
						else if ($scope.model.pickUpLocation.geocode) {
							queryObject.countryID = $scope.model.pickUpLocation.countryCode || 'US';
							queryObject.pkLat = $scope.model.pickUpLocation.geocode.lat;
							queryObject.pkLng = $scope.model.pickUpLocation.geocode.long;
						}
						// drop off params
						if ($scope.model.dropOffLocation && $scope.model.dropOffLocation.DisplayName) {

							queryObject.returnName = $scope.model.dropOffLocation.DisplayName;
							if ($scope.model.dropOffLocation.Code) {
								queryObject.returnIATACode = $scope.model.dropOffLocation.Code;
							}
							else if ($scope.model.dropOffLocation.geocode) {
								queryObject.rtLat = $scope.model.dropOffLocation.geocode.lat;
								queryObject.rtLng = $scope.model.dropOffLocation.geocode.long;
							}

							if ($scope.tripType === 2) {
								var dropOffDate = moment($scope.model.returnDate).format('YYYY-MM-DD');
								var dropOffTime = 'T' + timestampFromHourAndMinute($scope.model.returnHour, $scope.model.returnMinute);
								queryObject.returnDateTime = dropOffDate + dropOffTime;
							}
						}
						
						formGetUrl = getUrl(formGetUrl, queryObject);

						//$log.debug(formGetUrl);
						//$log.debug('query object', queryObject);
						window.location.href = formGetUrl;
					}

					function submitCarForm() {
						var queryObject;
						var carPricing;
						window.digitalData.rentalCarPayment = {};

						if ($scope.model.carPricing.toLowerCase() === "dollars") {
							carPricing = "Dollars";
						}
						else if ($scope.model.carPricing.toLowerCase() === "miles") {
							carPricing = "Miles";
						}
						else {
							carPricing = "Miles/Dollars";
						}

						// event to post ABG Car Booking from Standalone Path
						document.body.dispatchEvent(new CustomEvent('StandaloneCarBooking',  {
							detail: {
								name: 'StandaloneCarBooking',
								timestamp: new Date().getTime(),
								paid: carPricing,
								requestFrom: 'StandaloneBooking'
							}
						}));

						var formGetUrl = $scope.scContent.searchcarrentalsurl;

						var key = "AmazonCarRentalKey";

						var destinationCode = $scope.model.destinationCity.Code;
						var originCode = $scope.model.carDropoff ? $scope.model.carDropoff.Code : destinationCode;

						var pickupHour = $scope.model.pickupTime;
						// based on pickup value of 'Anytime' on hawaiianairlines.com
						if (+pickupHour === 24) {
							pickupHour = 10;
						}

						var returnHour = $scope.model.dropoffTime;
						// based on dropoff value of 'Anytime' on hawaiianairlines.com
						if (+returnHour === 24) {
							returnHour = 11;
						}

						var clientId = getCarClientId();

						queryObject = {
							ops: 'spec',
							srch: 'car',
							eapid: '11428-30001',
							GOTO: 'CARSEARCH',
							lang: $scope.scContent.cartrawlerlanguageparam, // variable param
							rfrr: '-34980',
							age: '30',
							pickupIATACode: destinationCode,
							returnIATACode: originCode,
							pickupYear: $scope.model.departDate instanceof Date ? $scope.model.departDate.getFullYear() : '',
							pickupMonth: $scope.model.departDate instanceof Date ? $scope.model.departDate.getMonth() : '',
							pickupDate: $scope.model.departDate instanceof Date ? $scope.model.departDate.getDate() : '',
							pickupHour: pickupHour,
							pickupMinute: '00',
							returnYear: $scope.model.returnDate instanceof Date ? $scope.model.returnDate.getFullYear() : '',
							returnMonth: $scope.model.returnDate instanceof Date ? $scope.model.returnDate.getMonth() : '',
							returnDate: $scope.model.returnDate instanceof Date ? $scope.model.returnDate.getDate() : '',
							returnHour: returnHour,
							returnMinute: '00',
							currency: $scope.scContent.cartrawlercurrencycodeparam || 'USD',
							residencyId: $scope.scContent.cartrawlerresidencyidparam || 'US',
							c: $scope.scContent.cartrawlerlanguageparam,
							clientID: clientId
						};


						if ($scope.model.carPricing.toLowerCase() === "dollars" && $scope.model.carDiscount && $scope.model.carDiscount.selected && $scope.model.discountCode) {
							var avMembership = $scope.model.discountCode.avisWizard;
							var bgMembership = $scope.model.discountCode.budgetFastbreak;
							var zaMembership = $scope.model.discountCode.paylessPerks;

							if ($scope.encryptionEnabled) {
								var avMembershipPromise = (avMembership && avMembership !== '') ? encryptionSvc.EncryptString(avMembership, key) : getEmptyStringPromise();
								var bgMembershipPromise = (bgMembership && bgMembership !== '') ? encryptionSvc.EncryptString(bgMembership, key) : getEmptyStringPromise();
								var zaMembershipPromise = (zaMembership && zaMembership !== '') ? encryptionSvc.EncryptString(zaMembership, key) : getEmptyStringPromise();
								var discountPromises = [avMembershipPromise, bgMembershipPromise, zaMembershipPromise];

								$q.allSettled(discountPromises)
									.then(function (results) {
										var carRentalQueryObj = getCarRentalQueryObj(results[0].value, results[1].value, results[2].value);

										formGetUrl = getUrl(formGetUrl, queryObject, carRentalQueryObj);

										//$log.debug(formGetUrl);
										//$log.debug('query object', queryObject);
										window.location.href = formGetUrl;
									})
							} else {
								var carRentalQueryObj = getCarRentalQueryObj(avMembership, bgMembership, zaMembership);

								formGetUrl = getUrl(formGetUrl, queryObject, carRentalQueryObj);
								//$log.debug(formGetUrl);
								//$log.debug('query object', queryObject);
								window.location.href = formGetUrl;
							}
						} else {

							formGetUrl = getUrl(formGetUrl, queryObject);

							//$log.debug(formGetUrl);
							//$log.debug('query object', queryObject);
							window.location.href = formGetUrl;
						}
					}

					function getCarClientId() {
						var location = window.location.href.toLowerCase(),
						clientId;

						var isDollars = ($scope.model.carPricing && $scope.model.carPricing.toLowerCase() === 'dollars');
						var isBookPage = location.match(/book/i);

						// determine location for which client ID to use
						if (isBookPage) {
							if (isDollars) {
								clientId = $scope.scContent.cartrawlercarsclientidbookpage;
							} else {
								clientId = $scope.scContent.cartrawlercarsclientidmilesbook;
							}
						} else {
							if (isDollars) {
								clientId = $scope.scContent.cartrawlercarsclientidhomepage;
							} else {
								clientId = $scope.scContent.cartrawlercarsclientidmileshome;
							}
						}

						return clientId;
					}

					function getEmptyStringPromise() {
						return new Promise(function (resolve) {
							resolve("");
						});
					}

					function getCarRentalQueryObj(avMembership, bgMembership, zaMembership) {
						var carRentalQueryObj = {
							//avis
							AV: {
								CORPORATE_RATE: $scope.model.discountCode.awd,
								MEMBERSHIP_NUMBER: avMembership,
								PROMOTIONAL_CODE: $scope.model.discountCode.avisCoupon
							},
							//budget
							BG: {
								CORPORATE_RATE: $scope.model.discountCode.bcd,
								MEMBERSHIP_NUMBER: bgMembership,
								PROMOTIONAL_CODE: $scope.model.discountCode.budgetCoupon
							},
							//payless 
							ZA: {
								CORPORATE_RATE: $scope.model.discountCode.pdn,
								MEMBERSHIP_NUMBER: zaMembership,
								PROMOTIONAL_CODE: $scope.model.discountCode.paylessCoupon
							}
						}
						return carRentalQueryObj;
					}

					function getUrl(formGetUrl, queryObject, carRentalQueryObj) {
						var urlInfo = haUtils.splitUrl(formGetUrl);

						if (urlInfo.params.length > 0) {
							formGetUrl = urlInfo.url;
							formGetUrl += haUtils.createQueryString(queryObject, carRentalQueryObj, urlInfo.params);
						}
						else {
							formGetUrl += haUtils.createQueryString(queryObject, carRentalQueryObj);
						}

						return formGetUrl;
					}

					function processDate(date) {
						var dateString = date.substr(0, 10);
						var parts = dateString.split('-');
						return new Date(parts[0], parts[1] - 1, parts[2]);
					}

					function timestampFromHourAndMinute(hour, minute) {
						var hourInt = parseInt(hour);
						if (hour.indexOf('pm') > -1 && hourInt !== 12) {
							hourInt += 12;
						}
						if (hourInt === 12 && hour.indexOf('am') > -1) {
							hourInt = 0;
						}
						hour = '0'+hourInt;
						return hour.slice(-2) + ':' + minute + ':00';
					}
				}
			};
		}]);

})(angular);
;
(function (angular) {

	// Ha Personal Message
	// --------------------------------------------
	//
	// * **Class:** HaPersonalMessage
	// * **Author:** Jamie Perkins
	//
	// Textarea with character count for personal messages

	'use strict';

	var module = angular.module('haPersonalMessageModule', []);

	module.directive('haPersonalMessage', [function () {

		return {
			restrict: 'A',
			link: function ($scope, $el, $attrs) {

				$scope.label = $attrs.label;
				$scope.charsLeftText = $attrs.charsLeftText || 'characters remaining';
				$scope.textarea = $el.find('textarea');


				var max = $scope.textarea.attr('ng-maxlength');
				$scope.charactersRemaining = max + ' ' + $scope.charsLeftText;

				$scope.countCharacters = function () {
					$scope.charsExceeded = false;
					var txt = $scope.textarea.val();

					//console.log('count characters ('+txt.length+')');
					var remaining = max - txt.length;
					if (remaining < 0) {
						$scope.charsExceeded = true;
					}
					$scope.charactersRemaining = remaining + ' ' + $scope.charsLeftText;

				};
				var waitForElement = setInterval(function () {
					if ($scope.textarea.outerWidth() > 0) {
						clearInterval(waitForElement);
						$scope.countCharacters();
						$scope.$digest();
					}
				}, 50);

				// for when you need count to reset w/o keyup
				$scope.$on('$clearPersonalMessage', function () {
					$scope.textarea.val('');
					$scope.countCharacters();
				});

			}
		};

	}]);

})(angular);
;
(function(angular) {

	// Hawaiian Ancillary Upsells Directive
	// --------------------------------------------
	//
	// * **Class:** HaAncillariesUpsell
	// * **Author:** Jamie Perkins
	//
	// Ancillary upsells - Car, hotel, shuttle, etc. displayed with images and icons

	'use strict';

	var module = angular.module('haAncillaryUpsellsModule', []);

	module.directive('haAncillaryUpsells', ['haConfig', 'haUtils', 'haAncillaryUpsellsService', function (haConfig, haUtils, haAncillaryUpsellsService) {
		return {
			restrict: 'A',
			scope: {
				'upsellsModel': '=',
                'specialOffersText': '=',
				'itineraryUrl': '@',
				'confirmationViewmodel': '=',
				'tripType': '='
			},
			templateUrl: haConfig.getTemplateUrl('ha-ancillary-upsells-template.html'),
			link: function ($scope) {
				$scope.upsellsModel.sort(function (a, b) {
					return a.SortOrder - b.SortOrder;
				});

				$scope.UVM = $scope.upsellsModel;
				$scope.isWebView = isWebView;
						// upsells view model	
						$scope.tripType = $scope.tripType ? parseInt($scope.tripType) : undefined;

						function getPremiumSeatType() {
							var offerType = "";

							// if manage itinerary page
							if (typeof ItineraryDetailsJson !== "undefined") {
								offerType = (ItineraryDetailsJson.ItineraryMarketType === 1) ? "preferred" : "extracomfort";
							}

							// if booking confirmation page
							if (typeof modelJson !== "undefined") {
								offerType = (modelJson.marketType === 1) ? "preferred" : "extracomfort";
							}
							return offerType;
						}

						function getUpsellClassName(ancType) {
							return ancType +
								(ancType === "seatupgrade"
									? "-" + getPremiumSeatType().toLowerCase()
									: "") +
								"-upsell";
						}

						// sort and add cartrawler tracking
						if ($scope.UVM != null) {
							angular.forEach($scope.UVM, function(upsell) {

								upsell.LinkURL = haAncillaryUpsellsService.getLinkURL(upsell, $scope.tripType, $scope.confirmationViewmodel, $scope.itineraryUrl);

								// The variable below is used in the html template,
								//as a class selector for click event DTM tracking
								upsell.DtmClassName = getUpsellClassName(upsell.AncillaryType.toLowerCase());

								// DTM TRACKING - BEGINS
								switch (upsell.AncillaryTypeEnum) {
									//Tracking Cars Offered
									case 0: // "CAR"
										document.body.dispatchEvent(new CustomEvent("RentalCarOffered")); //event61
										break;

									//Tracking Hotel Offers
									case 2: //"HOTEL"
										document.body.dispatchEvent(new CustomEvent("HotelsOffered")); //event76
										break;

									//Tracking Trip Insurance Offers
									case 4: //"INSURANCE"
										document.body.dispatchEvent(new CustomEvent("TripInsuranceOffered")); //event63
										break;

									//Tracking Lei Greeting Offers
									case 5: //"LEI"
										document.body.dispatchEvent(new CustomEvent("LeiGreetingOffered")); //event59
										break;

									//Tracking Shuttle service Offers
									case 10: // "GTAIRPORTSHUTTLE"
										document.body.dispatchEvent(new CustomEvent("ShuttleServiceOffered")); //event62
										break;

									case 11: //"SEATUPGRADE"
										// market types:
										// 1 -> interisland -> preferred
										// 2 -> mainland -> extra comfort
										var offerType = getPremiumSeatType();
										document.body.dispatchEvent(new CustomEvent('UpgradeSeatOffered', { 'detail': {
											'seatType': offerType
										}}));
										break;
								}
								// DTM TRACKING - ENDS
							});
						}
					}
				};
			}
		]);

})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haAncillaryUpsellsMytripsModule', []);

	module.directive('haAncillaryUpsellsMytrips', ['haConfig', 'haAncillaryUpsellsService', function (haConfig, haAncillaryUpsellsService) {
		return {
			restrict: 'A',
			scope: {
				'upsellsModel': '=',
				'ancillaryLoc': '=', // to load template dynamically
				'specialOffersText': '@',
				'itineraryUrl': '@',
				'tripType': '=',
				'viewModel': '='
			},
			// Template will be dynamically loaded based on the ancillary location (bottom or right)
			// location is coming from ancillary-loc attribute on ItineraryDeteils.cshtml
			template: '<ng-include src="getTemplateUrl()" />',
			link: function ($scope) {
				$scope.getTemplateUrl = function () {
					if ($scope.ancillaryLoc === "right") // Complete Your Trip grid
						return haConfig.getTemplateUrl('ha-ancillary-upsells-mytrips-right-template.html');
					else // "bottom" Special Offers grid
						return haConfig.getTemplateUrl('ha-ancillary-upsells-mytrips-template.html');
				};

				// upsells view model
				$scope.UVM = $scope.upsellsModel;
				$scope.tripType = $scope.tripType ? parseInt($scope.tripType) : undefined;

				function getPremiumSeatType() {
					var offerType = "";

					// if manage itinerary page
					if (typeof ItineraryDetailsJson !== "undefined") {
						offerType = (ItineraryDetailsJson.ItineraryMarketType === 1) ? "PREFERREDSEAT" : "EXTRACOMFORT";
					}

					return offerType;
				}

				function getUpsellClassName(ancType) {
					return ancType +
						(ancType === "seatupgrade"
							? "-" + getPremiumSeatType().toLowerCase()
							: "") +
						"-upsell";
				}

				// sort and add cartrawler tracking
				if ($scope.UVM !== null) {
					angular.forEach($scope.UVM, function(upsell) {
						upsell.LinkURL = haAncillaryUpsellsService.getLinkURL(upsell, $scope.tripType, $scope.viewModel, $scope.itineraryUrl);

							// The variable below is used in the html template,
							//as a class selector for click event DTM tracking
							upsell.DtmClassName = getUpsellClassName(upsell.AncillaryType.toLowerCase());

							// DTM TRACKING - BEGINS
							switch (upsell.AncillaryTypeEnum) {
								//Tracking Cars Offered
							case 0: // "CAR"
								document.body.dispatchEvent(new CustomEvent("RentalCarOffered")); //event61
								break;

							//Tracking Hotel Offers
							case 2: //"HOTEL"
								document.body.dispatchEvent(new CustomEvent("HotelsOffered")); //event76
								break;

							//Tracking Trip Insurance Offers
							case 4: //"INSURANCE"
								document.body.dispatchEvent(new CustomEvent("TripInsuranceOffered")); //event63
								break;

							//Tracking Lei Greeting Offers
							case 5: //"LEI"
								document.body.dispatchEvent(new CustomEvent("LeiGreetingOffered")); //event59
								break;

							//Tracking Shuttle service Offers
							case 10: // "GTAIRPORTSHUTTLE"
								document.body.dispatchEvent(new CustomEvent("ShuttleServiceOffered")); //event62
								break;

							case 11: //"SEATUPGRADE"
								// market types:
								// 1 -> interisland -> preferred
								// 2 -> mainland -> extra comfort
								var offerType = getPremiumSeatType();
								document.body.dispatchEvent(new CustomEvent("UpgradeSeatOffered", { "detail": offerType }));
								break;
							}
							// DTM TRACKING - ENDS
						});

						$scope.UVM.sort(function (a, b) {
							return a.SortOrder - b.SortOrder;
						});
					}
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Hawaiian Purchased Ancillaries
	// ===============================
	//
	// * **Class:** HaPurchasedAncillaries
	// * **Author:** Jamie Perkins
	//
	// Shows ancillaries user has purchased on booking confirmation page

	'use strict';

	var module = angular.module('haPurchasedAncillariesModule', []);

	module.directive('haPurchasedAncillaries', ['haConfig', function (haConfig) {

		return {
			restrict: 'A',
			scope: {
				model: '='
			},
			templateUrl: haConfig.getTemplateUrl('ha-purchased-ancillaries-template.html'),
			link: function ($scope) {

				$scope.PVM = $scope.model;

			}
		};

	}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Neighbor Island Travel Plan Dashboard
	// ===============================
	//
	// * **Class:** haNitpDashboard
	// * **Author:** Jamie Perkins
	//
	// View NITP usage

	var module = angular.module('haNitpDashboardModule', []);

	module.directive('haNitpDashboard', ['haHttpService', '$document', '$timeout', 'haConfig', '$window', 'haUtils', 'haSitecoreStrings',
		function (haHttpService, $document, $timeout, haConfig, $window, haUtils, $scs) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-nitp-dashboard.html'),
				restrict: 'A',
				scope: {},
				link: function ($scope) {

					$scope.nitpDataEndpoint = '/program/nitppurchase/GetDashboardDetails';
					$scope.noModelError = false;
					$scope.error = '';
					$scope.chart = [];

					$scs.get('NITP_DASHBOARD_PANEL').then(function (data) {
						$scope.strings = data;
					});

					$scope.animateChart = function (i) {
						var waitForChart = setInterval(function () {
							if ($scope.chart[i]) {
								clearInterval(waitForChart);
								$scope.chart[i].start();
							}
						}, 50);
					};

					$scope.initChart = function (i) {

						if ($scope.model.DisplayNITPDashboard) {

							var chartData = [];
							if ($scope.model.plans[i].FlightsUsed > 0) {
								chartData[0] = { title: 'Used', value: $scope.model.plans[i].FlightsUsed, color: '#4d2e91' };
								if ($scope.model.plans[i].FlightsAvailable > 0) {
									chartData[1] = { title: 'Available', value: $scope.model.plans[i].FlightsAvailable, color: '#B83292' };
								}
							}
							else {
								chartData[0] = { title: 'Available', value: $scope.model.plans[i].FlightsAvailable, color: '#B83292' };
							}
							var chartContain = document.getElementById('chartContain' + i);
							$scope.chart[i] = new DrawDoughnutChart(chartContain, chartData, i);
							$scope.model.plans[i].chartReady = true; // for displaying used/available numbers
							haUtils.safeApply($scope); // "tell angular" that chartReady flag has been changed
							//$scope.animateChart(i);
						}
					};

					$scope.processModel = function () {
						angular.forEach($scope.model.plans, function (val) {
							val.chartReady = false;
						});
						// need to wait for angular to render chartContain divs in markup
						setTimeout(function () {
							angular.forEach($scope.model.plans, function (val, key) {
								$scope.initChart(key);
							});
						}, 500);
					};

					// GET VIEW MODEL
					haHttpService.GET($scope.nitpDataEndpoint).then(function (response) {
						// $log.debug('get NITP dashboard view model');
						// $log.debug(response.data);
						if (response.data.IsSuccess) {
							$scope.noModelError = false;
							if (response.data && response.data.DashboardVM && response.data.DashboardVM.length > 0) {
								$scope.model = {
									plans: response.data.DashboardVM,
									DisplayNITPDashboard: response.data.DashboardVM[0].DisplayNITPDashboard
								};
								$scope.processModel();
							} else {
								$scope.model.DisplayNITPDashboard = false;
							}
						} else {
							$scope.noModelError = true;
							$scope.error = 'We\'re sorry, there was an error retrieving data.';
						}
					});

					// listen for scroll spy event
					$scope.$on('elementFirstScrolledIntoView', function (event, data) {
						//$log.debug('elementFirstScrolledIntoView received for '+data);
						var match = data.match(/nitp-dashboard/);
						if (match && match.length > 0) {
							var index = data.match(/\d/);
							if (index.length === 1) {
								var i = Number(index[0]);
								$scope.animateChart(i);
							}
						}
					});

					/* adapted from https://github.com/githiro/drawDoughnutChart
					 - refactored into vanilla js
					 - design customized
					 - removed tooltips
					 - removed baseDoughnut
					 - removed summary
					 - number animation for 2 numbers
					 - IE8 & IE9 support
					 */
					function DrawDoughnutChart(el, data, index) {
						var self = this;
						var W = 240;
						var H = 240;
						var centerX = W / 2;
						var centerY = H / 2;
						var PI = Math.PI;
						var settings = {
							segmentShowStroke: false,
							segmentStrokeColor: '#0C1013',
							segmentStrokeWidth: 0,
							edgeOffset: 5, // offset from edge of element
							percentageInnerCutout: 50,
							animationSteps: 120,
							animationEasing: 'easeInOutExpo',
							callback: function () { }
						};
						var animationOptions = {
							linear: function (t) {
								return t;
							},
							easeInOutExpo: function (t) {
								var v = t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
								return (v > 1) ? 1 : v;
							}
						};

						// check for svg support
						self.IE8 = false;
						if (angular.element('html.ie8').length > 0) {
							// no svg in IE8
							self.IE8 = true;
						}

						// misc utilities
						function isNumber(n) {
							return !isNaN(parseFloat(n)) && isFinite(n);
						}

						function cap(valueToCap, maxValue, minValue) {
							if (isNumber(maxValue) && valueToCap > maxValue) {
								return maxValue;
							}
							if (isNumber(minValue) && valueToCap < minValue) {
								return minValue;
							}
							return valueToCap;
						}

						// initialize
						self.tripsAvailable = document.getElementById('tripsAvailable' + index);
						self.tripsUsed = document.getElementById('tripsUsed' + index);
						self.usedBar = document.getElementById('usedBar' + index);
						self.easingFunction = animationOptions[settings.animationEasing];
						self.segmentTotal = 0;
						self.usedIndex = data.map(function (e) {
							return e.title;
						}).indexOf('Used');
						self.availableIndex = data.map(function (e) {
							return e.title;
						}).indexOf('Available');

						if (self.IE8) {
							for (var i = 0; i < data.length; i++) {
								self.segmentTotal += data[i].value;
							}
						} else {
							self.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
							self.svg.setAttribute('width', W);
							self.svg.setAttribute('height', H);
							self.svg.setAttribute('viewBox', '0 0 ' + W + ' ' + H);
							el.appendChild(self.svg);

							self.doughnutRadius = Math.min(H / 2, W / 2) - settings.edgeOffset;
							self.cutoutRadius = self.doughnutRadius * (settings.percentageInnerCutout / 100);
							// Set up pie segments wrapper
							var pathGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
							// pathGroup.style.opacity = 0;
							self.svg.appendChild(pathGroup);
							// set up segments
							var paths = [];
							for (var i = 0; i < data.length; i++) {
								self.segmentTotal += data[i].value;
								paths[i] = document.createElementNS('http://www.w3.org/2000/svg', 'path');
								paths[i].setAttribute('stroke-width', settings.segmentStrokeWidth);
								paths[i].setAttribute('stroke', settings.segmentStrokeColor);
								paths[i].setAttribute('fill', data[i].color);
								paths[i].setAttribute('data-order', i);
								pathGroup.appendChild(paths[i]);
							}
						}

						// Private Functions
						function getHollowCirclePath(doughnutRadius, cutoutRadius) {
							//Calculate values for the path.
							//We needn't calculate startRadius, segmentAngle and endRadius, because base doughnut doesn't animate.
							var startRadius = -1.570;// -Math.PI/2
							var endRadius = 4.7131;// startRadius + segmentAngle
							var startX = centerX + Math.cos(startRadius) * doughnutRadius;
							var startY = centerY + Math.sin(startRadius) * doughnutRadius;
							var endX2 = centerX + Math.cos(startRadius) * cutoutRadius;
							var endY2 = centerY + Math.sin(startRadius) * cutoutRadius;
							var endX = centerX + Math.cos(endRadius) * doughnutRadius;
							var endY = centerY + Math.sin(endRadius) * doughnutRadius;
							var startX2 = centerX + Math.cos(endRadius) * cutoutRadius;
							var startY2 = centerY + Math.sin(endRadius) * cutoutRadius;
							var cmd = [
								'M', startX, startY,
								'A', doughnutRadius, doughnutRadius, 0, 1, 1, endX, endY,//Draw outer circle
								'Z', //Close path
								'M', startX2, startY2,//Move pointer
								'A', cutoutRadius, cutoutRadius, 0, 1, 0, endX2, endY2,//Draw inner circle
								'Z'
							];
							cmd = cmd.join(' ');
							return cmd;
						}

						function drawPieSegments(animationDecimal) {

							// animate available and used trips numbers
							if (self.availableIndex >= 0) {
								self.tripsAvailable.innerHTML = (Math.round(data[self.availableIndex].value * animationDecimal));
							}
							if (self.usedIndex >= 0) {
								self.tripsUsed.innerHTML = (Math.round(data[self.usedIndex].value * animationDecimal));
							}

							var startRadius = -PI / 2;//-90 degree
							var rotateAnimation = animationDecimal;//count up between0~1

							if (self.IE8) {
								//var usedbarPos = ((data[0].value / self.segmentTotal) * animationDecimal)*100;
								var usedbarPos = (data[self.availableIndex].value / self.segmentTotal) * animationDecimal * 100;
								self.usedBar.style.left = usedbarPos + '%';
							} else {
								pathGroup.setAttribute('opacity', animationDecimal);
								//If data have only one value, we draw hollow circle(#1).
								if (data.length === 1 &&
									(4.7122 < (rotateAnimation * ((data[0].value / self.segmentTotal) * (PI * 2)) + startRadius))) {
									paths[0].setAttribute('d', getHollowCirclePath(self.doughnutRadius, self.cutoutRadius));
									return;
								}
								for (var i = 0, len = data.length; i < len; i++) {
									var segmentAngle = rotateAnimation * ((data[i].value / self.segmentTotal) * (PI * 2));
									var endRadius = startRadius + segmentAngle;
									var largeArc = ((endRadius - startRadius) % (PI * 2)) > PI ? 1 : 0;
									var startX = centerX + Math.cos(startRadius) * self.doughnutRadius;
									var startY = centerY + Math.sin(startRadius) * self.doughnutRadius;
									var endX2 = centerX + Math.cos(startRadius) * self.cutoutRadius;
									var endY2 = centerY + Math.sin(startRadius) * self.cutoutRadius;
									var endX = centerX + Math.cos(endRadius) * self.doughnutRadius;
									var endY = centerY + Math.sin(endRadius) * self.doughnutRadius;
									var startX2 = centerX + Math.cos(endRadius) * self.cutoutRadius;
									var startY2 = centerY + Math.sin(endRadius) * self.cutoutRadius;
									var cmd = [
										'M', startX, startY,//Move pointer
										'A', self.doughnutRadius, self.doughnutRadius, 0, largeArc, 1, endX, endY,//Draw outer arc path
										'L', startX2, startY2,//Draw line path(this line connects outer and innner arc paths)
										'A', self.cutoutRadius, self.cutoutRadius, 0, largeArc, 0, endX2, endY2,//Draw inner arc path
										'Z' //Close path
									];
									paths[i].setAttribute('d', cmd.join(' '));
									startRadius += segmentAngle;
								}
							}
						}

						function animateFrame() {
							var easeAdjustedAnimationPercent = cap(self.easingFunction(self.cnt), null, 0);
							drawPieSegments(easeAdjustedAnimationPercent);
							self.cnt += self.animFrameAmount;

							if (self.cnt <= 1) {
								requestAnimationFrame(function () {
									animateFrame(self.cnt);
								});
							} else {
								settings.callback();
							}
						}

						// Animation start
						this.start = function () {
							//$log.debug('start nitp animation');

							self.animFrameAmount = 1 / cap(settings.animationSteps, Number.MAX_VALUE, 1);
							self.cnt = 0;
							animateFrame(self.cnt);
						};
						// remove svg
						this.remove = function () {
							if (!self.IE8) {
								el.removeChild(self.svg);
							}
						};

						// make sure requestAnimationFrame and cancelAnimationFrame are defined
						// polyfill for browsers without native support
						// by Opera engineer Erik Möller
						var lastTime = 0;
						var vendors = ['webkit', 'moz', 'ms', 'o'];
						for (var x = 0; x < vendors.length && !$window.requestAnimationFrame; ++x) {
							$window.requestAnimationFrame = $window[vendors[x] + 'RequestAnimationFrame'];
							$window.cancelAnimationFrame =
								$window[vendors[x] + 'CancelAnimationFrame'] || $window[vendors[x] + 'CancelRequestAnimationFrame'];
						}
						if (!$window.requestAnimationFrame) {
							$window.requestAnimationFrame = function (callback) {
								var currTime = new Date().getTime();
								var timeToCall = Math.max(0, 16 - (currTime - lastTime));
								var id = $window.setTimeout(function () {
									callback(currTime + timeToCall);
								},
									timeToCall);
								lastTime = currTime + timeToCall;
								return id;
							};
						}
						if (!$window.cancelAnimationFrame) {
							$window.cancelAnimationFrame = function (id) {
								clearTimeout(id);
							};
						}
					}
				}
			};
		}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Premier Club Dashboard
	// ===============================
	//
	// * **Class:** haPremierClubDashboard
	// * **Author:** Jamie Perkins
	//
	// View Premier Club enrollment status

	var module = angular.module('haPremierClubDashboardModule', []);

	module.directive('haPremierClubDashboard', ['haHttpService', 'haConfig', '$window', 'haSitecoreStrings',
		function (haHttpService, haConfig, $window, $scs) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-premier-club-dashboard.html'),
				restrict: 'A',
				scope: {},
				link: function ($scope) {

					var dataEndpoint = '/myaccount/mydashboard/GetPremierClubPanel';
					$scope.noModelError = false;
					$scope.error = '';

					$scs.get('PREMIER_CLUB_DASHBOARD_PANEL').then(function (data) {
						$scope.strings = data;
					});

					/* 	utility method:
					 takes "/Date(1420009200000)/"
					 returns date object
					 */
					$scope.dateStringToDate = function (datestring) {
						//$log.debug('parse date string: '+datestring);
						var time = datestring.match(/[\d]+/);
						time = Number(time[0]);
						return new Date(time);
					};

					// GET VIEW MODEL
					haHttpService.GET(dataEndpoint).then(function (response) {
						// $log.debug('get Premier club dashboard view model ///////////////');
						// $log.debug(response.data);
						if (response.data.IsSuccess) {
							$scope.noModelError = false;
							$scope.model = response.data;
							// date
							$scope.model.ExpiryDate = $scope.dateStringToDate(response.data.ExpiryDate);
						} else {
							$scope.noModelError = true;
							$scope.error = 'We\'re sorry, there was an error retrieving data.';
						}
					});
				}
			};
		}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Foodland Registration
	// ===============================
	//
	// * **Class:** haFoodlandRegistration
	// * **Author:** Jamie Perkins
	//
	// Register your Maika'i rewards number to earn HawaiianMiles

	var module = angular.module('haFoodlandRegistrationModule', []);

	module.directive('haFoodlandRegistration', ['haHttpService', 'haConfig', 'haSitecoreStrings', '$log',
		function (haHttpService, haConfig, $scs, $log) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-foodland-registration.html'),
				restrict: 'A',
				link: function ($scope) {

					var submitEndpoint = '/Program/FoodLand/MakaiRegistration';

					$scope.error = '';
					$scope.foodlandVM = {};
					$scope.state = 'register';

					$scs.get('FoodlandMakaiRegistration').then(function (data) {
						$scope.strings = data;
						// $log.debug('strings');
						// $log.debug($scope.strings);
					});

					function validateMaikaiNumber(maikai) {
						/*
						 Step 1  Validate Makaii Foodland Number Length is 12. Example: 432102834281
						 Step 2 Sum up the digits at positions – 1, 3, 5, 7, 9 , 11.  Ex: 4 + 2 + 0 + 8 + 4 + 8 = 26 (A)
						 Step 3 Multiply the sum (A) by 3 Ex: 26 * 3 = 78 (B)
						 Step 4 Now sum up the digits at positions – 2, 4, 6, 8, 10. Ex: 3 + 1 + 2 + 3 + 2 = 11 (C)
						 Step 5 Add B + C. Ex: 78 + 11 = 89 (D)
						 Step 6 Perform (10 – (D  MOD 10)) MOD 10 (E)
						 Step 7 Validate 12th digit equal (same ) as E. Ex: 1 == 1 (F)
						 Step 8 If (F) succeeds, the given number is a valid Makaii Foodland Number
						 */
						var maikaiString = String(maikai);
						if (maikaiString.length === 0) {
							return true;
						}

						if (maikaiString.length === 12) {
							var arr = maikaiString.split('');
							var nums = arr.slice(0, 11);
							var A = 0;
							var C = 0;
							var twelvethDigit = Number(arr[11]);

							for (var i = 0; i < nums.length; i++) {
								var n = Number(nums[i]);
								if (i % 2) {
									C += n;
								}
								else {
									A += n;
								}
							}
							var B = A * 3;
							var D = B + C;
							var E = (10 - (D % 10)) % 10;
							return (E === twelvethDigit);
						}
						else {
							return false;
						}
					}

					$scope.setForm = function (form) {
						$scope.form = form;
					};

					$scope.checkMaikaiNumberValidity = function () {
						$scope.form.maikaiNumber.$setValidity('pattern', validateMaikaiNumber($scope.foodlandVM.MaikaiAccountNumber));
					};

					$scope.submitForm = function (e, form) {
						e.preventDefault();

						if (form.$valid) {
							// $log.debug('post');
							// $log.debug($scope.foodlandVM);

							$scope.submitting = true;
							haHttpService.POST(submitEndpoint, $scope.foodlandVM).success(function (data) {
								// $log.debug('submit response');
								// $log.debug(data);
								$scope.submitting = false;
								if (data.IsRegistrationSuccess) {
									// clear fields
									form.$setPristine();
									$scope.state = 'complete';
								}
								else {
									$scope.error = data.ErrorMessage;
								}

							}).error(function (data, status) {
								$scope.submitting = false;
								$log.debug('submit error: ' + status);
							});
						}
					};

					$scope.resetForm = function () {
						$scope.foodlandVM = {};
						$scope.state = 'register';
						$scope.submitting = false;
						$scope.error = '';
					};
				}
			};
		}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Upcoming Trips Dashboard
	// ===============================
	//
	// * **Class:** haUpcomingTripsDashboard
	// * **Author:** Jamie Perkins
	//
	// View upcoming trips on the my-account dashboard

	var module = angular.module('haUpcomingTripsDashboardModule', []);

	module.directive('haUpcomingTripsDashboard', ['haGlobals', 'haHttpService', 'haConfig', 'haModal', '$rootScope', '$window', '$log', 'haSitecoreStrings', '$timeout',
		function (haGlobals, haHttpService, haConfig, haModal, $rootScope, $window, $log, $scs, $timeout) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-upcoming-trips-dashboard.html'),
				restrict: 'A',
				scope: {},
				link: function ($scope) {

					$scope.dataEndpoint = '/myaccount/mydashboard/GetUpcomingTrips' + ((/Windows /i).test(window.navigator.userAgent) ? ('?' + Math.floor(Math.random() * 9999) + 1) : '');
					$scope.isReloading = false;

					$scope.noModelError = false;
					$scope.error = '';
					$scope.hasUpcomingTrips = false;
					$scope.model = {
						sortUpcomingByOptions: [],
						sortUpcomingByIndex: 0,
						tripCountByImminence: []
					};

					// check language for whether to show last name first
					var languageName = $rootScope.$language.toLowerCase();


					haGlobals('enableOnlineUpgrade', function(enableOnlineUpgrade) {
						$scope.enableOnlineUpgrade = enableOnlineUpgrade && languageName === 'en';
					});
					$scope.lastNameFirst = /^ja|ko|zh|zh$/.test(languageName);
					$scope.strings = {};
					$scs.get('UPCOMING_TRIPS').then(function (data) {
						$scope.strings = data;
						$scope.model.sortUpcomingByOptions[0] = $scope.strings.alldatestext;
					});

					// get flight upgrade timeframe configuration
					$scs.get('UPGRADE_MODAL').then(function (data) {
						if (data.starthoursbeforedeparture !== undefined && data.endhoursbeforedeparture !== undefined) {
							$scope.upgradeStart = Number(data.starthoursbeforedeparture);
							$scope.upgradeEnd = Number(data.endhoursbeforedeparture);
						}
					});

					/*
					 Private functions
					 */
					function numDaysBetween(d1, d2) {
						var diff = Math.abs(d1.getTime() - d2.getTime());
						return diff / (1000 * 60 * 60 * 24);
					}

					// TEMPORARY local storage fns
					function storeObject(key, object) {
						// save
						//object = JSON.stringify(object);
						if (window.localStorage) {
							window.localStorage.setItem(key, object);
						}
					}

					function retrieveObject(key) {
						var saved;
						if (window.localStorage) {
							saved = window.localStorage[key];
						}

						if (saved) {
							return saved;
						} else {
							return null;
						}
					}

					/*
					 end private fns
					 */

					// GET VIEW MODEL
					$scope.getViewModel = function () {
						haHttpService.GET($scope.dataEndpoint).then(function (response) {
							// $log.debug('get upcoming trips dashboard view model');
							// $log.debug(response.data);

							if (response.data.IsSuccess) {

								$scope.noModelError = false;
								$scope.VM = response.data;

								// dropdown strings
								if ($scope.VM.ShowUpComingTripsFilterDropDown) {
									for (var i = 0; i < $scope.VM.ShowUpComingTripsFilterDropDown.length; i++) {
										$scope.model.sortUpcomingByOptions.push($scope.VM.ShowUpComingTripsFilterDropDown[i].Name);
									}
								}
								$timeout(function () {
									// fixes IE9 bug in which only the first letter of selected option is displayed
									$('#sortUpcomingByOptions').width($('#sortUpcomingByOptions').width());
								});
								// trips
								if ($scope.VM.UpComingTripsList.length) {
									$scope.hasUpcomingTrips = true;
									getNativeAppBannerStrings();

								}
								if ($scope.VM.AllTripsMaxRecordsPerPage === 0) {
									$scope.VM.AllTripsMaxRecordsPerPage = 3;
								}

								// sorting
								if ($scope.VM.UpComingTripsList.length > 0) {
									$scope.VM.UpcomingTripsCount = $scope.VM.UpComingTripsList.length;

									var today = new Date();

									// 1 day, 3 days , 1 week, 1 month, 1 year
									for (var i = 0; i < $scope.VM.UpComingTripsList.length; i++) {

										var flightDate = moment($scope.VM.UpComingTripsList[i].DepartureDateTime).toDate();
										var daysBack = numDaysBetween(flightDate, today);
										var threshold = 0;

										if (daysBack <= 1) {
											threshold = 1;
											$scope.model.tripCountByImminence[1] += 1;
										}
										else if (daysBack <= 3) {
											threshold = 2;
											$scope.model.tripCountByImminence[2] += 1;
										}
										else if (daysBack <= 7) {
											threshold = 3;
											$scope.model.tripCountByImminence[3] += 1;
										}
										else if (daysBack <= 30) {
											threshold = 4;
											$scope.model.tripCountByImminence[4] += 1;
										}
										else if (daysBack <= 365) {
											threshold = 5;
											$scope.model.tripCountByImminence[5] += 1;
										}
										$scope.VM.UpComingTripsList[i].threshold = threshold;
									}

									// check if loggedin Platinum user is in pax
									angular.forEach($scope.VM.UpComingTripsList, function (trip) {
										var loggedinUser = trip.PAXList.filter(function (item) {
											return item.IsLoggedinPassenger;
										})[0];
										if (loggedinUser === undefined) {
											trip.IsOnlineUpgradeEligible = false;
										}
									});

									// check for saved sorting index
									var savedSortingPrefIndex = Number(retrieveObject('sortUpcomingByIndex')) || 0; // default - show all
									var preferredIndex = 0;
									// determine most imminent index with backwards loop
									for (var i = $scope.model.tripCountByImminence.length; i >= 0; i--) {
										if (i > 0) {
											if ($scope.model.tripCountByImminence[i] > 0 && !$scope.VM.IsIndividual) {
												preferredIndex = i;
											}
										}
									}
									// make sure saved sorting index has trips, else use preferried index (most imminent)
									if ($scope.model.tripCountByImminence[savedSortingPrefIndex] > 0) {
										$scope.model.sortUpcomingByIndex = savedSortingPrefIndex;
									} else {
										$scope.model.sortUpcomingByIndex = preferredIndex;
									}

									// wait for strings, then add counts to sort options
									/* Bruno requested removal of count
									 var waitForStrings = setInterval(function() {
									 if ($scope.strings) {
									 clearInterval(waitForStrings);
									 for (var i = 0; i < $scope.sortUpcomingByOptions.length; i++) {
									 if (i > 0) {
									 var dateOptionString = $scope.sortUpcomingByOptions[i];
									 dateOptionString += ' ('+$scope.tripCountByImminence[i]+')';
									 $scope.sortUpcomingByOptions[i] = dateOptionString;
									 }
									 }
									 }
									 }, 50)
									 */
								}
							}
							//else {
							// IsSuccess = false
							//$scope.noModelError = true;
							//$scope.error = response.data.ModelMessage;
							//}
							$scope.isReloading = false;
						});
					}

					// get View model as soon as the document is ready
					angular.element(document).ready(function () {
						$scope.getViewModel();
					});

					// predicate function to filter upcoming trips by how soon they are
					$scope.tripsByImminence = function (val) {
						return +$scope.model.sortUpcomingByIndex === 0 || val.threshold <= $scope.model.sortUpcomingByIndex;
					};

					$scope.dismissDropdown = function () {
						$scope.$broadcast('$closeCustomDropdown');
					};

					$scope.chooseTripFilter = function (index) {
						$scope.model.sortUpcomingByIndex = index;
						storeObject('sortUpcomingByIndex', index);
						$scope.dismissDropdown();
					};

					$scope.openUpgradeModal = function (trip) {
						trip.allLegs = Array.prototype.concat.apply([], trip.Segments.map(function(segment) {
							return segment.Flights;
						}));
						haModal(haConfig.getTemplateUrl('ha-upgrade-modal.html'), {
							id: 'upgrade-modal',
							backdrop: 'true',
							scope: $scope,
							extendScope: {
								trip: trip,
								parent_view: "dashboard"
							},
							cancel: {
								label: 'Close',
								fn: function () {
									$scope.resetModalData();
								}
							}
						});
					};

					$scope.resetModalData = function() {
						angular.forEach($scope.VM.UpComingTripsList, function (trip) {
							angular.forEach(trip.allLegs, function (leg) {
								leg.selected = false;
								leg.platinum_pax = false;
								leg.optional_pax = "";
							});
						});
					}

					function getNativeAppBannerStrings() {
						if (!$rootScope.isMobile) {
							$scs.get('UPCOMING_TRIPS_NATIVE_APP_BANNER').then(function (data) {
								$scope.nativeAppBannerCookie = 'upcomingTripsNativeAppBannerDismissed';
								$scope.nativeAppBannerHeader = data.header;
								$scope.nativeAppBannerLinkText = data.linktext;
								$scope.nativeAppBannerCookieDays = data.cookiedays;
								$scope.nativeAppBannerEnabled = !!data.enable;
							});
						}
					};

					$scope.$on('OnlineUpgradeRqSuccess', function (event) {
						$scope.hasUpcomingTrips = false;
						$scope.isReloading = true;
						// GET VIEW MODEL
						$scope.getViewModel();
					});

					$scope.disableOpenItinerary = function (event) {
						if ($scope.openItineraryDisabled) {
							event.preventDefault();
							event.stopPropagation();
							return false;
						}
						$scope.openItineraryDisabled = true;

					};

					$scope.disableOpenSubItinerary = function (upcomingTrip, event) {
						if (upcomingTrip.openItineraryDisabled) {
							event.preventDefault();
							event.stopPropagation();
							return false;
						}
						upcomingTrip.openItineraryDisabled = true;
					};
				}
			};
		}]);

})(angular);
;
(function (angular) {

	// HA Share Itinerary
	// --------------------------------------------
	//
	// * **Class:** HaShareItinerary
	// * **Author:** Jamie Perkins
	//
	// Share flight itinerary with friends

	'use strict';

	var module = angular.module('haShareItineraryModule', []);

	module.directive('haShareItinerary', ['haConfig', 'haHttpService', 'haSitecoreStrings', 'haModal', '$rootScope', '$log',
		function (haConfig, haHttpService, $scs, haModal, $rootScope, $log) {

			return {
				restrict: 'A',
				scope: {
					tripVM: '=trip',
					isExpertBooking: '='
				},
				link: function ($scope, $el) {

					var shareItineraryModalId = 'share-itinerary';

					$scope.shareVM = {
						ShareItinerarySuccess: false,
						ShareItineraryError: false
					};

					$scope.strings = {};
					$scs.get('UPCOMING_TRIPS').then(function (data) {
						$scope.strings = data;
					});

					$el.on('click', openShareModal);

					// private fns

					function dismissDropdown() {
						$scope.$broadcast('$closeCustomDropdown');
					}

					function openShareModal() {
						dismissDropdown();
						$scope.shareTripVM = {
							PNR: $scope.tripVM.ReservationCode,
							EncryptedReservationCode: $scope.tripVM.EncryptedReservationCode || '',
							EncryptedLastName: $scope.tripVM.EncryptedLastName,
							ReadOnlyItineraryDetailsURL: $scope.tripVM.ReadOnlyItineraryDetailsURL
						};

						haModal(haConfig.getTemplateUrl('ha-share-itinerary-modal.html'), {
							id: shareItineraryModalId,
							backdrop: 'true',
							scope: $scope
						});
					}

					function validateMultipleEmails(val) {
						if (val && val.length) {
							var emails = val.split(',');
							for (var i = 0; i < emails.length; i++) {
								if (!emails[i].trim().match($rootScope.$regex.email)) {
									return false;
								}
							}
							return true;
						} else {
							return false;
						}
					}

					$scope.shareItinerary = function ($event) {

						if ($scope.isExpertBooking) {
							$event.preventDefault();
						}

						var shareItineraryEndpoint = '/myaccount/mytrips/ShareItineraryDetails';

						$scope.shareTripVM.PersonalMessage = $scope.shareVM.ShareItineraryMessage;
						$scope.shareTripVM.RecipientEmailIDs = $scope.shareVM.ShareItineraryRecipients;
						$scope.shareTripVM.SendersName = $scope.shareVM.ShareItinerarySendersName;
						
						//Added for expert booking path.
						if (!$scope.shareTripVM.PNR){
							$scope.shareTripVM.PNR = $scope.tripVM.pnr;
						}
						if (!$scope.shareTripVM.ReadOnlyItineraryDetailsURL){
							$scope.shareTripVM.ReadOnlyItineraryDetailsURL = $scope.tripVM.readonlyitinerarydetailsurl;
						}

						// $log.debug('share itinerary');
						// $log.debug($scope.shareTripVM);

						$scope.sharing = true;

						haHttpService.POST(shareItineraryEndpoint, $scope.shareTripVM)
							.success(function () {
								// $log.debug('share itinerary response');
								// $log.debug(data);
								$scope.sharing = false;
								$scope.shareVM.ShareItinerarySuccess = true;
								$scope.shareVM.ShareItineraryError = false;
								
								// clear fields
								$scope.shareVM.ShareItineraryRecipients = '';
								$scope.shareVM.ShareItineraryMessage = '';
								$scope.shareVM.ShareItinerarySendersName = '';
								$scope.$broadcast('$clearPersonalMessage');

								// reset form state
								$scope.form.recipientEmails.$touched = false;
								$scope.form.$setPristine();
								$scope.form.$submitted = false;
								$('form[name=' + $scope.form.$name + ']').removeClass('submitted');

								//Close the Modal if success.
								if ($scope.isExpertBooking) {
									$scope.$modalClose();
								}

							}).error(function (data, status) {
								$log.error('Share Itinerary error: ' + status);
								$scope.sharing = false;
								$scope.shareVM.ShareItinerarySuccess = false;
								$scope.shareVM.ShareItineraryError = true;
							});

					};

					// on modal close, reset modal
					$scope.$on('haModalClosed', function (event, data) {
						if (data === shareItineraryModalId) {
							$scope.sharing = false;
							$scope.shareVM.ShareItinerarySuccess = false;
							$scope.shareVM.ShareItineraryError = false;
						}
					});

					$scope.setForm = function (form) {
						$scope.form = form;
					};

					$scope.checkMultipleEmailValidity = function () {
						$scope.form.recipientEmails.$setValidity('pattern', validateMultipleEmails($scope.shareVM.ShareItineraryRecipients));
					};
				}
			};

		}]);

})(angular);
;
(function (angular) {

	// Ha Passenger Count Directive
	// --------------------------------------------
	//
	// * **Class:** haPaymentTypeMasterPass
	// * **Author:** Cory Shaw
	//
	// Module for the Master Pass pament type

	/* globals MasterPass,jsonCCTypes,jsonMemberAddress */

	'use strict';

	var mod = angular.module('haPaymentTypeMasterPassModule', []);

	mod.directive('haPaymentTypeMasterpass', [
		'haConfig',
		'$log',
		'haPaymentAPI',
		'haPaymentTypesService',
		'haUtils',
		'$timeout',
		function (haConfig, $log, haPaymentAPI, haPaymentTypesSvc, haUtils, $timeout) {

			var mpLink = function ($scope, $el, $attrs) {
				$scope.isSandboxEnv = $attrs.sandboxEnviornment;
				$scope.purchaseType = $attrs.purchaseType;
			};

			var mpController = function ($scope) {
				$scope.working = false;
				$scope.state = 'loading';
				$scope.haPaymentTypesSvc = haPaymentTypesSvc;
				$scope.getImg = haConfig.getImgUrl;

				// bind Credit Card and Country data to controller, so we can extract CC names and Country names from it
				var creditCardVM = jsonCCTypes;
				var countryVM = jsonMemberAddress.CountryStateDropDown;
				var MPGlobalAvailable = false;

				var mapCodeToName = function (sourceArray, sourceCode, sourceValue) {
					for (var i = 0; i < sourceArray.length; i++) {
						if (sourceArray[i][sourceValue] === sourceCode) {
							return sourceArray[i].Name;
						}
					}
					return sourceCode;
				};

				var showError = function () {
					$scope.working = false;
					//$log.error(error);
					$scope.state = 'error';
					$scope.errorMessage = {
						header: 'An Error Occured with Logging In',
						description: 'Please do something'
					};
				};

				// test for the availablity of the MasterPass global variable, reveal UI once ready
				var checkMPGlobal = function () {
					var interval = setInterval(function () {
						if (typeof MasterPass !== 'undefined') {
							MPGlobalAvailable = true;
							$scope.state = 'login';
							$scope.$apply();
							clearInterval(interval);
						}
					}, 300);
					$timeout(function () {
						if (!MPGlobalAvailable) {
							showError('MasterPass Global Variable Not Available');
							clearInterval(interval);
						}
					}, 10000);
				};

				if (!MPGlobalAvailable) {
					checkMPGlobal();
				}

				$scope.fetchToken = function () {
					$scope.working = true;
					haPaymentAPI.fetchMasterPassToken($scope.purchaseType).success(function (data) {
						$scope.working = false;
						if (data.IsSuccess) {
							MasterPass.client.checkout({
								'requestToken': data.RequestToken,
								'merchantCheckoutId': data.MerchantId,
								'allowedCardTypes': data.AllowedCardTypes,
								'suppressShippingAddressEnable': true,
								'loyaltyEnabled': false,
								'requestBasicCheckout': false,
								'version': 'v6',
								'successCallback': loginSuccess,
								'failureCallback': showError
							});
						}
						else {
							showError('There was a server error with the haPaymentAPI.fetchMasterPassToken response');
						}
						// $log.info(data);
					}, function (error) {
						showError(error);
						$scope.working = false;
					});
				};

				var loginSuccess = function (data) {
					$scope.state = 'loading';
					haPaymentAPI.fetchMasterPassPaymentInfo(data).success(function (result) {
						if (result.IsSuccess) {
							$scope.cardName = mapCodeToName(creditCardVM, result.CCInfo.CardType, 'Value');
							$scope.countryName = mapCodeToName(countryVM, result.AddressInfo.Country, 'Iso2Code');
						    //Mask all digits except for last four number. Card number is CDE token
							result.CCInfo.CardNumber = result.CCInfo.CardNumber.replace(/\d(?=\d{4})/g, "*");
							$scope.paymentData = result;
							$scope.state = 'paymentDetails';
						} else {
							showError('There was a server error with the haPaymentAPI.fetchMasterPassPaymentInfo response');
							$scope.working = false;
						}
					}, function (error) {
						showError(error);
						$scope.working = false;
					});
				};

				$scope.changeMpPaymentType = function () {
					$scope.state = 'login';
					$scope.fetchToken();
					$scope.working = true;
				};

				$scope.startOver = function () {
					$scope.state = 'login';
				};
			};

			mpController.$inject = ['$scope'];

			return {
				restrict: 'A',
				templateUrl: haConfig.getTemplateUrl('ha-payment-type-masterpass-base-template.html'),
				scope: {},
				controller: mpController,
				link: mpLink
			};
		}
	]);

})(angular);
;
(function(angular){

	// Ha Global Message Directive
	// --------------------------------------------
	//
	// * **Class:** haProgressBreadcrumb
	// * **Author:** Cory Shaw
	//
	// Progress breadcrumb for use in angular templates

	'use strict';

	var module = angular.module('haProgressBreadcrumbModule',[]);

	module.directive('haProgressBreadcrumb', ['haConfig', function (haConfig) {

		var haProgressBreadcrumbLink = function ($scope, $el) {
			$scope.breadcrumbs = $scope.breadcrumbData;
		};

		return {
			restrict: 'A',
			scope: {
				breadcrumbData: '='
			},
			link: haProgressBreadcrumbLink,
			replace: true,
			templateUrl: haConfig.getTemplateUrl('ha-progress-breadcrumb.html')
	};
}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haStickyMessageBarModule', []);

	// Author: Jamie Perkins
	// sticky message bar at the top of the screen, for notifying users of seat credits
	module.directive('haStickyMessageBar', [ 'haConfig', '$log', '$window', '$timeout', 'haVerticalSeatmapService', '$rootScope', function (haConfig, $log, $window, $timeout, haVerticalSeatmapService, $rootScope) {

		return {
			restrict: 'A',
			templateUrl: haConfig.getTemplateUrl('ha-sticky-message-bar-template.html'),
			scope: true,
			link: function ($scope, $el) {

				$scope.svc = haVerticalSeatmapService;
				var $bar = $el.children('.containerFullBleed');

				$scope.$on('legResolvedAtIndex', function(event, data) {
					if (data === $scope.svc.activeLegIndex) {
						$log.debug('received legResolvedAtIndex');
						queueShowHideAnimation();
					}
				});
				$scope.$on('seatSelectionLegChanged', function() {
					$log.debug('recieved seatSelectionLegChanged');
					if ($scope.svc.legs[$scope.svc.activeLegIndex] && $scope.svc.legs[$scope.svc.activeLegIndex].resolved) {
						queueShowHideAnimation();
					}
				});

				// on pax change
				$scope.$watch('svc.selectedPassengerIndex', function(newVal, oldVal) {
					if (newVal !== undefined && $scope.svc.legs[$scope.svc.activeLegIndex] && $scope.svc.legs[$scope.svc.activeLegIndex].resolved) {
						queueShowHideAnimation();
					}
				});

				// Sticky scrolling
				var fixed = false,
					stickyHeight = ($rootScope.isMobile) ? 120 : 70,
					stickyOffsetY = $el.offset().top,
					stuckOffset = ($('[ha-book-sticky-progress-bar]').length) ? $('[ha-book-sticky-progress-bar]').outerHeight() : 0,
					stickPoint = (stuckOffset > 0) ? stuckOffset : stickyOffsetY;
				function onWindowScroll() {
					var pageYOffset = window.pageYOffset || document.documentElement.scrollTop;
					if (pageYOffset >= stickPoint) {
						if (!fixed) {
							fixed = true;
							$bar.css({
								position: 'fixed',
								'z-index': 9,
								top: stuckOffset
							});
						}
					} else {
						if (fixed) {
							$bar.css({
								position: 'relative',
								top: 0
							});
							fixed = false;
						}
					}
				}
				onWindowScroll();
				angular.element($window).bind('scroll', onWindowScroll);

				// animation queue

				var animationQueue = [],
					animating = false,
					barIsVisible = false,
					animations = {
						hideBar: function() {
							if (barIsVisible) {
								$bar.removeClass('animate-show-bar').addClass('animate-hide-bar');
								$scope.showSeatCreditTooltip = false;
								$timeout(function() {
									barIsVisible = false;
								}, 300);
							}
						},
						showBar: function() {
							if (!barIsVisible) {
							    var pax = $scope.svc.passengers[$scope.svc.selectedPassengerIndex],
									paxSeat = pax.Seats[$scope.svc.activeLegIndex],
									fareClass = $scope.svc.legs[$scope.svc.activeLegIndex].SelectedFareClass;

								if (!$scope.ready) {
									$scope.ready = true;
									$rootScope.$broadcast('haStickyResize');
								}
								$scope.selectingPassenger = pax;
								$scope.currency = $rootScope.$currency;

								// only show bar if there is a seat credit
								if (fareClass !== 'first' && paxSeat.originalSeat && paxSeat.originalSeat.credit > 0) {
									$bar.removeClass('animate-hide-bar waiting-in-the-wings').addClass('animate-show-bar');

									$timeout(function() {
										barIsVisible = true;
									}, 300);
								}
							}
						}
					};

				function queueShowHideAnimation() {
					queueAnimation('hideBar', 300);
					queueAnimation('showBar', 300);
				}

				function queueAnimation(name, duration, callback) {
					//$log.debug('queueAnimation', name);
					var okToAdd = true;
					for (var i = 0, len = animationQueue.length; i < len; i++) {
						if (animationQueue[i].name === name) {
							okToAdd = false;
							break;
						}
					};
					if (okToAdd) {
						animationQueue.push({
							name: name,
							duration: duration,
							callback: callback
						});
					}
					if (!animating) {
						fireNextAnimationInQueue();
					}
				}
				function fireNextAnimationInQueue() {
					if (!animationQueue[0]) {
						animating = false;
						return;
					}
					if (!animating) {
						animating = true;
					}
					animations[animationQueue[0].name]();
					$timeout(function() {
						if (animationQueue[0].callback) {
							animationQueue[0].callback();
						}
						animationQueue.shift();
						fireNextAnimationInQueue();
					}, animationQueue[0].duration);
				}

				// if sticky message bar loads after listened for events fire
				if ($scope.svc.legs[$scope.svc.activeLegIndex] && $scope.svc.legs[$scope.svc.activeLegIndex].resolved) {
					queueShowHideAnimation();
				}

			}
		}
	}]);

})(angular);
;
(function (angular) {

	// Ha Primary Nav Account Menu
	// --------------------------------------------
	//
	// * **Class:** HaPrimaryNavAccountMenu
	// * **Author:** Cory Shaw
	//
	// Makes an ajax call to an endpoint that returns cached html to improve the backend performance of the global header

	'use strict';

	var module = angular.module('haPrimaryNavAccountMenuModule', []);

	module.directive('haPrimaryNavAccountMenu', ['haHttpService', 'haConfig', '$compile', '$timeout', 'haGlobals', '$q', '$rootScope', function ($http, haConfig, $compile, $timeout, haGlobals, $q, $rootScope) {
		var HaPrimaryNavAccountMenuLink = function ($scope, $el, $attrs) {

			// set theme (coloration)
			$scope.theme = $attrs.theme;

			// Logged out
			if (window.returnUrlQs !== undefined && !$rootScope.isMobile) {

				$scope.retURL = window.returnUrlQs;

				// work around for known issue where scs-* directives don't fetch string in time
				$scs.get('Header.SignInText').then(function (content) {
					$scope.signInText = content;
				});

				$scs.get('Header.JoinNowText').then(function (content) {
					$scope.joinNowText = content;
				});

				$scope.navLoggedOut = true;

				return;
			}

			var accountMenuEndpoint = '/Header/GetMyAccountMenu/';
			var canceller = $q.defer();
			window.onunload = function() {
				canceller.resolve();
			};
			haGlobals('areaName', function (areaName) {
				if (areaName) {
					accountMenuEndpoint += '?area=' + areaName;
				}
			});
			$http.GET(accountMenuEndpoint, { timeout: canceller.promise }).success(function (markup) {
				// note, the markup that is returned in this endpoint can be found in
				// /Areas/Shared/Views/Renderings/Header/MyAccountMenu.cshtml for Desktop
				// /Areas/Shared/Views/Renderings/Header/MyAccountMenuMobile.cshtml for Mobile

				$el.replaceWith($compile(markup)($scope));

				$timeout(function() {
					haGlobals(['registrationUrl'], function (registrationUrl) {
						$scope.$root.registrationUrl = registrationUrl;
					});
				}, 0);
			}).error(function () {
				console.error('haPrimaryNavAccountMenu ajax call to /Header/GetMyAccountMenu/ failed');
			});

			$scope.toggleAccountMenu = function () {
				if ($scope.accountMenuIsOpen) {
					toggleAccountMenu(false);
				} else {
					toggleAccountMenu(true);
				}
			};

			$scope.toggleMyTripsMenu = function () {
				if ($scope.myTripsMenuIsOpen) {
					toggleMyTripsMenu(false);
				} else {
					toggleMyTripsMenu(true);
				}
			};

			function toggleAccountMenu(open) {
				$scope.accountMenuIsOpen = open;
			}

			function toggleMyTripsMenu(open) {
				$scope.myTripsMenuIsOpen = open;
			}

			if (!$scope.$root.isMobile) {
				$('html')
					.on('click',
						function(e) {
							if ($scope.myTripsMenuIsOpen && !$(e.target).closest('.parent').hasClass('my-trips')) {
								$timeout(function() {
									toggleMyTripsMenu(false);
								});
							}

							if ($scope.accountMenuIsOpen && !$(e.target).closest('.parent').hasClass('my-account')) {
								$timeout(function() {
									toggleAccountMenu(false);
								});
							}
						});

				$('body')
					.delegate('a:not(.my-account)',
						'focus',
						function() {
							if ($scope.accountMenuIsOpen) {
								$timeout(function() {
									toggleAccountMenu(false);
								});
							}
						})
					.delegate('a:not(.my-trips)',
						'focus',
						function() {
							if ($scope.myTripsMenuIsOpen) {
								$timeout(function() {
									toggleMyTripsMenu(false);
								});
							}
						})

				$('body')
					.on('keyup',
						function(e) { // -- escape key
							if (e.keyCode === 27) {
								e.preventDefault();
								if ($scope.myTripsMenuIsOpen) {
									$timeout(function() {
										$('a.my-trips').first().focus();
										toggleMyTripsMenu(false);
									});
								}
								if ($scope.accountMenuIsOpen) {
									$timeout(function() {
										$('a.my-account').first().focus();
										toggleAccountMenu(false);
									});
								}
							}
						});
			}

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaPrimaryNavAccountMenuLink,
			templateUrl: haConfig.getTemplateUrl('ha-primary-nav-account-menu.html') // default logged-out markup; will get replaced for auth'd users, as seen above
		};
	}]);

})(angular);
;
(function (angular) {

    // Ha Primary Nav Alerts
    // --------------------------------------------
    //
    // * **Class:** HaPrimaryNavAlerts
    // * **Author:** Cory Shaw
    //
    // Makes an ajax call to an endpoint that returns cached html to improve the backend performance of the global header

    'use strict';

    var module = angular.module('haPrimaryNavAlertsModule', []);

    module.directive('haPrimaryNavAlerts', ['haHttpService', '$compile', '$location', function ($http, $compile, $location) {
        var HaPrimaryNavAlertsLink = function ($scope, $el) {
            var url = $location.absUrl();
            var queryString = url.indexOf("?") > -1 ? url.split("?")[1] : "";
            var alertsEndpoint = '/Header/GetAlerts/';
            if (queryString !== "") {
                alertsEndpoint += "?" + queryString;
            }
            $http.GET(alertsEndpoint).success(function (markup) {
                // note, the markup that is returned in this endpoint can be found in
                // /Areas/Shared/Views/Renderings/Header/Alerts.cshtml

                $el.replaceWith($compile(markup)($scope));
            }).error(function () {
                console.error('haPrimaryNavAlerts ajax call to /Header/GetAlerts/ failed');
            });

        };

        return {
            restrict: 'A',
            scope: true,
            link: HaPrimaryNavAlertsLink
        };
    }]);

})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haSigninPromptModule', []);

	module.directive('haSigninPrompt', ['haConfig', '$timeout', '$rootScope', function (haConfig, $timeout, $rootScope) {
		var haSigninPromptLink = function ($scope, $el, $attrs) {

			// don't display if switch is set to false
			if (!window.HA.SCSwitches['paxinfo:signinprompt']) {
				return;
			}

			// don't display if logged in
			if ($rootScope.isLoggedIn) {
				return;
			}

			// don't display except for Pax pages
			if ($scope.stickyBarContext !== 'bookpax') {
				return;
			}

			// display
			if (window.returnUrlQs !== undefined) {
				$scope.retURL = window.returnUrlQs;
			}

			$scope.showPrompt = function(e) {
				$scope.promptVisible = true;
				$scope.$broadcast('signinPromptShown');
			};

			$scope.hidePrompt = function(e) {
				// e.preventDefault();
				$scope.promptVisible = false;
				$scope.$broadcast('signinPromptHidden');
			};

			$scope.showPrompt()
			$timeout($scope.hidePrompt, 5000);
		};

		return {
			restrict: 'A',
			scope: true,
			link: haSigninPromptLink,
			templateUrl: haConfig.getTemplateUrl('ha-signin-prompt.html')
		};
	}]);

})(angular);
;
(function () {

	'use strict';

	function bodyController($scope, $rootScope, haConfig, $interval, $q, haModal, $scs, haSessionTimeoutApi, $location, $log) {

		// Session handling
		initializeSession($scope, $rootScope, haConfig, $interval, $q, haModal, $scs, haSessionTimeoutApi, $location, $log);

		// Initialize ARIA injector
		initializeAriaInjector($scs, $log);

	}

	function initializeAriaInjector($scs, $log) {
		$scs.get('Accessibility').then(function (data) {
			var regex = data.externallinkregex,
				subDomainRegex = data.subdomainexcludes,
				selector = data.cssselector,
				message = data.externallinkmessage,
				re,
				subdomainRe;


			try {
				re = new RegExp(regex, 'i');
				subdomainRe = new RegExp(subDomainRegex, 'i');
			} catch (err) {
				$log.error('invalid regex from sitecore for accessibility');
			return;
		}

			var $elements = $(selector);
			if (!$elements.length) { return; }

			$elements.each(function () {
				var $link = $(this),
					href = $link.prop('href');

				if (!href || (re.test(href) && !subdomainRe.test(href))) {
					return;
				}

				$link.append($('<span class="sr-only">{0}</span>'.format(message)));
			});
		});
	}

	function initializeSession($scope, $rootScope, haConfig, $interval, $q, haModal, $scs, haSessionTimeoutApi, $location, $log) {

		var sessionIsOn = $rootScope.$switch('Global:EnableSessionTimeout'),
			sessionIsOnBookingPath = $rootScope.$switch('Global:EnableSessionTimeoutBookingPath'),
			sessionIsOnMyAccount = $rootScope.$switch('Global:EnableSessionTimeoutMyAccount'),
			isBookingPath = /^\/book/i.test(location.pathname),
			isMyAccount = /^\/my-account/i.test(location.pathname);

		$scope.showGlobalHeader = true;

		// Is it turned on or homepage
		if (!sessionIsOn || (isBookingPath && !sessionIsOnBookingPath) || (isMyAccount && !sessionIsOnMyAccount) || /^\/$/.test(location.pathname)) {
			return;
		}

		// Booking Path Exceptions
		if (isBookingPath) {
			if (/\/flights|\/home|\/activities-and-cruises|\/hotels|\/car-rentals|\/vacation-packages|\/confirmation|\/expertbooking/i.test(location.pathname)) {
				return;
			} else {
				$scope.showGlobalHeader = false;
			}
		}

		// My-Account Path Exceptions
		if (isMyAccount) {
			if (/\/login|\/hawaiianmiles|\/join-hawaiianmiles/i.test(location.pathname)) {
				return;
			}
		}

		var stop,
			timerRunning = true,
			modalSessionRemainingTime,
			startOverMyAccount = '/book/shared/startover?redirect=myacount',
			startOverBookHome = '/book/shared/startover?redirect=home',
			startOverFlightHome = '/Book/Home/Index',
			myAccountUrl = '/my-account';

		$scope.userTimeLeft = 0;
		$scope.myAccountAccess = '';

		// 20 minutes by default
		$scope.userTimeLeft = 1200000;

		// Booking path or my account
		if (isBookingPath || isMyAccount) {
			$scs.get('SessionExpiration').then(function (data) {
				modalSessionRemainingTime = parseInt(data.sessionseconds, 10) * 1000;
				stop = $interval(intervalCounter, 1000, 100000, false);
			});
		}

		$scope.showSessionTimeoutWarningModal = function () {
			haModal(haConfig.getTemplateUrl('ha-session-timeout-warning.html'), {
				id: 'session-timeout-modal',
				backdrop: 'true',
				modalLock: true,
				scope: $scope,
				extendScope: { userTimeLeft: $scope.userTimeLeft },
				size: 'modal-md'
			});
		};

		$scope.stopSessionCounter = function () {
			if (angular.isDefined(stop)) {
				$interval.cancel(stop);
				stop = undefined;
				timerRunning = false;
			}
		};

		$scope.resumeSession = function () {
			return restartSessionTime().then(function (response) {
				if (angular.isDefined($scope.$modalCancel)) $scope.$modalCancel();
			});
		}

		$scope.$on('$destroy', function () {
			// Make sure that the interval is destroyed
			$scope.stopSessionCounter();
		});

		$rootScope.$on('$locationChangeStart', function (e, newUrl, oldUrl) {
			// Bail out if the referrer was not a hashed url
			if (!/\#/.test(oldUrl)) {
				return;
			}
			$scope.myAccountAccess = window.location.href.search('dashboard');
			if (timerRunning) {
				restartSessionTime();
			}
		});

		$scope.accountPageRedirect = function () {
			$scope.isLoggedIn = true;
			window.location.href = myAccountUrl;
		};

		$scope.resetSession = function () {
			window.location.href = (window.location.pathname === myAccountUrl) ? startOverMyAccount : startOverBookHome;
		};

		$scope.restartBooking = function () {
			window.location.href = startOverFlightHome;
		};

		function intervalCounter() {
			if ($scope.userTimeLeft > modalSessionRemainingTime) {
				$scope.myAccountAccess = window.location.href.search('dashboard');
				$scope.userTimeLeft -= 1000;
			}
			else if ($scope.userTimeLeft === modalSessionRemainingTime) {
				$scope.showSessionTimeoutWarningModal();
				$scope.userTimeLeft -= 1000;
			}
			else if ($scope.userTimeLeft <= modalSessionRemainingTime && $scope.userTimeLeft > 0) {
				$scope.userTimeLeft -= 1000;
			}
			else {
				$scope.stopSessionCounter();
			}
			$scope.$digest();
		}

		function redirectToErrorPage(errorCode) {
			window.location.href = '/Book/Error?ErrorCode=' + errorCode;
		}

		function restartSessionTime() {
			return haSessionTimeoutApi.restartUserSessionTime()
				.then(function (response) {
					if ( !response || response.status !== 200 || !response.data || isNaN(+response.data) ) {
						redirectToErrorPage('SessionExtendFailed');
					} else {
						$scope.userTimeLeft = +response.data * 1000 * 60;
					}
				})
				.catch(function (error) {
					$log.error('ExtendSession API call Failed: ', error);
					//redirectToErrorPage('SessionExtendFailed');
					// Cancel the redirect here as this causes much BK100 traffic from BOTS
				});
		};
	}

	angular.module('haBodyExtensionModule', []).directive('body', function () {
		return {
			restrict: 'E',
			scope: true,
			controller: bodyController
		};
	});

	bodyController.$inject = ['$scope', '$rootScope', 'haConfig', '$interval', '$q', 'haModal', 'haSitecoreStrings', 'haSessionTimeoutAPI', '$location', '$log'];

})();
;
(function (angular) {

	'use strict';

	// Aiport or Address Type-ahead input
	// ===================================
	//
	// * **Class:** haAirportOrAddressInputModule
	// * **Author:** Jamie Perkins
	//
	// Type-ahead input which autocompletes airports and addresses
	//
	// ---- IMPORTANT: DEPENDENCY ----
	// you must use haUtils.injectScriptDependency() in a parent directive to load the following script:
	// https://maps.googleapis.com/maps/api/js?key=AIzaSyDhFgUSHBggq68VqQGtWJyhpFY2g-ouiV4&libraries=places

	var module = angular.module('haAirportOrAddressInputModule', ['haCitiesModule', 'ui.bootstrap.typeahead.ha']);

	module.directive('haAirportOrAddressInput', ['haConfig', '$timeout', 'haCitiesSvc', '$q', '$parse', '$log',
		function (haConfig, $timeout, haCitiesSvc, $q, $parse, $log) {

		return {
			templateUrl: haConfig.getTemplateUrl('ha-airport-or-address-input-template.html'),
			restrict: 'A',
			scope: {
				ngModel: '=',
				name: '@',
				label: '@',
				placeholder: '@',
				required: '@',
				disabled: '@'
			},
			link: function ($scope, $el, $attrs) {

				var autocompleteService,
					placesService,
					bounds,
					geocodeService;

				function handleError(errMsg) {
					$log.error(errMsg);
					$scope.Error = errMsg;
				}

				// google services defined by dependency script

				function defineGoogleServices() {
					if (google && google.maps && google.maps.places) {
						autocompleteService = new google.maps.places.AutocompleteService();
						geocodeService = new google.maps.Geocoder();
						placesService = new google.maps.places.PlacesService($('#'+$scope.name+'-details')[0]);
						// rough rectangle around where HA flies to restrict results
						bounds = {
							east: -66.828,
							north: 49.0324,
							south: -39.4678,
							west: 100.0187,
						};
						$scope.Error = '';
					} else {
						handleError('Google API service not yet loaded or is not linked');
					}
				}

				function getPlacesResultsForInput(userInput) {
					var deferred = $q.defer();

					if (!autocompleteService) {
						defineGoogleServices();
					}
					autocompleteService.getPlacePredictions({ input: userInput, bounds: bounds/*, types: ['address'] */}, function (predictions, status) {

						if (status !== google.maps.places.PlacesServiceStatus.OK &&
							status !== google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
							deferred.reject('Google Places API status: '+status);
						}
						var result = [];
						if (predictions && predictions.length) {
							result = predictions.slice();
						}
						deferred.resolve(result);
					});
					return deferred.promise;
				}

				function getCountryCodeFromGeocodedResults(results) {
					for (var i = 0, len = results.address_components.length; i < len; i++) {
						var component = results.address_components[i];
						for (var j = 0, jlen = component.types.length; j < len; j++) {
							if (component.types[j] === 'country') {
								return component.short_name;
							}
						}
					}
					return '';
				}

				// cities

				function getAirportResultsForInput(userInput) {
					return haCitiesSvc.getMatchingCities(userInput);
				}

				// handling results

				$scope.getComboResultsForInput = function(userInput) {
					var deferred = $q.defer();

					$q.all([getAirportResultsForInput(userInput), getPlacesResultsForInput(userInput)]).then(function(resolved) {

						var result = [],
							airports = resolved[0],
							places = resolved[1];

						if (airports.length) {
							airports[0].firstAirportResult = true; // for section header
							result = result.concat(airports);
						}
						if (places.length) {
							var cleanPlaces = [];
							// construct new array to only use properties we need
							for (var i = 0, len = places.length; i < len; i++) {
								cleanPlaces.push({
									DisplayName: places[i].description,
									isAddress: true,
									placeId: places[i].place_id
								});
							}
							cleanPlaces[0].firstPlacesResult = true; // for section header
							cleanPlaces[0].isFromGoogle = true;
							result = result.concat(cleanPlaces);
						}

						deferred.resolve(result);

					}, function(error) {
						deferred.reject(error);
					});

					return deferred.promise;
				};

				$scope.onSelect = function () {

					if ($scope.ngModel.isAddress) {
						// get lat and long
						placesService.getDetails({ placeId: $scope.ngModel.placeId }, function(results, status) {
							//$log.debug('place details', status, results);
							if (status === 'OK') {
								var lat = results.geometry.location.lat(),
									long = results.geometry.location.lng();
								$scope.ngModel.geocode = {
									lat: lat,
									long: long,
								};
								$scope.ngModel.countryCode = getCountryCodeFromGeocodedResults(results);
							} else {
								handleError('PlacesService error:', status);
							}
						});

						/*  not using geocode service bc it can't find many addresses
							even though they came from google
						geocodeService.geocode({ address: $scope.ngModel.DisplayName }, function(results, status) {
							if (status === 'OK' && results.length) {
								var lat = results[0].geometry.location.lat(),
									long = results[0].geometry.location.lng();
								$scope.ngModel.geocode = {
									lat: lat,
									long: long,
								};
								$scope.ngModel.countryCode = getCountryCodeFromGeocodedResults(results);
							} else {
								handleError('geocoding error:', status);
							}
						});*/
					}
					$el.find('input[type="text"]').focus();

					$scope.$emit('airportOrAddressInputChanged', {
						model: $scope.ngModel,
						name: $scope.name
					});
				};

				$scope.onFocus = function() {
					// scroll input to top on mobile
					if ($scope.$root.isMobile) {
						$('html, body').animate({scrollTop: $el.offset().top - 10});
					}
				};
			}
		}
	}]);


})(angular);
;
(function (angular) {

	'use strict';

	// Hotels Aiport or Address Type-ahead input
	// ===================================
	//
    // * **Class:** haHotelsInputModule
	// * **Author:** Mark Hagelberg
	//
	// Type-ahead input which autocompletes airports and cities with Expedia typeahead API

	var module = angular.module('haHotelsInputModule', ['haCitiesModule', 'ui.bootstrap.typeahead.ha']);

	module.directive('haHotelsInput', ['haConfig', 'haUtils', 'haCitiesSvc', '$q', '$log', '$timeout',
		function (haConfig, haUtils, haCitiesSvc, $q, $log, $timeout) {

		return {
			templateUrl: haConfig.getTemplateUrl('ha-hotels-input-template.html'),
			restrict: 'A',
			scope: {
				ngModel: '=',
				name: '@',
				label: '@',
				placeholder: '@',
				required: '@',
				disabled: '@'
			},
			link: function ($scope, $el, $attrs) {
	
				function handleError(errMsg) {
					$log.error(errMsg);
					$scope.Error = errMsg;
				}

			    // Get Expedia suggest results
				function getPlacesResultsForInput(userInput) {
				    return haCitiesSvc.getHotelCities(userInput);
				}

			    // airports
				function getAirportResultsForInput(userInput) {
					return haCitiesSvc.getMatchingCities(userInput);
				}

			    // handling results
				$scope.getComboResultsForInput = function(userInput) {
				    var deferred = $q.defer();

					$q.all([getAirportResultsForInput(userInput), getPlacesResultsForInput(userInput)]).then(function(resolved) {

						var result = [],
							airports = resolved[0],
							places = resolved[1];

						if (airports && airports.length) {
						    airports = haUtils.removeDuplicatesFromArray(airports, 'DisplayName').slice(0, 5);
						    var cleanAirports = [];
						    // construct new array to only use properties we need
						    for (var i = 0, len = airports.length; i < len; i++) {
						        cleanAirports.push({
						            DisplayName: airports[i].DisplayName,
						            IsHACity: airports[i].IsHACity,
						            Code: airports[i].Code
						        });
						    }
						    cleanAirports[0].firstAirportResult = true; // for section header
						    result = result.concat(cleanAirports);
						}
						if (places && places.length) {
						    var cleanPlaces = [];
						    // construct new array to only use properties we need
							for (var i = 0, len = places.length; i < len; i++) {
								cleanPlaces.push({
								    DisplayName: places[i].regionNames.fullName,
									isAddress: true
								});
							}
							cleanPlaces = haUtils.removeDuplicatesFromArray(cleanPlaces, 'DisplayName');
							cleanPlaces[0].firstPlacesResult = true; // for section header
							result = result.concat(cleanPlaces);
						}

						deferred.resolve(result);

					}, function (error) {
					    handleError(error);
						deferred.reject(error);
					});

					return deferred.promise;
				};
				
				// fires when coming from modal window
				$scope.onSelected = function (city) {
					$scope.ngModel = city;
					$timeout(function () {
						$scope.focus();
					}, 0);
				};

				// fires when clicking or tabbing a highlighted result
				$scope.onSelect = function () {
					$timeout(function () {
						$scope.focus();
					}, 0);
				};

				$scope.onClick = function ($event) {
					// select input value on click
					$event.target.select();
				};

				$scope.onFocus = function() {
					// scroll input to top on mobile
					if ($scope.$root.isMobile) {
						$('html, body').animate({scrollTop: $el.offset().top - 10});
					}
				};

				$scope.pinClicked = function () {
					$scope.$emit('haWhereWeFlyPinClicked');
				};

				$scope.focus = function () {
					setTimeout(function () {
						$el.find('input[type="text"]').focus();
					}, 0);
				};
			}
		}
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian RV form
	// ===============================
	//
	// * **Class:** HaRvForm
	// * **Author:** Jamie Perkins
	//
	// Forms to hand user off to Motorhome Replubic.
	// Depends on inheriting ha-cartrawler-forms.js scope

	var module = angular.module('haRVFormModule', []);

	module.directive('haRvForm', ['haConfig', 'haUtils', '$log', '$http',
		function (haConfig, haUtils, $log, $http) {

			return {
				templateUrl: haConfig.getTemplateUrl('ha-rv-form.html'),
				restrict: 'A',
				link: function ($scope, $el) {
					var MAX_RV_DRIVER_AGE = 100,
						MIN_RV_DRIVER_AGE_US = 21,
						MIN_RV_DRIVER_AGE = 18;

					// extend inherited model, if necessary
					if (!$scope.model.rvCountries) {
						$scope.model.rvCountries = [];
						$scope.model.rvCities = [];
						$scope.model.rvPickUp = '';
						$scope.model.rvDropOffDisplay = 'SAME';
						$scope.model.rvDropOff = '';
						$scope.model.driverAge = '';
						$scope.model.countryOfResidence = 'US';
					}
					$scope.model.thirdPartyPartnerImage = $scope.onMauve === 'true'
						? $scope.scContent.bgrvsthirdpartypartnerimage
						: $scope.scContent.nobgrvsthirdpartypartnerimage;
					// driver ages
					$scope.driverAgesUS = [];
					for (var a = MIN_RV_DRIVER_AGE_US; a <= MAX_RV_DRIVER_AGE; a++) {
						$scope.driverAgesUS.push(a);
					}
					$scope.driverAgesElsewhere = [];
					for (var a = MIN_RV_DRIVER_AGE; a <= MAX_RV_DRIVER_AGE; a++) {
						$scope.driverAgesElsewhere.push(a);
					}

					// watch country to change driver age range
					$scope.$watch('model.rvCountry', function (newValue) {
						if (newValue && newValue === 'US') {
							$scope.driverAges = $scope.driverAgesUS;
							$scope.driverAgeLabel = $scope.scContent.driverageustext;
						} else {
							$scope.driverAges = $scope.driverAgesElsewhere;
							$scope.driverAgeLabel = $scope.scContent.driveragetext;
						}
						if (newValue) {
							$scope.model.rvDropOffDisplay = 'SAME';
						}
					});

					// set country from city list if it is not set yet
					$scope.$watch('model.rvPickUp', function (newValue) {
						if (newValue && !$scope.model.rvCountry) {
							// find country of chosen city and set country renting from
							for (var i = 0, len = $scope.model.rvCities.length; i < len; i++) {
								if ($scope.model.rvCities[i].value === newValue) {
									$scope.model.rvCountry = $scope.model.rvCities[i].countryCode;
									break;
								}
							}
						}
						if (newValue) {
							// set dropoff location if set to SAME
							if ($scope.model.rvDropOffDisplay === 'SAME') {
								$scope.model.rvDropOff = newValue;
							}
						}
					});

					/*  actual and display values exist because a location name
						must be present for dropoff_location. "Same" or empty are
						not valid values.
					*/
					$scope.$watch('model.rvDropOffDisplay', function (newValue) {
						if (newValue && newValue !== 'SAME') {
							$scope.model.rvDropOff = newValue;
						}
						else if (newValue && newValue === 'SAME') {
							if ($scope.model.rvPickUp) {
								$scope.model.rvDropOff = $scope.model.rvPickUp;
							}
						}
					});

					function countryPredicate (rvCity) {
						if (rvCity.countryCode === '') {
							return true;
						}
						if ($scope.model.rvCountry && $scope.model.rvCountry.length) {
							if (rvCity.countryCode === $scope.model.rvCountry) {
								return true;
							}
							else {
								return false;
							}
						}
						else {
							return true;
						}
					}

					/*	have to return an array with this function because IE does
						not support filter functions in ng-options */
					$scope.citiesForSelectedCountry = function(isReturn) {
						if ($scope.model.rvDestCities && $scope.model.rvReturnCities) {
							if (isReturn) {
								return $scope.model.rvReturnCities.filter(countryPredicate);
							} else {
								return $scope.model.rvDestCities.filter(countryPredicate);
							}
						} else {
							return [];
						}

					}

					function handleError(errMsg) {
						//$scope.Error = errMsg;
						$log.error(errMsg);
					}

					function getRVCountryCityList() {
						// handle errors
						if (!$scope.scContent.countrycitylisturl) {
							handleError('Missing URL for RV country and city list');
							return;
						}

						// load country-city list and establish dropdown options
						$http.get($scope.scContent.countrycitylisturl, {
		                    cache: true
						}).then(function(response) {
							if (response && response.data) {
								var locations = response.data;
								var allowedCountries = ['AU', 'NZ', 'US'];
								var countries = [];
								for (var i = 0, len = locations.length; i < len; i++) {
									if (allowedCountries.indexOf(locations[i].CountryCode) >= 0) {
										$scope.model.rvCities.push({
											name: locations[i].Name +', '+ locations[i].State,
											value: locations[i].Name,
											country: locations[i].CountryName,
											countryCode: locations[i].CountryCode
										});
										if (countries.indexOf(locations[i].CountryCode) < 0) {
											$scope.model.rvCountries.push({
												name: locations[i].CountryName,
												code: locations[i].CountryCode
											});
											countries.push(locations[i].CountryCode);
										}
									}
								}
								$scope.model.rvDestCities = $scope.model.rvCities.slice();
								$scope.model.rvDestCities.unshift({
									name: $scope.scContent.rvlocationplaceholdertext,
									value: '',
									countryCode: ''
								});
								$scope.model.rvReturnCities = $scope.model.rvCities.slice();
								$scope.model.rvReturnCities.unshift({
									name: $scope.scContent.sameaspickuptext,
									value: 'SAME',
									countryCode: ''
								});
							}
							else {
								handleError('Error getting RV cities: incomplete response');
							}
						}, function (error) {
							handleError('Error getting RV cities: '+error);
						});
					}

					// init
					if ($scope.model.rvCountries.length === 0) {
						getRVCountryCityList();
					}
				}
			};

		}]);

})(angular);
;
(function (angular) {
	'use strict';

	// HA Persisent Native App Banner Directive
	var module = angular.module('haPersistentNativeAppBannerModule', ['ngCookies']);

	module.directive('haPersistentNativeAppBanner', ['haConfig', function (haConfig) {
		return {
			restrict: 'A',
			scope: {
				dismissalCookie: '=',
				keepHiddenLengthDays: '=',
				header: '=',
				linkText: '='
			},
			templateUrl: haConfig.getRazorTemplateUrl('_PersistentNativeAppBannerPartial'),
			controller: 'haPersistentNativeAppBannerController'
		};
	}]);

	module.controller('haPersistentNativeAppBannerController', ['$scope', '$cookies', 'haConfig', function ($scope, $cookies, haConfig) {

		$scope.dismiss = function ($event) {
			$event.preventDefault();
			$scope.hideAppBanner = true;
			createDismissalCookie();
		}

		function init() {
			$scope.hideAppBanner = !!$cookies.get($scope.dismissalCookie);
		}

		function createDismissalCookie() {
			var DEFAULT_DAYS = 180;
			var cookieDays = !$scope.keepHiddenLengthDays || isNaN(+$scope.keepHiddenLengthDays) ? DEFAULT_DAYS : +$scope.keepHiddenLengthDays;
			var expiration = new Date();
			var options = { path: '/' }; // Do not restrict cookie to subdomain, so it can be detected sitewide

			expiration.setDate(expiration.getDate() + cookieDays);
			options.expires = expiration;

			$cookies.put($scope.dismissalCookie, true, options);
		}

		init();

	}]);

})(angular);
;
(function (angular) {
	'use strict';
	var module;
	try {
		module = angular.module('haGiftCardModule');
	} catch (e) {
		module = angular.module('haGiftCardModule', []);
	}

	module.directive('haGiftcardPurchase', ['haHttpService', function (haHttpService) {
		return {
			restrict: 'A',
			scope: true,
			link: function ($scope) {
				$scope.showInputRow = true;
				$scope.maxGiftCardAllowed = 4;

				$scope.addCard = function () {
					$scope.giftCardForm.$validate();
					if (!$scope.giftCardForm.$valid) return;

					var payload = {
						CardNumber: $scope.giftCard.Number,
						Pin: $scope.giftCard.Pin
					};
					if (_.some($scope.giftCards, 'Number', payload.CardNumber)) {
						return $scope.giftCardForm.gcNumber.$setValidity('duplicate', false);
					}

					$scope.checkingGiftCard = true;
					haHttpService.POST('/MyAccount/GiftCard/GiftCardBalance', JSON.stringify(payload)).then(
						function (response) {
							var card = response.data;
							response.data.Number = $scope.giftCard.Number;

							if (response.data.Status === 'Active' && card.BalanceAmount !== 0) {
								card.AmountApplied = Math.min(card.BalanceAmount, $scope.giftCardState.balance);
								$scope.giftCardState.balance -= card.AmountApplied;
								card.BalanceAmount -= card.AmountApplied;

								response.data.Pin = payload.Pin;
								$scope.giftCards.push(response.data);
								$scope.giftCardForm.$setPristine();
								$scope.showInputRow = false;
							} else {
								$scope.giftCardForm.gcNumber.$setValidity('invalidcard', false);
							}
						},
						function () {
							// This typically won't happen, since our servers respond with 200 codes even with an error.
							$scope.giftCardForm.gcNumber.$setValidity('invalidcard', false);
						}
					).finally(function () {
						$scope.checkingGiftCard = false;
					});
				};

				$scope.addAnother = function () {
					// Reset form state
					$scope.giftCard.Number = '';
					$scope.giftCard.Pin = '';
					$scope.giftCardForm.$setUntouched();
					$scope.showInputRow = true;
				};

				function recalculateBalances() {
					for (var i = 0; i < $scope.giftCards.length; i++) {
						var card = $scope.giftCards[i];
						if ($scope.giftCardState.balance >= card.BalanceAmount) {
							var appliedDelta = Math.min(card.BalanceAmount, $scope.giftCardState.balance);
							card.AmountApplied += appliedDelta;
							$scope.giftCardState.balance -= appliedDelta;
							card.BalanceAmount -= appliedDelta;
						}
					}
				}

				$scope.removeCard = function (card) {
					var removed = $scope.giftCards.splice($scope.giftCards.indexOf(card), 1);
					if (removed.length) {
						// Recalculate
						$scope.giftCardState.balance += removed[0].AmountApplied;
						recalculateBalances();
					}
					if ($scope.giftCards.length === 0) {
						$scope.showInputRow = true;
					}
				}

				$scope.$watch('giftCard.Number', function (n, o) {
					$scope.giftCardForm.gcNumber.$setValidity('invalidcard', true);
					$scope.giftCardForm.gcNumber.$setValidity('duplicate', true);
				});
			}
		};
	}]);
})(angular);
;
(function (angular) {
	'use strict';
	var module;
	try {
		module = angular.module('haGiftCardModule');
	} catch (e) {
		module = angular.module('haGiftCardModule', []);
	}

	module.directive('haGiftcardBalance', ['haModal', 'haHttpService', '$rootScope', 'haConfig', 'haSitecoreStrings',
	function (haModal, haHttpService, $rootScope, haConfig, $scs) {

		return {
			restrict: 'A',
			scope: true,
			link: function ($scope) {

				var endpoints = {
					lookup: '/MyAccount/GiftCard/GiftCardBalance'
				};


				$scope.loading = true;

				$scs.get('gift_card_balance').then(function (data) {
					$scope.strings = data;
					$scope.loading = false;
				})
					['catch'](function () {
						$scope.loading = false;
					});

				$scope.currentCard = undefined;
				$scope.error = false;
				$scope.serviceError = false;
				$rootScope.formData = {};

				$scope.openGiftCardBalanceModal = function () {
					$scope.currentCard = undefined;
					$scope.error = false;
					$scope.serviceError = false;
					$scope.loading = false;
					$rootScope.formData = {};
					haModal(haConfig.getTemplateUrl('ha-giftcard-balance-modal.html'), {
						id: 'giftCardBalanceModal',
						backdrop: 'true',
						scope: $scope
					});
				};

				$scope.setForm = function (form) {
					$scope.gcBalanceForm = form;
				};

				$scope.submitSearch = function () {

					var payload = {
						CardNumber: $rootScope.formData.number,
						Pin: $rootScope.formData.pin
					};
					$scope.loading = true;
					haHttpService.POST(endpoints.lookup, JSON.stringify(payload)).then(
						function (response) {
							if (response.data && response.data.IsSuccess) {
								$scope.currentCard = response.data;
								$scope.serviceError = false;
								$rootScope.formData.number = '';
								$rootScope.formData.pin = '';
								$scope.gcBalanceForm.$setPristine();

								$scope.gcBalanceForm.CardNumber.$touched = false;
								$scope.gcBalanceForm.PinNumber.$touched = false;
								$scope.gcBalanceForm.$submitted = false;
								$scope.gcBalanceForm.$setPristine();
								$('form[name=' + $scope.gcBalanceForm.$name + ']').removeClass('submitted');
							}
							else {
								$scope.currentCard = undefined;
								$scope.serviceError = true;
								$scope.strings.serviceErrorMessage = response.data.ErrorMessage || response.data.TranslateServiceError;
							}
							$scope.error = false;
						},
						function () {
							$scope.error = true;
							$scope.serviceError = false;
						}
					)['finally'](function () {
						$scope.loading = false;
					});

				};

				// listen for alert being closed
				$scope.$on('haAlertClosed', function (event, alertId) {
					if (alertId === 'balanceCheckErrorAlert') {
						// reset alert
						setTimeout(function () {
							$scope.serviceError = false;
							$scope.$broadcast('$showAlert', 'balanceCheckErrorAlert');
						}, 1000);
					}
				});
			}
		};
	}]);
})(angular);
;
(function (angular) {
	'use strict';

	var mod = angular.module('haPairLinksModule', []);
	mod.directive('pairLinksMenu', ['$window', '$timeout', function ($window, $timeout) {
		return {
			restrict: 'A',
			scope: true,
			link: function ($scope, $el, $attrs, $ctrl) {
				var selectA = $el.find('select').first();
				var selectB = $el.find('select').last();
				var formController;

				$attrs.$observe('name', function(val) {
					formController = $scope[val];
				});
				$attrs.$observe('listA', function(val) {
					val = JSON.parse(val);
					selectA.html(buildSelect(val));
					$scope.listA = val;
				});
				$attrs.$observe('listB', function(val) {
					val = JSON.parse(val);
					selectB.html(buildSelect(val));
					$scope.listB = val;
				});
				$attrs.$observe('pairs', function(val) {
					$scope.pairs = JSON.parse(val);
					$scope.pairMap = buildPairMap($scope.pairs);
				});
				$attrs.$observe('defaultA', function(val) {
					$timeout(function() {
						selectA.val(val.Id).change();
					});
				});
				$attrs.$observe('defaultB', function(val) {
					$timeout(function() {
						selectB.val(val.Id).change();
					});
				});

				$scope.$watchGroup(['itemA', 'itemB'], function(nv) {
					formController.itemA.$setValidity('notfound', true);
					formController.itemB.$setValidity('notfound', true);
				});
				$scope.go = function(event) {
					event.preventDefault();
					var link = $scope.pairMap[$scope.itemA + $scope.itemB];
					if (link && link.Url) {
						// link pair found
						var url = link.Url;
						var target = link.Target;
						$window.open(url, (target || '_self'));
					} else {
						formController.itemA.$setValidity('notfound', false);
						formController.itemB.$setValidity('notfound', false);
					}
				};

				function buildPairMap(pairs) {
					var pairMap = {};
					angular.forEach(pairs, function(val) {
						pairMap[val.ItemA + val.ItemB] = val.Link;
					});
					return pairMap;
				}
				function buildSelect(list) {
					var listMarkup = '';
					var inGroup = false;
					angular.forEach(list, function(val, key) {
						if ((val.IsGroupHeader || val.IsTopLevel) && inGroup) {
							listMarkup += '</optgroup>';
							inGroup = false;
						}
						if (val.IsGroupHeader) {
							listMarkup += '<optgroup label="' + val.Label + '">';
							inGroup = true;
						} else {
							listMarkup += '<option value="' + val.Id + '">' + val.Label + '</option>';
						}
					});
					if (inGroup) {
						listMarkup += '</optgroup>';
						inGroup = false;
					}
					return listMarkup;
				}
			}
		};
	}]);
})(angular);
;
(function (angular) {

	// * **Author:** Cory Shaw / Steven Tate
	//
	// Simple logic to invalidate KTN/Redress country dropdowns they are invalid as a whole.

	'use strict';

	var module = angular.module('haPaxCountryModule', []);

	module.directive('haPaxCountry', ['haDateUtils', '$parse', function (haDateUtils, $parse) {

		return {
			restrict: 'A',
			require: 'ngModel',
			link: function ($scope, $el, $attrs, ngModel) {

				var validCountrySelection = function (dropdown, number) {
					if (number != 'undefined' && number != null && number != "") {
						if (dropdown == null || dropdown === '' || dropdown === 'undefined') {
							return 0;
						}
						else {
							return 2;
						}
					}
					else if (dropdown != 'undefined' && dropdown != null && dropdown != "") {
					    if (number == null || number === '' || number === 'undefined') {
					        return 1;
					    }
					    else {
					        return 2;
					    }
					}
					else {
						return 2;
					}
				};

				var validate = function (viewValue) {

					var dropdown = $parse($attrs.dropdownModel)($scope);
					var number = $parse($attrs.numberModel)($scope);
					var isValid = validCountrySelection(dropdown, number);

                    if(isValid == 0)
                        ngModel.$setValidity('haPaxCountry', false);
                    else {
                        ngModel.$setValidity('haPaxCountry', true);
                    }

					return viewValue;
				};

				ngModel.$parsers.unshift(validate);
				ngModel.$formatters.push(validate);

				$scope.$watch($attrs.dropdownModel, function () {
					return validate(ngModel.$viewValue);
				});
				$scope.$watch($attrs.numberModel, function () {
					return validate(ngModel.$viewValue);
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// HA Name
	// --------------------------------------------
	//
	// * **Class:** haName
	// * **Author:** Nathan Probst
	//
	// A directive to allow interpolation of name attributes on form inputs

	'use strict';

	var module = angular.module('haName', []);

	// NEP: TODO: Move this.
	module.directive('haName', ['$compile', '$interpolate', function ($compile, $interpolate) {
		return {
			restrict: 'A',
			terminal: true,
			priority: 100000,
			scope: false,
			link: function ($scope, $el) {
				var attr = $el.attr('ha-name');
				var name = $interpolate(attr, false, null, true)($scope);
				$el.removeAttr('ha-name');
				$el.attr('name', name);
				$compile($el)($scope);
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// @scniro

	'use strict';

	var module = angular.module('haCustomDropdownModule', []);

	module.directive('haCustomDropdown', ['haConfig', '$timeout', function (haConfig, $timeout) {
		return {
			restrict: 'A',
			scope: {
				label: '@'
			},
			transclude: true,
			templateUrl: haConfig.getTemplateUrl('ha-custom-dropdown-base-template.html'),
			link: function (scope, elem) {

				var sender = angular.element([]); // empty element selector. guard against possible undefined.focus(); error

				function onFocus(event) {
					if (!elem.has(event.target).length) {
						$timeout(scope.customDropdownDeactivate);

						if (!$(event.target).is(':focusable')) {
							// focus to sender when target is non-focusable e.g. body, <div> etc. -- sample use case: allow expected focus shift to <input /> element. jQuery UI dependency.
							sender.focus();
						}
					}
				}

				function onKey(event) {
					if (event.keyCode === 27) { // esc key
						event.stopPropagation();
						$timeout(scope.customDropdownDeactivate);
						sender.focus();
					}
				}

				scope.customDropdownActivate = function () {
					angular.element('body')
					.on('focusin touchstart click', onFocus); // attach locally scoped handlers

					angular.element(elem)
					.on('keydown', onKey);

					scope.active = true;
				};

				scope.customDropdownDeactivate = function () {
					angular.element('body')
					.off('focusin touchstart click', onFocus); // detach locally scoped handlers

					angular.element(elem)
					.off('keydown', onKey);

					scope.active = false;
				};

				scope.toggleCustomDropdownActive = function ($event) {
					sender = angular.element($event.target);
					if (scope.active) {
						scope.customDropdownDeactivate();
					} else {
						scope.customDropdownActivate();
					}
				};

				scope.$on('$closeCustomDropdown', function () {
					sender.focus(); // focus on custom close action e.g. avatar selection <button ng-click="closeAvatarSelector()">done</button>
					scope.customDropdownDeactivate();
				});

				scope.$on('$openCustomDropdown', function () {
					scope.customDropdownActivate();
				});
			}
		};
	}]);
})(angular);
;
(function (angular) {

	// * **Author:** Cory Shaw / Steven Tate
	//
	// Simple logic to invalidate day/month/year dropdowns for a birthdate if they are invalid as a whole.

	'use strict';

	var module = angular.module('haBirthdateModule', []);

	module.directive('haBirthdate', ['haDateUtils', '$parse', function (haDateUtils, $parse) {

		return {
			restrict: 'A',
			require: 'ngModel',
			link: function ($scope, $el, $attrs, ngModel) {


				var validate = function (viewValue) {

					var month = $parse($attrs.monthModel)($scope);
					var day = $parse($attrs.dayModel)($scope);
					var year = $parse($attrs.yearModel)($scope);
					var isValid = haDateUtils.isValid(year, month, day) && haDateUtils.isBefore(new Date(year, month - 1, day), new Date());

					ngModel.$setValidity('haBirthdate', isValid);

					return viewValue;
				};

				ngModel.$parsers.unshift(validate);
				ngModel.$formatters.push(validate);

				$scope.$watch($attrs.monthModel, function () {
					return validate(ngModel.$viewValue);
				});
				$scope.$watch($attrs.dayModel, function () {
					return validate(ngModel.$viewValue);
				});
				$scope.$watch($attrs.yearModel, function () {
					return validate(ngModel.$viewValue);
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Form Validation Directive
	// --------------------------------------------
	//
	// * **Class:** HaFormValidation
	// * **Author:** Cory Shaw
	//
	// Highlights and focuses errous form fields on submit

	'use strict';

	var module = angular.module('haFormValidationModule', []);

	module.directive('haFormValidation', ['$timeout', function ($timeout) {

		var HaFormValidationController = function (/* $scope */) {

		};

		HaFormValidationController.$inject = ['$scope'];

		var HaFormValidationLink = function ($scope, elem) {

			$timeout(function () {
				$scope.theFormName = elem.attr('name');
				$scope.theForm = $scope[$scope.theFormName];
				$scope.$emit('$haFormValidationReady', $scope.theForm);
				$scope.theForm.validate = $scope.fireValidation;
			}, 0);

			$scope.fireValidation = function (e) {
				$scope.$broadcast('validateForm');

				if ($scope.theForm.$valid) {
					$scope.$emit('haFormValidationSuccess', {
						'formName': $scope.theFormName,
						'formScope': $scope.theForm,
						'event': e
					});
				} else {
					var firstInValidElement = $('input.ng-invalid:first');
					var haModalParent = null;
					if ($('form[name=\'' + $scope.theFormName + '\']').get(0)) {
						firstInValidElement = $('form[name=\'' + $scope.theFormName + '\']').find('input.ng-invalid:first');
						haModalParent = $('form[name=\'' + $scope.theFormName + '\']').parents().find('.ha-modal');
					}

					if (haModalParent != null && haModalParent.get(0)) {
						var childPosTop = $(firstInValidElement).offset().top;
						var parentPosTop = $(haModalParent).offset().top;
						$(haModalParent).animate({scrollTop: (parentPosTop - childPosTop)}, 'slow');
					}
					else {
						$('body, html').animate({scrollTop: $('.ng-invalid:not(form.ng-invalid):first').offset().top - 140}, 'slow');
					}

					$timeout(function () {
						$(firstInValidElement).focus();
					}, 300);

					if (e && e.preventDefault) {
						e.preventDefault();
					}
				}
			};

			$scope.safeApply = function (fn) {
				var phase = this.$root.$$phase;
				if (phase === '$apply' || phase === '$digest') {
					if (fn && (typeof (fn) === 'function')) {
						fn();
					}
				} else {
					this.$apply(fn);
				}
			};
			//Added for pristine
			elem.on('reset', function () {
				$scope.$broadcast('pristine');
				$scope.theForm.$setPristine();
			});

			elem.on('submit', $scope.fireValidation);
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaFormValidationLink,
			controller: HaFormValidationController
		};
	}]);
})(angular);
;
(function (angular) {

	'use strict';

	// Ha Password Strength Directive
	// --------------------------------------------
	//
	// * **Class:** HaPasswordStrength
	// * **Author:** Cory Shaw, Nathan Probst & Jamie Perkins
	//
	// When applied to input fields, typing in the field will reveal a password strength indicator

	// Passwords requirements:
	//• Must be 10 – 16 characters in length
	//• Must have at least 1 lower case letter
	//• Must have at least 1 upper case letter
	//• Must have at least 2 letters  (sort of repetitive if we are already requiring the two above)
	//• Must have at least 1 number
	//• Can’t be the same as the email (anything before the @ symbol – so can’t be “asdf123” if email is asdf123@gmail.com)
	//• Can’t contain HM Number

	//
	// In-line Password Strength Check will display password strength as user types, including the following states/rules:
	// 1. Too Short
	// 2. Weak: 10 characters or more, but either all numeric or all alpha, or derivative of Username*
	// 4. Good: 10 characters or more, combination of alpha and numeric, and NOT derivative of Username*
	// 5. Strong: 10 characters or more, combination of alpha and numeric, and NOT derivative of Username*, special characters/punctuation
	//
	// Only Good and Strong Passwords will be accepted.
	//
	// * "derivative of Username" is defined as (Levenshtein Distance < 5).


	// SEE: http://en.wikipedia.org/wiki/Levenshtein_distance
	// SEE: http://andrew.hedges.name/experiments/levenshtein
	var levenshtein = (function () {
		function min(x, y, z) {
			if (x < y && x < z) {
				return x;
			}
			if (y < x && y < z) {
				return y;
			}
			return z;
		}

		return function (a, b) {
			// Degenerate cases
			if (a === b) {
				return 0;
			}
			if (a.Length === 0) {
				return b.length;
			}
			if (b.Length === 0) {
				return a.length;
			}

			if (a.indexOf(b) > -1) {
				return 0;
			}

			var cost;
			var m = a.length;
			var n = b.length;

			if (m < n) {
				var swap;
				swap = a;
				a = b;
				b = swap;
				swap = m;
				m = n;
				n = swap;
			}

			var r = [];
			r[0] = [];
			for (var c = 0; c < n + 1; ++c) {
				r[0][c] = c;
			}

			for (var i = 1; i < m + 1; ++i) {
				r[i] = [];
				r[i][0] = i;
				for (var j = 1; j < n + 1; ++j) {
					cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
					r[i][j] = min(r[i - 1][j] + 1, r[i][j - 1] + 1, r[i - 1][j - 1] + cost);
				}
			}

			return r[r.length - 1][r[r.length - 1].length - 1];
		};
	})();

	var module = angular.module('haPasswordStrengthModule', []);

	module.directive('haPasswordStrength', ['$log', 'haRegexService', function ($log, $regex) {

		var HaPasswordStrengthController = ['$scope', function ($scope) {
			this.user = undefined;
			this.email = undefined;
			this.hmno = undefined;
			this.pass = undefined;

			$scope.$parent.pwStrength = 'NONE';

			this.test = function (ngModelCtrl) {

				ngModelCtrl.$setValidity('tooShort', true);
				ngModelCtrl.$setValidity('tooLong', true);
				ngModelCtrl.$setValidity('pattern', true);
				ngModelCtrl.$setValidity('alikeness', true);

				// $log.debug('username: ', this.user);
				// $log.debug('email: ', this.email);
				// $log.debug('hmno: ', this.hmno);

				// TOO SHORT
				if (this.pass && this.pass.length < 10) {
					ngModelCtrl.$setValidity('tooShort', false);
					$scope.$parent.pwStrength = 'SHORT';
					//$log.debug('password: '+this.pass+' - too short');
					return;
				}

				// TOO LONG
				if (this.pass && this.pass.length > 16) {
					ngModelCtrl.$setValidity('tooLong', false);
					$scope.$parent.pwStrength = 'LONG';
					//$log.debug('password: '+this.pass+' - too long');
					return;
				}

				// LIKE USERNAME / EMAIL / HM NUMBER
				var MIN_LD = 5;
				if (this.pass && this.pass.length && (this.user || this.email || this.hmno)) {
					var alikeUsername;
					var alikeEmail;
					var alikeHMNO;
					// Case-insensitive distance
					if (this.user) {
						var levenshteinUsernameDistance = levenshtein(this.pass.toUpperCase(), this.user.toUpperCase());
						alikeUsername = levenshteinUsernameDistance < MIN_LD;
					}
					if (this.email) {
						var levenshteinEmailDistance = levenshtein(this.pass.toUpperCase(), this.email.toUpperCase());
						alikeEmail = levenshteinEmailDistance < MIN_LD;
					}
					if (this.hmno) {
						var levenshteinHmnoDistance = levenshtein(this.pass.toUpperCase(), this.hmno);
						alikeHMNO = levenshteinHmnoDistance < MIN_LD;
					}

					var alikeness = (alikeUsername || alikeEmail || alikeHMNO);

					ngModelCtrl.$setValidity('alikeness', !alikeness);
					if (alikeness) {
						$scope.$parent.pwStrength = 'ALIKE';
						//$log.debug('password: '+this.pass+' - too much alikeness');
						return;
					}
				}

				// WEAK
				var ok = false;
				var pwStrength = 'NONE';
				if (this.pass && this.pass.length) {
					pwStrength = 'WEAK';
					ngModelCtrl.$setValidity('pattern', false);

					// GOOD (passes)
					if ($regex.password.test(this.pass)) {
						ngModelCtrl.$setValidity('pattern', true);
						pwStrength = 'GOOD';
						ok = true;

						// STRONG (contains special characters)
						if ((/[^A-Za-z0-9]+/).test(this.pass)) {
							pwStrength = 'STRONG';
						}

					}
				}
				$scope.$parent.pwStrength = pwStrength;
				//$log.debug('password: '+this.pass+' strength: '+pwStrength);
				return ok;

			};
		}];

		var HaPasswordStrengthLink = function ($scope, $el, $attrs, ctrls) {
			var ctrl = ctrls[0];
			var ngModelCtrl = ctrls[1];

			// watch own value and re-validate on change
			ngModelCtrl.$parsers.unshift(function (value) {
				ctrl.pass = value;
				ctrl.test(ngModelCtrl);
				return value;   // pass-through
			});

			// watch username value and re-validate on change
			$scope.$watch($attrs.usernameModel, function (value) {
				if (value && value.length) {
					ctrl.user = value;
					ctrl.test(ngModelCtrl);
				}
			});
			// watch email value and re-validate on change
			$scope.$watch($attrs.emailModel, function (value) {
				// only care about username portion
				if (value && value.length) {
					ctrl.email = value.substr(0, value.indexOf('@'));
					ctrl.test(ngModelCtrl);
				}
			});

			// watch hmno value and re-validate on change
			$scope.$watch($attrs.hmnoModel, function (value) {
				if (value && value.toString().length) {
					ctrl.hmno = value.toString();
					ctrl.test(ngModelCtrl);
				}
			});
		};

		return {
			restrict: 'A',
			require: ['haPasswordStrength', 'ngModel'],
			link: HaPasswordStrengthLink,
			controller: HaPasswordStrengthController,
			transclude: true,
			templateUrl: ''
		};
	}]);

	module.directive('haPasswordMatch', [function () {
		return {
			require: 'ngModel',
			link: function ($scope, $el, $attrs, ctrl) {
				var p1;
				var p2;

				function test() {
					if (p1 != null && p2 != null) {
						ctrl.$setValidity('passwordMatch', (p1 === p2));
					}
				}

				// watch own value and re-validate on change
				ctrl.$parsers.unshift(function (value) {
					p1 = value;
					test();
					return value;   // pass-through
				});

				// watch password value and re-validate on change
				$scope.$watch($attrs.haPasswordMatch, function (value) {
					p2 = value;
					test();
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Window Events Directive
	// --------------------------------------------
	//
	// * **Class:** HaWindowEvents
	// * **Author:** George Pantazis
	//
	// Broadcaster for global window/body events.

	'use strict';

	var module = angular.module('haWindowEventsModule', []);

	module.directive('body', function () {

		var HaWindowEventsLink = function ($scope, $el) {

			$el.on('click', function (e) {
				$scope.$root.$emit('HaWindow:clicked', e);
			});

			$scope.$emit('$haWindowEventsReady');
		};

		return {
			restrict: 'E',
			scope: false,
			link: HaWindowEventsLink
		};
	});

})(angular);
;
(function (angular) {

	// No Click Focus Directive
	// --------------------------------------------
	//
	// * **Class:** NoClickFocus
	// * **Author:** George Pantazis
	//
	// For anchors that serve as navigation, yet have focus states for accessibility, don't focus on click.

	'use strict';

	var module = angular.module('noClickFocusModule', []);

	module.directive('noClickFocus', function () {

		var NoClickFocusLink = function ($scope, $el) {
			$el.click(function () {
				$el.blur();
			});
		};

		return {
			restrict: 'A',
			scope: false,
			link: NoClickFocusLink
		};
	});

})(angular);
;
(function (angular) {

	// Ha Toggle Directive
	// --------------------------------------------
	//
	// * **Class:** HaToggle
	// * **Author:** Jamie Perkins
	//
	// UI element that represents a boolean state

	'use strict';

	var module = angular.module('haToggleModule', []);

	module.directive('haToggle', ['haConfig', function (haConfig) {
		return {
			restrict: 'A',
			scope: {
				'ngModel': '=',
				'onLabel': '=',
				'offLabel': '='
			},
			templateUrl: haConfig.getTemplateUrl('ha-toggle-base-template.html'),
			transclude: true,
			link: function ($scope, $el) {

				// console.log('ha-toggle model: ');
				// console.log($scope.ngModel);

				$el.find('input').prop('checked', $scope.ngModel);

				$scope.toggle = function () {
					$scope.ngModel = !$scope.ngModel;
					$el.find('input').prop('checked', $scope.ngModel);
				};

			}
		};
	}]);

})(angular);;
(function (angular) {

	// Ha Keyboard Directive
	// --------------------------------------------
	//
	// * **Class:** HaKeyboard
	// * **Author:** George Pantazis
	//
	// A collection of directives that abstract keyboard interactions.

	'use strict';

	var module = angular.module('haKeyboardModule', []);

	module.directive('haKeyEnter', function () {
		return function (scope, element, attrs) {
			element.bind('keydown keypress', function (event) {
				if (event.which === 13) {
					scope.$apply(function () {
						scope.$eval(attrs.haKeyEnter);
					});
					event.preventDefault();
				}
			});
		};
	});

	module.directive('haKeyUp', function () {
		return function (scope, element, attrs) {
			element.bind('keydown keypress', function (event) {
				if (event.which === 38) {
					scope.$apply(function () {
						scope.$eval(attrs.haKeyUp);
					});
					event.preventDefault();
				}
			});
		};
	});

	module.directive('haKeyDown', function () {
		return function (scope, element, attrs) {
			element.bind('keydown keypress', function (event) {
				if (event.which === 40) {
					scope.$apply(function () {
						scope.$eval(attrs.haKeyDown);
					});
					event.preventDefault();
				}
			});
		};
	});

})(angular);
;
(function (angular) {
	'use strict';
	var module = angular.module('haErrorsModule', []);
	var directive = ['haSitecoreStrings', '$timeout', function ($scs, $timeout) {

		var defaults = {
			editable: $scs.get('Forms.patternError').then(function (value) {
				return (defaults.editable = value);
			}),
			required: $scs.get('Forms.requiredError').then(function (value) {
				return (defaults.required = value);
			}),
			language: $scs.get('Forms.languageError').then(function (value) {
				return (defaults.language = value);
			}),
			pattern: $scs.get('Forms.patternError').then(function (value) {
				return (defaults.pattern = value);
			}),
			date: $scs.get('Forms.dateError').then(function (value) {
				return (defaults.date = value);
			}),
			parse: ''
		};

		return {
			require: ['?^form', '?ngModel', '?^haInput'],
			restrict: 'E',
			link: function ($scope, $el, attrs, requirements) {
				var name = $el[0].name;
				var form = requirements[0];
				var model = requirements[1];
				var haInput = requirements[2];
				var allowNonEnglish = typeof attrs.allowNonEnglish !== 'undefined';

				if ((!form || !model || haInput || !$el.is('[ha-errors]'))) {

					if (allowNonEnglish || !$el.is(':not([ha-errors]):not(select):not(:checkbox):not(:radio):not(:hidden):not(:submit):not(:button)')) {
						return;
					}


					$el.on('keydown input', function (e) {
						if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) {
							return true;
						}

						var val = $(this).val();
						var isValid = !/[^\u0000-\u007F]+/.test(val);

						if (e.type === 'input' && !isValid) {
							$el.val(val.replace(/[^\u0000-\u007F]+/g, ''));

							return true;
						}

						return isValid;
					});

					return;
				}

				if (typeof form.attempts === 'undefined') {
					form.attempts = 0;
					$($el[0].form).on('submit', validate);
				}

				var errors = model.$error;
				var messages = angular.extend({}, defaults, $scope.$eval(attrs.haErrors || '{}'));

				if (angular.version.major === 1 && angular.version.minor < 7) {
					$el.one('blur', function () {
						$scope.$evalAsync(function () {
							model.$untouched = !(model.$touched = true);
							$el.removeClass('ng-untouched').addClass('ng-touched');
						});
					});
					$el.addClass('ng-untouched');
					model.$untouched = !(model.$touched = false);
					form.$validate = validate.bind($el[0].form);
				}

				//allow the error <em>'s to be manually specified but wait for interpolation
				var $em;
				var $comment;
				$timeout(function () {

					$em = $('em[for="' + name + '"]');
					if (!$em.length) {
						$em = $('<em>').attr('for', name);
						if (!$el.is('[required]:radio,[required]:checkbox')) {
							$em.insertAfter($el);
						} else {
							$el.closest(':not(ul):not(ol):not(li):not(input)').append($em);
						}
					}

					$comment = $(document.createComment('ha-errors for=' + name)).insertBefore($em);

					// Watch text inputs and textareas
					if (!allowNonEnglish && $el.is(':not(select):not(:checkbox):not(:radio):not(:hidden):not(:submit):not(:button)')) {
						$scope.$watch(function () {
							return $el.val();
						}, function (v) {
							if (v) {
								model.$setValidity('language', !/[^\u0000-\u007F]+/.test(v));
							}
						});
					}

					$scope.$watch(prop, function (error) {
						$el.attr('aria-invalid', model.$invalid);
						$em.text('').detach();
						if (error) {
							$em.html(getMessage(error)).insertAfter($comment);
						}
					});
				});

				function validate(externalform) {

                    // for cases where submit button might live outside the form (meaning form would be otherwise undefined), allow substitution of a passed-in form instead
                    if (!form) {
                        form = externalform;
                    }

					form.attempts++;
					form.$submitted = true;

                    var formelement = $('[name="' + form.$name + '"]');

					formelement.addClass('submitted');

					$timeout(function () {
						//use timeout to force the watches to re-evaluate so error messages render
					}, 0);

					if (form.$invalid) {
						var $firstError = $('.ng-invalid:not(div):first', formelement);
						var $modal = $firstError.closest('.modalContainer');
						var $scrollEl = $modal.size() ? $modal : $('html,body')
						$scrollEl.animate({scrollTop: $firstError.offset().top - 50}, function() {
							$firstError.focus().select();
						});
					}

					return form.$valid;
				}

				function prop() {

					if (!(form.$submitted || model.$touched)) {
						return false;
					}

					if (errors.required) {
						return 'required';
					}
					if (errors.language) {
						return 'language';
					}
					if (errors.pattern) {
						return 'pattern';
					}

					for (var n in errors) {
						if (errors.hasOwnProperty(n) && errors[n]) {
							return n;
						}
					}
				}

				function getMessage(error) {
					var message = messages[error];
					if (typeof message === 'undefined') {
						console.error('Missing "' + error + '" message for input:', $el[0]);
						return 'Unknown Error'; //only happens when a dev forgot something!
					}
					if (message.then) {
						message.then(function (value) {
							$em.html(messages[error] = value);
						});
						return '...'; //what else can we do until the promise resolves?
					}

					return messages[error];
				}
			}
		};
	}
	];

	module.directive('input', directive);
	module.directive('textarea', directive);
	module.directive('select', directive);

})(angular);
;
(function (angular) {

    'use strict';

    var module = angular.module('haRestrictSpacesModule', []);

    module.directive('haRestrictSpaces', function () {
        return function ($scope, $el) {
            $el.on('keydown input', function (e) {
                return e.which !== 32; // reject space
            });
        };
    });

})(angular);
;
(function (angular) {

	// Ha Foot Note Directive
	// --------------------------------------------
	//
	// * **Class:** HaFootNote
	// * **Author:** George Pantazis
	//
	// Behavior for transposing footnotes to a data-model that can be presented
	// elsewhere in the page, primarily in HaGlobalFooter.

	'use strict';

	var module = angular.module('haFootNoteModule', []);
	var currentStatic = 0;
	var currentNumeric = 1;

	module.directive('haFootNote', ['haConfig', function (haConfig) {

		var HaFootNoteController = function ($scope, $rootScope) {

			$rootScope.footnotes = $rootScope.footnotes || {};

			$scope.addFootnote = function (text) {

				$rootScope.footnotes[$scope.type] = $rootScope.footnotes[$scope.type] || [];

				$rootScope.footnotes[$scope.type].push({
					'id': $scope.id,
					'label': $scope.label,
					'text': text
				});
			};

		};

		HaFootNoteController.$inject = ['$scope', '$rootScope'];

		var HaFootNoteLink = function ($scope, $el, $attrs) {

			$scope.setValues = function () {
				if (typeof $attrs.labelNumber === 'string') {
					$scope.type = 'numeric';
					$scope.id = currentNumeric++;
				} else {
					$scope.type = 'static';
					$scope.label = $attrs.label;
					$scope.id = String.fromCharCode(65 + (currentStatic++));
				}
			};

			$scope.setValues();
			if ($scope.DisclaimerFootNote !== '') {
				$scope.addFootnote($scope.DisclaimerFootNote);
			}
			else {
				$scope.addFootnote($el.find('[ng-transclude]').text());
			}
		};

		return {
			restrict: 'A',
			transclude: true,
			scope: true,
			link: HaFootNoteLink,
			templateUrl: haConfig.getTemplateUrl('ha-foot-note-base-template.html'),
			controller: HaFootNoteController
		};
	}]);

})(angular);
;
(function (angular) {

	// Select On Click Directive
	// --------------------------------------------
	//
	// * **Class:** SelectOnClick
	// * **Author:** Cory Shaw
	//
	// Auto selects an input field on click

	'use strict';

	var module = angular.module('selectOnClickModule', []);

	module.directive('selectOnClick', function () {
		return function (scope, element) {
			element.bind('click', function () {
				this.select();
			});
		};
	});

})(angular);
;
(function (angular) {

	// Ha Reveal On Load Directive
	// --------------------------------------------
	//
	// * **Class:** HaRevealOnLoad
	// * **Author:** George Pantazis
	//
	// Reveals elements when Angular has loaded.

	'use strict';

	var module = angular.module('haRevealOnLoadModule', []);

	module.directive('haRevealOnLoad', function () {

		return {
			restrict: 'A',
			scope: false,
			link: function ($scope, el) {
				setTimeout(function () {
					el.addClass('ha-reveal-on-load-active');
					$scope.contentLoaded = true;
				}, 0);
			}
		};
	});

})(angular);
;

(function (angular) {
	'use strict';

	var module = angular.module('haWhenReadyModule', []);
	module.directive('haWhenReady', [function () {
		return {
			priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
			restrict: "A",
			link: function ($scope, $element, $attributes) {
				document.lazyLoadInstance.update(); 
			}
		};
	}]);
})(angular);
;
(function (angular) {

	// Hawaiian Draggable Directive
	// ============================
	//
	// * **Class:** HaDraggable
	// * **Author:** George Pantazis
	//
	// Wrap jQuery UI's draggable functionality with an angular directive.

	'use strict';

	var module = angular.module('haDraggableModule', []);

	module.directive('haDraggableContainer', function () {

		var HaDraggableContainerLink = function ($scope, $el) {

			$scope.exampleMethod = function () {
				return $el.scope();
			};

			$scope.$emit('$methodsBound');
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaDraggableContainerLink
		};
	});

	module.directive('haDraggable', function () {

		var HaDraggableLink = function ($scope, $el) {

			$el.draggable({
				start: function (e) {
					$scope.$emit('$dragStart', e);
				},
				drag: function (e) {
					$scope.$emit('$dragging', e);
				},
				stop: function (e, ui) {
					$scope.$emit('$dragEnd', e, ui);
				}
			});

			$scope.updateDraggableSettings = function (settings) {
				$el.draggable(settings);

				return $el.scope();
			};

		};

		return {
			restrict: 'A',
			scope: true,
			priority: 10,
			link: HaDraggableLink
		};
	});

})(angular);
;
(function (angular) {

	// Hawaiian Scroll Directive
	// =======================
	//
	// * **Class:** haScrollTo
	// * **Author:** Nathan Probst
	//
	// Smoothly scroll to an anchor. Uses angular-scroll.js
	// SEE: https://github.com/durated/angular-scroll/

	'use strict';

	angular.module('haScrollModule', ['duScroll'])

	.directive('haScrollTo', ['duSmoothScrollDirective', function (duSmoothScroll) {

		var HaScrollLink = function ($scope, $element, $attrs) {

			var hrefTarget = '#' + $attrs.haScrollTo;
			var offset = 90;    // Acommodate sticky header by default

			if ($attrs.offset != null) {
				offset = $attrs.offset;
			}

			$attrs.$set('href', hrefTarget);
			$attrs.$set('offset', offset);
			duSmoothScroll[0].link($scope, $element, $attrs);
		};

		return {
			restrict: 'A',
			priority: -1,
			link: HaScrollLink
		};
	}]);

})(angular);
;
(function (angular) {

	// Hawaiian Data Directive
	// =======================
	//
	// * **Class:** HaData
	// * **Author:** George Pantazis
	//
	// Binds string of data, or JSON object, to Angular object's scope.

	'use strict';

	var module = angular.module('haDataModule', ['haDataCacheService']);

	var haDataCacheFactory = function (haDataCacheService) {

		var HaDataLink = function ($scope, $el, $attrs) {

			$scope.haDataHref = $attrs.haData || $attrs.haDataCache;

			haDataCacheService.get($scope.haDataHref)
			.success(function (data) {
				$.extend($scope, data);
				$scope.$emit('$haDataLoaded');
			});
		};

		return {
			restrict: 'A',
			scope: true,
			priority: -1,
			link: HaDataLink
			//controller: HaDataController
		};
	};

	module.directive('haData', ['haDataCacheService', haDataCacheFactory]);
	module.directive('haDataCache', ['haDataCacheService', haDataCacheFactory]);

})(angular);
;
(function (angular) {

	// Hawaiian Tail Directive
	// =======================
	//
	// * **Class:** HaTail
	// * **Author:** Nathan Probst
	//
	// Auto-scoll to end of div. Think "tail -f".

	'use strict';

	var mod = angular.module('haTailModule', ['ng']);

	mod.directive('haTail', ['$parse', '$interval', function ($parse, $interval) {

		var HaTailLink = function ($scope, $el, $attrs) {
			var tailMode = $parse($attrs.haTail);

			function tail(element) {
				if (!tailMode($scope)) {
					return;
				}

				var el = $(element).get(0);
				if (el != null) {
					el.scrollTop = el.scrollHeight;
					// el.animate({
					//   scrollTop: el.scrollHeight
					// }, 500);
				}
			}

			if ($attrs.ngModel != null) {
				$scope.$watch($attrs.ngModel, function () {
					tail($el);
				});
			} else {
				var interval = $interval(function () {
					tail($el);
				}, 500, false);

				$scope.$on('$destroy', function () {
					$interval.cancel(interval);
				});
			}
		};

		return {
			restrict: 'A',
			priority: -1,
			link: HaTailLink
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha JSON Directive
	// --------------------------------------------
	//
	// * **Class:** HaJson
	// * **Author:** Nathan Probst
	//
	// A filter to display more than the default filter.json filter.

	'use strict';

	var mod = angular.module('haJson', []);

	// NEP: Don't hide the '$' members...but basicaly this is filter.json
	mod.filter('haJson', function () {
		function isWindow(obj) {
			return obj && obj.document && obj.location && obj.alert && obj.setInterval;
		}

		function isScope(obj) {
			return obj && obj.$evalAsync && obj.$watch;
		}

		function toJsonReplacer(key, value) {
			var val = value;
			if (isWindow(value)) {
				val = '$WINDOW';
			} else if (value && document === value) {
				val = '$DOCUMENT';
			} else if (isScope(value)) {
				val = '$SCOPE';
			}
			return val;
		}

		return function (object) {
			if (typeof object === 'undefined') {
				return undefined;
			}
			return JSON.stringify(object, toJsonReplacer, '  ');
		};
	});

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haFileModule', []);

	module.directive('haFile', ['haConfig', 'haUtils', function (haConfig, haUtils) {

		return {
			restrict: 'A',
			transclude: true,
			scope: true,
			templateUrl: haConfig.getTemplateUrl('ha-file-base-template.html'),
			link: function ($scope, $el, $attrs) {
				$scope.sizeLimit = $attrs.sizeLimit;
				$scope.errorMessage = $attrs.errorMessage;
				$scope.parentFormName = $attrs.parentFormName || 'theForm';
				$scope.ChooseFileText = $attrs.chooseFile;
				$scope.NoFileChosen = $attrs.noFile;
				$scope.$emit('$haFileReady');

				var $input = $el.find('input[type="file"]');

				$scope.open = function () {
					$input.trigger('click');
				};

				$input.change(function () {
					haUtils.safeApply($scope, function () {
						$scope.fileName = $input.val();

						if ($scope.sizeLimit && $input[0].files && $input[0].files.length === 1) {
							var byteLimit = $scope.sizeLimit * 1048576;
							var theFile = $input[0].files[0];
							var sizeError = theFile.size > byteLimit;

							$scope.fileName = theFile.name || $input.val();
							$scope.sizeError = sizeError;
							if ($scope[$scope.parentFormName]) {
								$scope[$scope.parentFormName].$setValidity('sizeError', !sizeError);
							}
						}
					});
				});
			}
		};
	}]);

})(angular);;
(function (angular) {

	// Hawaiian Equal Height Directive
	// ===============================
	//
	// * **Class:** HaEqualHeight
	// * **Author:** Nathan Probst
	//
	// Automatically equalize the height of multiple columns in the same row.

	'use strict';

	var module = angular.module('haEqualHeightModule', ['ng']);

	function debounce(func, wait, immediate) {
		var timeout;
		return function () {
			var self = this;
			var args = arguments;
			var later = function () {
				timeout = null;
				if (!immediate) {
					func.apply(self, args);
				}
			};
			var callNow = immediate && !timeout;
			clearTimeout(timeout);
			timeout = setTimeout(later, wait);
			if (callNow) {
				func.apply(self, args);
			}
		};
	}

	function bottomPaddingOf(el) {
		return parseInt(getComputedStyle(el).paddingBottom.replace('px', '')) || 0;
	}

	function contentHeightOf(el) {
		return el.clientHeight - bottomPaddingOf(el);
	}

	module.directive('haEqualHeightContainer', [
		'$log',
		'$window',
		'$interval',

		function ($log, $window, $interval) {
			return {
				restrict: 'A',
				controller: ['$scope', function ($scope) {
					var contained = [];

					this.register = function (el) {
						contained.push({
							element: el,
							contentHeight: contentHeightOf(el),
							paddingBottom: bottomPaddingOf(el)
						});
					};

					this.update = function () {
						if (contained.length === 0) {
							return;
						}

						// var then;
						// if ($window.performance != null) {
						//   then = $window.performance.now();
						// }

						var maxHeight = 0;
						for (var i = 0; i < contained.length; i++) {
							var c = contained[i];
							c.contentHeight = contentHeightOf(c.element);
							maxHeight = Math.max(maxHeight, (c.contentHeight + c.paddingBottom));
						}

						angular.forEach(contained, function (c) {
							var paddingBottom = (maxHeight - c.contentHeight) + 'px';
							if (paddingBottom !== c.element.style.paddingBottom) {
								c.element.style.paddingBottom = paddingBottom;
							}
						});

						// if ($window.performance != null) {
						//   $log.debug('haEqualHeightContainer['+contained.length+']:',
						//     Math.round(1000*($window.performance.now()-then))+'μs');
						// }
					};

					// Paranoia!
					var updateFn = debounce(this.update, 50);
					var updateInterval = $interval(updateFn, 1000, 15, false);
					var w = angular.element($window);
					w.bind('resize', updateFn);

					$scope.$on('$destroy', function () {
						w.unbind('resize', updateFn);
						$interval.cancel(updateInterval);
						updateInterval = undefined;
					});

				}]
			};
		}
	]);

	module.directive('haEqualHeight',
		function () {
			return {
				restrict: 'A',
				require: '^haEqualHeightContainer',
				link: function ($scope, $el, $attrs, ctrl) {
					var el = $el[0];
					ctrl.register(el);

					var first = true;
					$scope.$watch(function () {
							return el.clientHeight;
						},
						function (newHeight, oldHeight) {
							if (first || (newHeight !== oldHeight)) {
								first = false;
								ctrl.update();
							}
						});
				}
			};
		}
	);

})(angular);
;
(function (angular) {

	// Hawaiian Emit Error Directive
	// =============================
	//
	// * **Class:** HaEmitError
	// * **Author:** Nathan Probst
	//
	// Bind to and $emit error on element.

	'use strict';

	var mod = angular.module('haEmitErrorModule', ['ng']);

	mod.directive('haEmitError', [
		'$log',

		function () {
			return {
				restrict: 'A',
				link: function (scope, el) {
					el.bind('error', function (e) {
						// $log.debug('>> haEmitError', e);
						scope.$emit('haEmitError', e);
					});
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Ha "Is Working" Directive
	// --------------------------------------------
	//
	// * **Class:** haIsWorking
	// * **Author:** Nathan Probst
	//
	// Disable and update text on a/button/input while working.

	'use strict';

	var module = angular.module('haIsWorkingModule', []);

	module.directive('haIsWorking', ['$compile', function ($compile) {

		return {
			restrict: 'A',
			scope: {
				haIsWorking: '=',
				haIsWorkingLabel: '@'
			},
			link: function ($scope, $element) {
				var el = $element[0] || $element;
				var replaceLabel;
				var restoreLabel;

				var originalLabel;
				if ((el.tagName === 'A') || (el.tagName === 'BUTTON')) {
					originalLabel = el.innerHTML;
					replaceLabel = function () {
						$element.empty();
						el.innerHTML = $scope.haIsWorkingLabel;
					};
					restoreLabel = function () {
						$element.empty();
						el.innerHTML = originalLabel;
						if (originalLabel.indexOf('{{') > -1) {
							// NEP: This causes a $watcher leak, but we'll live with it for now...
							$compile($element.contents())($scope.$parent);
						}
					};
				}
				if (el.tagName === 'INPUT') {
					originalLabel = el.value;
					replaceLabel = function () {
						el.value = $scope.haIsWorkingLabel;
					};
					restoreLabel = function () {
						el.value = originalLabel;
					};
				}

				var first = true;
				$scope.$watch('haIsWorking', function (newVal, oldVal) {
					if (!first && (newVal === oldVal)) {
						return;
					}
					first = false;

					if (!!newVal) {
						$element.addClass('is-working');
						replaceLabel();
					} else {
						$element.removeClass('is-working');
						restoreLabel();
					}
				});

				$scope.$watch('haIsWorkingLabel', function (newVal, oldVal) {
					if (newVal === oldVal) {
						return;
					}

					if ($scope.haIsWorking) {
						replaceLabel();
					}
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// HA Name
	// --------------------------------------------
	//
	// * **Class:** haInputName
	// * **Author:** Nathan Probst
	//
	// A directive to allow interpolation of name attributes onto form inputs

	'use strict';

	var module = angular.module('haInputName', []);

	module.directive('haInputName', [
		'$interpolate',
		function ($interpolate) {
			return {
				restrict: 'A',
				priority: 1000,
				scope: false,
				link: function ($scope, $el) {
					var name = $interpolate($el.attr('ha-input-name'), false, null, true)($scope);
					$el.removeAttr('ha-input-name');
					var input = $el.find('input');
					input.attr('name', name);
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// HA Name
	// --------------------------------------------
	//
	// * **Class:** haScope
	// * **Author:** Nathan Probst
	//
	// A directive to injecting JSON from .NET directly into current scope!

	'use strict';

	var module = angular.module('haScopeModule', []);

	module.directive('haScope', [

		function () {
			return {
				restrict: 'A',
				scope: false,
				link: function ($scope, $el, $attrs) {
					// MUST be on a properly typed <script> tag!
					if (($el[0].tagName === 'SCRIPT') && ($attrs.type === 'text/ha-scope')) {
						try {
							var json = JSON.parse($el[0].text);
							var scope = $scope;
							var ns = $attrs.haScope || null;

							if (ns != null) {
								$scope[ns] = $scope[ns] || {};
								scope = $scope[ns];
							}

							angular.extend(scope, json);
						} catch (e) {/* do nothing */
						}
					}
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// HA View Model
	// --------------------------------------------
	//
	// * **Class:** haViewModel
	// * **Author:** Nathan Probst
	//
	// A directive to injecting ViewModel JSON directly, bypassing global vars and haGlobals!

	'use strict';

	// var module = angular.module('haViewModelModule', []);
	var module;
	try {
		module = angular.module('haViewModelModule');
	} catch (e) {
		module = angular.module('haViewModelModule', []);
	}

	module.directive('haViewModel', ['$log', 'haViewModelSvc', function ($log, svc) {
		return {
			restrict: 'A',
			scope: false,
			link: function ($scope, $el, $attrs) {
				var vmName = $attrs.haViewModel;
				// MUST be on a properly typed <script> tag!
				if ((vmName != null) &&
					($el[0].tagName === 'SCRIPT') &&
					($attrs.type === 'text/ha-view-model')) {
					try {
						var json = JSON.parse($el[0].text);
						svc.put(vmName, json);
					} catch (err) {
						$log.error(vmName, err);
					}
				}
			}
		};
	}
	]);

})(angular);
;
(function (angular) {

	// Ha Confirm When Dirty Directive
	// --------------------------------------------
	//
	// * **Class:** HaConfirmWhenDirty
	// * **Author:** Cory Shaw
	//
	// If form is dirty, trigger a confirm dialog box.

	'use strict';

	var module = angular.module('HaConfirmWhenDirtyModule', []);

	module.directive('haConfirmWhenDirty', ['$window', function ($window) {
		var HaConfirmWhenDirtyLink = function ($scope, elem, attrs) {
			$scope.confirmText = attrs.confirmText;
			$scope.theFormName = elem.attr('name');
			$scope.theForm = $scope[$scope.theFormName];

			$scope.submitForm = function () {
				$scope.working = true;
			};

			window.onbeforeunload = function () {
				if ($scope.theForm && $scope.theForm.$dirty && !$scope.working) {
					return $scope.confirmText;
				}
			};

			$scope.$on('$locationChangeStart', function (event /*, next, current*/) {
				if ($scope.theForm && $scope.theForm.$dirty) {
					if (!$window.confirm($scope.confirmText)) {
						event.preventDefault();
					}
				}
			});
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaConfirmWhenDirtyLink
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Alert Directive
	// --------------------------------------------
	//
	// * **Class:** haWatchTrailer
	// * **Author:** Cory Shaw
	//
	// Shows a modal with a youtube video

	'use strict';

	var module = angular.module('haWatchTrailerModule', []);

	module.directive('haWatchTrailer', ['$window', 'haModal', 'haConfig', function ($window, haModal, haConfig) {

		var HaWatchTrailerController = function ($scope) {


			var trailerTemplateUrl = haConfig.getTemplateUrl('ha-watch-trailer-modal-template.html');
			if ($scope.$root.isMobile) {
				trailerTemplateUrl = haConfig.getTemplateUrl('ha-watch-trailer-modal-mobile-template.html');
			}

			$scope.showTrailer = function (youtubeID) {
				$scope.youtubeURL = '//www.youtube.com/embed/' + youtubeID + '?autoplay=1&rel=0&origin=http://hawaiianairliens.comyoutubeID';
				haModal(trailerTemplateUrl, {
					backdrop: 'true',
					id: 'watch-video',
					scope: $scope
				});
			};
		};

		HaWatchTrailerController.$inject = ['$scope'];

		return {
			restrict: 'A',
			scope: true,
			controller: HaWatchTrailerController
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Scroll Spy Directive
	// ===============================
	//
	// * **Class:** haScrollSpy
	// * **Author:** Jamie Perkins
	//
	// $broadcast an event when an element comes into or goes out of view:
	//
	// 'elementFirstScrolledIntoView' is fired once when the element first scrolls into view
	// 'elementScrolledIntoView' is fired once every time the element scrolls into view
	// 'elementScrolledOutOfView' is fired once every time the element is scrolled out of view

	var module = angular.module('haScrollSpyModule', []);

	module.directive('haScrollSpy', ['$window', '$rootScope', function ($window, $rootScope) {

		return {
			restrict: 'A',
			link: function ($scope, $el, $attrs) {

				function ScrollSpy() {
					//console.log('haScrollSpy set for '+$attrs.id);

					var self = this;
					var initialized = false;
					var viewportHeight;
					var elementHeight;
					var topOffset, lastTopOffset = 0;
					var elementFirstScrolledIntoView = false;
					var elementScrolledIntoView = false;
					var elementScrolledOutOfView = false;
					var doc = document.documentElement;
					var id = $attrs.id || 'unknown element';
					var viewportShorterThanElement = false;
					var percentOfElementNeededInView = 0.9;

					// onscroll
					this.determinePosition = function () {
						if (initialized) {
							topOffset = angular.element($el).offset().top;
							if (topOffset - lastTopOffset > elementHeight) {
								lastTopOffset = topOffset;
								elementFirstScrolledIntoView = false;
							}
							var pos = (window.pageYOffset || doc.scrollTop);

							// element Scrolled Out Of View
							if (!elementScrolledOutOfView) {
								if (pos + viewportHeight < topOffset || pos > topOffset + elementHeight) {
									//console.log('element Scrolled Out Of View '+id);
									elementScrolledOutOfView = true;
									elementScrolledIntoView = false;
									$rootScope.$broadcast('elementScrolledOutOfView', id);
								}
							}
							if ((pos + viewportHeight >= topOffset + percentOfElementNeededInView * elementHeight && topOffset > pos) ||
								(pos >= topOffset && viewportShorterThanElement)) {
								// element First Scrolled Into View
								if (!elementFirstScrolledIntoView) {
									//console.log('element First Scrolled Into View '+id);
									elementFirstScrolledIntoView = true;
									$rootScope.$broadcast('elementFirstScrolledIntoView', id);
								}
								// element Scrolled Into View
								if (!elementScrolledIntoView) {
									//console.log('element Scrolled Into View '+id);
									elementScrolledIntoView = true;
									elementScrolledOutOfView = false;
									$rootScope.$broadcast('elementScrolledIntoView', id);
								}
							}
						}
					};
					this.takeMeasurements = function () {
						viewportHeight = angular.element($window).height();
						elementHeight = angular.element($el).outerHeight();
						topOffset = angular.element($el).offset().top;
						//console.log('take measurements- viewportHeight:'+viewportHeight+', element height: '+elementHeight+', top offset: '+topOffset);
						if (viewportHeight < elementHeight) {
							viewportShorterThanElement = true;
						}
						elementFirstScrolledIntoView = false;
						// determine position on page load
						initialized = true;
						self.determinePosition();
					};
					// wait for dom to render so correct measurements can be taken
					var waitForRender = setInterval(function () {
						if (angular.element($el).outerHeight() > 2 && angular.element($el).offset().top > 0) { // IE11  reports 2 at times...
							clearTimeout(waitForRender);
							self.takeMeasurements();
						}
					}, 50);
				}

				var name = $attrs.id + '-scrollSpy';
				$rootScope[name] = new ScrollSpy();

				// global onscroll fns array
				if (!$rootScope.globalOnScrollFunctions) {
					$rootScope.globalOnScrollFunctions = [];
				}
				$rootScope.globalOnScrollFunctions.push($rootScope[name]);

				// set up global onscroll function that will call each fn in global onscroll fns
				if (!$rootScope.globalOnScroll) {
					$rootScope.globalOnScroll = function () {
						angular.forEach($rootScope.globalOnScrollFunctions, function (val) {
							val.determinePosition();
						});
					};
					// on scroll
					$window.onscroll = $rootScope.globalOnScroll;
				}
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Favorite Directive
	// --------------------------------------------
	//
	// * **Class:** HaFavorite
	// * **Author:** Cory Shaw
	//
	// Handles favoriting an item ajax call and behavior

	'use strict';

	var module = angular.module('HaFavoriteModule', []);

	module.directive('haFavorite', ['haConfig', 'haHttpService', '$window', '$timeout', function (haConfig, haHttpService, $window, $timeout) {

		var HaFavoriteLink = function ($scope, $el, $attrs) {

			var addFavoriteEndpoint = '/IslandGuideFavorites/AddPlaceFavorite';
			var removeFavoriteEndpoint = '/IslandGuideFavorites/DeletePlaceFavorite';
			var titleStrings = {'titleOnString': '', 'titleOffString': ''};

			$scope = angular.element($el).children().eq(0).scope();
			$scope.isFavorited = $attrs.favorited;
			$scope.placeID = $attrs.placeId;
			angular.element($el).children().eq(0).attr('role', 'button');
			angular.element($el).children().eq(0).attr('ha-favorite',$attrs.favorited);
			angular.element($el).children().eq(0).attr('place-id',$attrs.placeId);

			function updateTitle() {
				if ($scope.isFavorited) {
					angular.element($el).children().eq(0).attr('title', titleStrings.titleOnString);
				} else {
					angular.element($el).children().eq(0).attr('title', titleStrings.titleOffString);
				}
			}

			function fetchFavoriteString(titleKey, titleString) {
				$scs.get(titleKey).then(function (data) {
					titleStrings[titleString] = data;
					updateTitle();
				});
			}

			fetchFavoriteString('EXPLORE_SHARED.FavoriteOnText', 'titleOnString');
			fetchFavoriteString('EXPLORE_SHARED.FavoriteOffText', 'titleOffString');

			$scope.favorite = function (itemID) {
				if (!$scope.working) {
					var item = {
						'PlaceId': itemID
					};
					if ($scope.isFavorited) {
						$scope.removeFavorite(item);
					} else {
						$scope.addFavorite(item);
					}
				}
			};

			$scope.strings = null;
			$scope.getStrings = function (data) {
				if (!$scope.strings) {
					$scope.strings = {};
					$scope.strings.AddFavoriteMessageLine1 = data.AddFavoriteMessageLine1;
					$scope.strings.AddFavoriteMessageLine2 = data.AddFavoriteMessageLine2;
					$scope.strings.DeleteFavoriteMessageLine1 = data.DeleteFavoriteMessageLine1;
					$scope.strings.DeleteFavoriteMessageLine2 = data.DeleteFavoriteMessageLine2;
				}

			};

			$scope.addFavorite = function (item) {
				$scope.isFavorited = true;
				updateTitle();
				$scope.working = true;
				haHttpService.POST(addFavoriteEndpoint, item).success(function (data) {
					console.log(data);
					$scope.working = false;
					if (data.IsSuccess) {
						$scope.isFavorited = true;
						$scope.getStrings(data);
						$scope.addedPopover = true;
						$timeout(function () {
							$scope.addedPopover = false;
						}, 5000);
					}
				}).error(function (data) {
					$scope.working = false;
					if (data.RedirectURL) {
						$window.location = data.RedirectURL;
					}
				});
			};

			$scope.removeFavorite = function (item) {
				//$scope.working = true;
				$scope.isFavorited = false;
				updateTitle();
				haHttpService.POST(removeFavoriteEndpoint, item).success(function (data) {
					//$scope.working = false;
					$scope.getStrings(data);
					// $scope.removedPopover = true;
					// $timeout(function () {
					//     $scope.removedPopover = false;
					// }, 3000);

				}).error(function (data) {
					//$scope.working = false;
					if (data.RedirectURL) {
						$window.location = data.RedirectURL;
					}
				});
			};

		};

		return {
			restrict: 'A',
			scope: true,
			templateUrl: haConfig.getTemplateUrl('ha-favorite.html'),
			replace: true,
			link: HaFavoriteLink
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Contextual Help Directive
	// --------------------------------------------
	//
	// * **Class:** HaContextualHelp
	// * **Author:** Cory Shaw
	//
	// Behavior added to a help link of a page to find and display contextual help content in a modal window

	'use strict';

	var module = angular.module('haContextualHelpModule', []);

	module.directive('haContextualHelp', ['haConfig', 'haModal', 'haSitecoreStrings', '$log', function (haConfig, haModal, $scs, $log) {

		var HaContextualHelpController = function ($scope) {

			$scope.showContextualHelp = function () {
				if ($scope.scsId) {
					$scs.get($scope.scsId)
					.then(function (content) {
						haModal('', {
							id: 'contextual-help',
							backdrop: 'true',
							template: content
						});
					})
						['catch'](function () {
						$log.log('There was an error retreiving sitecore content.');
					});
				} else {
					haModal('ContextualHelpModal.html', {
						id: 'contextual-help',
						backdrop: 'true'
					});
				}
			};
		};

		HaContextualHelpController.$inject = ['$scope', '$rootScope'];

		var HaContextualHelpLink = function ($scope, $el, $attrs) {
			$scope.scsId = $attrs.scsId;
		};

		return {
			restrict: 'A',
			replace: true,
			template: '<a class="bodycopy-sans-4 textlink" href ng-click="showContextualHelp()">' +
			'<span class="sr-only" i18n-content="inflightoptionsinfo.gethelpwiththispagelink"></span>' +
			'<i class="fontIcon fontIcon20-help pull-left"></i>' +
			'<div ng-transclude></div>' +
			'</a>',
			scope: true,
			transclude: true,
			link: HaContextualHelpLink,
			controller: HaContextualHelpController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Button Spinner
	// --------------------------------------------
	//
	// * **Class:** HaButtonSpinner
	// * **Author:** Jamie Perkins
	//
	// Creates a spinner inside a button when it is clicked

	'use strict';

	var module = angular.module('haButtonSpinnerModule', []);

	module.directive('haButtonSpinner', function () {

		return {
			template: '<span class="button-spinner"><span><span></span></span></span><span ng-transclude></span>',
			transclude: true,
			restrict: 'A',
			scope: {
				showSpinner: '='
			},
			link: function ($scope, $el) {

				$scope.$watch(function (scope) {
					return scope.showSpinner;
				}, function (newValue) {
					$el.blur();
					if (newValue) {
						$el.children('.button-spinner').css({opacity: 1});
						$el.css({paddingLeft: 60});
						$el.prop('disabled', true);
					} else {
						$el.children('.button-spinner').css({opacity: 0});
						$el.css({paddingLeft: 30});
						$el.prop('disabled', false);
					}
				});
			}
		};

	});

})(angular);
;
(function (angular) {

	// Ha Count-Up directive
	// --------------------------------------------
	//
	// * **Class:** CountUp
	// * **Author:** Jamie Perkins
	//
	// Creates a counting animation for numbers

	'use strict';

	var module = angular.module('haCountUpModule', []);

	module.directive('countUp', ['$filter',
		function ($filter) {

			return {
				restrict: 'A',
				scope: {
					endVal: '@',
					duration: '@'
				},
				link: function ($scope, $el, $attrs) {

					/* 	adapted from my GitHub repo @ https://github.com/inorganik/countUp.js
					 v1.5.1
					 removed as much code as possible to make it more lightweight
					 */
					function CountUp(target, startVal, endVal, duration) {

						// make sure requestAnimationFrame and cancelAnimationFrame are defined
						// polyfill for browsers without native support
						// by Opera engineer Erik Möller
						var lastTime = 0;
						var vendors = ['webkit', 'moz', 'ms', 'o'];
						for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
							window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
							window.cancelAnimationFrame =
								window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
						}
						if (!window.requestAnimationFrame) {
							window.requestAnimationFrame = function (callback) {
								var currTime = new Date().getTime();
								var timeToCall = Math.max(0, 16 - (currTime - lastTime));
								var id = window.setTimeout(function () {
										callback(currTime + timeToCall);
									},
									timeToCall);
								lastTime = currTime + timeToCall;
								return id;
							};
						}
						if (!window.cancelAnimationFrame) {
							window.cancelAnimationFrame = function (id) {
								clearTimeout(id);
							};
						}

						this.d = document.getElementById(target);
						this.startVal = Number(startVal);
						this.endVal = endVal;
						this.countDown = (this.startVal > this.endVal);
						this.frameVal = this.startVal;
						this.duration = duration * 1000 || 2000;
						var self = this;

						// Print value to target
						this.printValue = function (value) {
							var result = $filter('number')(value);
							this.d.innerHTML = result;
						};
						// Robert Penner's easeOutExpo
						this.easeOutExpo = function (t, b, c, d) {
							return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b;
						};
						this.count = function (timestamp) {

							if (!self.startTime) {
								self.startTime = timestamp;
							}

							self.timestamp = timestamp;

							var progress = timestamp - self.startTime;
							self.remaining = self.duration - progress;

							// always ease
							if (self.countDown) {
								self.frameVal = self.startVal - self.easeOutExpo(progress, 0, self.startVal - self.endVal, self.duration);
							} else {
								self.frameVal = self.easeOutExpo(progress, self.startVal, self.endVal - self.startVal, self.duration);
							}

							// don't go past endVal since progress can exceed duration in the last frame
							if (self.countDown) {
								self.frameVal = (self.frameVal < self.endVal) ? self.endVal : self.frameVal;
							} else {
								self.frameVal = (self.frameVal > self.endVal) ? self.endVal : self.frameVal;
							}

							// format and print value
							self.frameVal = Math.round(self.frameVal);
							self.printValue(self.frameVal);

							// whether to continue
							if (progress < self.duration) {
								self.rAF = requestAnimationFrame(self.count);
							} else {
								if (self.callback) {
									self.callback();
								}
							}
						};
						// start your animation
						this.start = function (callback) {
							self.callback = callback;
							// make sure values are valid
							if (!isNaN(self.endVal) && !isNaN(self.startVal) && self.startVal !== self.endVal) {
								self.rAF = requestAnimationFrame(self.count);
							} else {
								this.d.innerHTML = endVal;
							}
							return false;
						};
						// reset to startVal so animation can be run again
						this.reset = function () {
							self.paused = false;
							delete self.startTime;
							self.startVal = startVal;
							cancelAnimationFrame(self.rAF);
							self.printValue(self.startVal);
						};
						// pass a new endVal and start animation
						this.update = function (newEndVal) {
							delete self.callback; // avoid endless callback loop
							cancelAnimationFrame(self.rAF);
							self.paused = false;
							delete self.startTime;
							self.startVal = self.frameVal;
							self.endVal = Number(newEndVal);
							self.countDown = (self.startVal > self.endVal);
							self.rAF = requestAnimationFrame(self.count);
						};

						/* methods removed:
						 - pauseResume
						 - formatNumber
						 */

						// format startVal on initialization
						self.printValue(self.startVal);
					}

					$scope.end = $attrs.endVal || 0;
					if (isNaN($scope.end)) {
						$scope.end = Number($scope.end.match(/[\d\-]+/g).join('')); // strip non-numerical characters
					}
					$scope.dur = Number($attrs.duration) || 2.5;

					// construct countUp
					function animate() {
						if ($scope.end > 9999) {
							// make easing smoother for large numbers
							$scope.countUp = new CountUp($attrs.id, 0, $scope.end - 100, $scope.dur / 2);
							$scope.countUp.start(function () {
								$scope.countUp.update($scope.end);
							});
						}
						else {
							$scope.countUp = new CountUp($attrs.id, 0, $scope.end, $scope.dur);
							$scope.countUp.start();
						}
					}

					animate();

					// re-animate on click
					$el.on('click', function () {
						animate();
					});
				}
			};
		}]);
})(angular);
;
(function (angular) {

	// Ha Contextual Help Directive
	// --------------------------------------------
	//
	// * **Class:** HaContentModal
	// * **Author:** Cory Shaw
	//
	// Behavior to serve sitecore content in a modal window.

	'use strict';

	var module = angular.module('haContentModalModule', []);

	module.directive('haContentModal', ['haConfig', 'haModal', 'haSitecoreStrings', '$log', function (haConfig, haModal, $scs, $log) {

		var HaContentModalController = function ($scope) {

			$scope.showModal = function () {
				if (!$scope.hasError && $scope.modalContent) {
					if ($scope.modalType === 'content') {
						haModal('', {
							modalClass: 'ha-content-modal',
							id: $scope.modalId,
							backdrop: 'true',
							template: $scope.modalContent
						});
					} else {
						haModal('', {
							id: $scope.modalId,
							backdrop: 'true',
							template: $scope.modalContent,
							modalClass: 'image-modal'
						});
					}
				}
			};

			$scope.fetchModalContent = function () {
				if ($scope.scsId) {
					$scs.get($scope.scsId)
					.then(function (content) {
						$scope.modalContent = content;
					})
						['catch'](function () {
						$scope.hasError = true;
						$log.error('There was an error retreiving sitecore content.');
					});
				}
				else if ($scope.imgSrc) {
					$scope.modalContent = '<section class="modal-template ng-scope"><div class="modal-header centered-header"><h1>' + $scope.imgTitle + '</h1></div><div class="modal-main"><img src="' + $scope.imgSrc + '" alt=""></div></section>';
				}
			};
		};

		HaContentModalController.$inject = ['$scope', '$rootScope'];

		var HaContentModalLink = function ($scope, $el, $attrs) {
			$scope.modalType = 'content';
			$scope.scsId = $attrs.scsId;
			$scope.modalId = $attrs.modalId;
			if ($attrs.imgSrc) {
				$scope.modalType = 'image';
				$scope.imgSrc = $attrs.imgSrc;
                $scope.imgTitle = $attrs.title;
			}

			// Fetch Modal Content on Load
			$scope.fetchModalContent();

			$($el).on('click', function () {
				$scope.showModal();
			});

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaContentModalLink,
			controller: HaContentModalController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Contextual Help Directive
	// --------------------------------------------
	//
	// * **Class:** HaDomModal
	// * **Author:** Jake Albaugh
	//
	// Behavior to use dom content in a modal window.

	'use strict';

	var module = angular.module('haDomModalModule', []);

	module.directive('haDomModal', ['haModal', function (haModal) {

		var HaDomModalController = function ($scope) {
			$scope.showModal = function () {
				if (!$scope.hasError && $scope.modalContent) {
					haModal($scope.modalContent, {
						id: $scope.modalId,
						backdrop: 'true'
					});
				}
			};
		};

		HaDomModalController.$inject = ['$scope', '$rootScope'];

		var HaDomModalLink = function ($scope, $el, $attrs) {
			$scope.domId = $attrs.domId;
			$scope.modalId = $attrs.modalId;
			$scope.modalContent = $attrs.domId;

			$($el).on('click', function () {
				$scope.showModal();
			});

		};

		return {
			restrict: 'A',
			scope: true,
			link: HaDomModalLink,
			controller: HaDomModalController
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Loading Spinner Directive
	// --------------------------------------------
	//
	// * **Class:** HaLoadingSpinner
	// * **Author:** Mari Yamaha
	//
	// Injects Loading Spinner html and styling where used

	'use strict';

	var module = angular.module('haLoadingSpinnerModule', []);

	module.directive('haLoadingSpinner', function () {

		var HaLoadingSpinnerLink = function ($scope, $el, $attrs) {

			if ($attrs.small !== undefined) {
				$el.children('.ha-loading-spinner').addClass('small');
			}
			if ($attrs.white !== undefined) {
				$el.children('.ha-loading-spinner').addClass('white');
			}
			$scope.loadingText = '';
			if ($attrs.loadingText !== undefined) {
				$scope.loadingText = $attrs.loadingText;
			}

			$scope.$emit('$haLoadingSpinnerReady');
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaLoadingSpinnerLink,
			transclude: true,
			template: '<div class="ha-loading-spinner" tabindex="0"><span class="sr-only">{{loadingText}}</span><div class="spin"><div></div></div></div>'
		};
	});

	module.directive('haLoadingSpinnerWithText', function () {

		var HaLoadingSpinnerWithTextLink = function ($scope) {

			$scope.$emit('$haLoadingSpinnerWithTextReady');
		};

		return {
			restrict: 'A',
			scope: true,
			link: HaLoadingSpinnerWithTextLink,
			transclude: true,
			template: '<div class="ha-loading-spinner-with-text" ha-reveal-on-load tabindex="0"><div class="ha-loading-spinner small"><div class="spin"><div></div></div></div><div class="ha-loading-text"><span ng-transclude></span></div></div>'
		};
	});

})(angular);

;
(function (angular) {

	// Focus on Display Directive
	// --------------------------------------------
	//
	// * **Class:** FocusOnDisplay
	// * **Author:** Michael Toymil
	//
	// Focus's on a given element when it is shown

	'use strict';

	var module = angular.module('focusOnDisplayModule', []);

	module.directive('focusOnDisplay', function () {
		return {
			restrict: 'A',
			link: function ($scope, element, attrs) {

				// skip this behavior for IE9, bc it suppresses placeholder shim
				if (!$('html').hasClass('lte-ie9')) {
					attrs.$observe('focusOnDisplay', function () {
						element.focus();
					});
				}
			}
		};
	});

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Sticky Directive
	// ===============================
	//
	// * **Class:** haSticky
	// * **Author:** Jamie Perkins
	//
	// Keep an element stuck to its position in its containing view as user scrolls

	var module = angular.module('haStickyModule', []);

	module.directive('haSticky', ['$window', '$rootScope', function ($window, $rootScope) {

		return {
			restrict: 'A',
			link: function ($scope, $el, $attrs) {

				function Sticky() {

					var self = this,
						type = $attrs.haSticky || 'margin',
						initialized = false,
						viewportHeight,
						startingTopMargin,
						elemHeight,
						elemOffsetTop,
						elemOffsetParent,
						containerHeight,
						maxTopMargin,
						otherStickies = 0,
						isFixed = false,
						parentHeight,
						parentOffsetTop,
						lockElemOffsetTop = false,
						doc = document.documentElement;

					// determine presence of other stickies (ha-sticky is only used on desktop)
					if ($('[ha-book-sticky-progress-bar]').length) {
						otherStickies += 70;
					}
					if ($('[ha-sticky-message-bar]').length) {
						otherStickies += 70;
					}
					if ($('[ha-sticky-booking-widget]').length) {
						otherStickies += 75;
					}
					if ($('#sessiontimer aside').length) {
						if (!$('#sessiontimer aside').hasClass('ng-hide')) {
							otherStickies += 70;
						}
					}

					this.unfix = function() {
						$el[0].style.removeProperty('position');
						$el[0].style.removeProperty('width');
						$el[0].style.removeProperty('top');
						$el[0].style.removeProperty('left');
						$el[0].style.removeProperty('padding-left');
						$el[0].style.removeProperty('padding-right');
					};

					// onscroll
					this.determinePosition = function () {
						if (initialized) {
							if (elemHeight + otherStickies <= viewportHeight) {
								var pos = (window.pageYOffset || doc.scrollTop);

								if (type === 'margin') {
									// use margin to make sticky
									if (pos >= elemOffsetTop - otherStickies) {
										var mar = pos - elemOffsetTop + otherStickies + startingTopMargin;
										mar = Math.min(maxTopMargin, mar);
										$el.css('marginTop', mar);
									} else {
										$el.css('marginTop', startingTopMargin);
									}
								} else {
									// use position:fixed to make sticky
									if (pos >= elemOffsetTop - otherStickies &&
										pos < parentHeight + parentOffsetTop - elemHeight) {

										if (!isFixed) {
											isFixed = true;
											lockElemOffsetTop = true;
											var elemWidth = $el.outerWidth(),
												elemLeft = $el.offset().left,
												paddingLeft = $el.css('padding-left'),
												paddingRight = $el.css('padding-right');
											$el.css({
												position: 'fixed',
												top: startingTopMargin,
												left: elemLeft,
												width: elemWidth,
												paddingLeft: paddingLeft,
												paddingRight: paddingRight,
												marginTop: startingTopMargin
											});
										}
									}
									else {
										if (isFixed) {
											self.unfix();
											if (pos < elemOffsetTop - otherStickies) {
												lockElemOffsetTop = false;
												// above stuck elem
												$el.css({
													marginTop: startingTopMargin
												});
											} else {
												lockElemOffsetTop = true;
												// below stuck item: margin holds it where it was last
												// stuck when user scrolls past containing div
												var mar = parentHeight - elemHeight + startingTopMargin;
												$el.css({
													marginTop: mar
												});
											}
											isFixed = false;
										}
									}
								}
							}
						}
					};

					this.takeMeasurements = function () {

						clearTimeout(self.resizeTimeout);
						self.resizeTimeout = setTimeout(function() {

							if (startingTopMargin === undefined) { // don't redefine starting top margin
								startingTopMargin = parseInt($el.css('marginTop'));
							}
							if (type === 'margin') {
								// reset margin
								$el.css({
									'marginTop': startingTopMargin,
									'position': 'relative',
									'z-index': 8
								});
							}
							// // measurements
							viewportHeight = window.innerHeight;
							elemOffsetParent = $el.position().top;
							elemHeight = $el.outerHeight();
							containerHeight = $el.closest('.container').height();
							maxTopMargin = containerHeight - elemHeight - elemOffsetParent;
							parentHeight = $el.parent().outerHeight();
							parentOffsetTop = $el.parent().offset().top;

							if (!lockElemOffsetTop) {
								elemOffsetTop = $el.offset().top;
							}

							initialized = true;

							self.determinePosition();

						}, 50);
					};
					// wait for dom to render so correct measurements can be taken
					var waitForRender = setInterval(function () {
						if ($el.outerHeight() > 2) { // IE11  reports 2 at times...
							clearTimeout(waitForRender);
							self.takeMeasurements();
						}
					}, 50);
				}

				if (!$rootScope.isMobile) { // disable on mobile

					var sticky = new Sticky();
					var name = sticky.id + '-sticky';
					$rootScope[name] = sticky;

					// global onscroll fns array
					if (!$rootScope.globalOnScrollFunctions) {
						$rootScope.globalOnScrollFunctions = [];
					}
					$rootScope.globalOnScrollFunctions.push($rootScope[name]);

					// set up global onscroll function that will call each fn in global onscroll fns
					if (!$rootScope.globalOnScroll) {
						$rootScope.globalOnScroll = function () {
							angular.forEach($rootScope.globalOnScrollFunctions, function (val) {
								val.determinePosition();
							});
						};
						// on scroll
						$window.onscroll = $rootScope.globalOnScroll;
					}

					$rootScope.$on('haStickyResize', function() {
						sticky.takeMeasurements();
					});

					// re-measure when the elem changes height
					$scope.$watch(function() {
						return $el.outerHeight()
					}, function () {
						sticky.takeMeasurements();
					});

					// re-measure when doc height changes
					// (aka anything in the doc changes height)
					$scope.$watch(function() {
						return window.document.body.scrollHeight;
					}, function (newValue) {
						if (newValue) {
							sticky.takeMeasurements();
						}
					});

					window.addEventListener('resize', sticky.takeMeasurements);
				}
			}
		};
	}]);

	// makes a div stick to the bottom when it's not in view.
	module.directive('haStickyFooter', ['$window', '$rootScope', '$timeout', function ($window, $rootScope, $timeout) {

		return {
			restrict: 'A',
			link: function ($scope, $el, $attrs) {

				function StickyFooter() {

					var self = this,
						fixed = false,
						$fixedElem = $el.children(),
						viewportHeight,
						elemHeight,
						id = $attrs.id,
						otherStickies = 0;

					// determine presence of other stickies, create id
					$('[ha-sticky-footer]').each(function() {
						if ($(this).is(':visible') && $(this).attr('id') !== id) {
							otherStickies +=  $(this).children().height();
						}
					});

					this.determinePosition = function() {
						var elemOffset = $el.offset().top,
							breakpoint = window.pageYOffset + viewportHeight - elemHeight;
						if (breakpoint < elemOffset) {
							if (!fixed) {
								$fixedElem.css({
									position: 'fixed',
									bottom: otherStickies,
									left: 0,
									right: 0,
									zIndex: 50
								});
								fixed = true;
								$fixedElem.addClass('fixed');
							}
						} else {
							if (fixed) {
								$fixedElem.css({
									position: 'static'
								});
								fixed = false;
								$fixedElem.removeClass('fixed');
							}
						}
					}
					// wait for dom to render so correct measurements can be taken
					var waitForRender = setInterval(function () {
						if ($el.outerHeight() > 2) { // IE11  reports 2 at times...
							clearTimeout(waitForRender);
							viewportHeight = window.innerHeight;
							elemHeight = $el.outerHeight();
							self.determinePosition();
						}
					}, 50);
				}

				var stickyFooter = new StickyFooter();
				var name = 'stickyFooter-'+stickyFooter.id;
				$rootScope[name] = stickyFooter;

				// global onscroll fns array
				if (!$rootScope.globalOnScrollFunctions) {
					$rootScope.globalOnScrollFunctions = [];
				}
				$rootScope.globalOnScrollFunctions.push($rootScope[name]);

				// set up global onscroll function that will call each fn in global onscroll fns
				if (!$rootScope.globalOnScroll) {
					$rootScope.globalOnScroll = function () {
						angular.forEach($rootScope.globalOnScrollFunctions, function (val) {
							val.determinePosition();
						});
					};
					// on scroll
					$window.onscroll = $rootScope.globalOnScroll;
				}

				$rootScope.$on('haStickyResize', function() {
					stickyFooter.determinePosition();
				});

				$scope.$watch($el.outerHeight(), function () {
					stickyFooter.determinePosition();
				});

				window.addEventListener('resize', stickyFooter.determinePosition);


			}
		};
	}]);

})(angular);
;
(function (angular) {

	/*
	 for adding behavior to any standard html element
	 */

	'use strict';

	var module = angular.module('haElementDirectivesModule', []);

	// for fixing IE9 select bug, where only first letter of option is displayed
	// using ng-options in combination with this should fix all occurrences.
	module.directive('select', ['$interval', '$timeout', function ($interval, $timeout) {
		return {
			restrict: 'E',
			link: function ($scope, $el) {

				if ($('html').hasClass('lte-ie9')) {

					var waitForOptions;
					if ($el.children('option').length <= 1) {
						// wait for options to populate
						var count = 0;
						waitForOptions = $interval(function () {
							count++;
							if ($el.children('option').length > 1) {
								$interval.cancel(waitForOptions);
								$el.width($el.css('width'));
							}
							if (count > 40) {
								$interval.cancel(waitForOptions);
							}
						}, 50);
					} else {
						$timeout(function () {
							$el.width($el.css('width'));
						}, 50);
					}
				}
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Check Directive
	// --------------------------------------------
	//
	// * **Class:** HaClearData
	// * **Author:** Carlos Martin
	//
	// Adding clear data option to input elements

	'use strict';

	var module = angular.module('haClearDataModule', []);

	module.directive('haClearData', [function () {
		return {
			scope: { haClearData: '=' },
			link: function ($scope, $el) {
				var $elem = $($el);
				$scope.$watch('haClearData', function (value) {
					if (value) {
						$elem.addClass('clearable');
					} else {
						$elem.removeClass('clearable');
					}
				});

				$elem.click(function () {
					$scope.haClearData = '';
					$scope.$apply();
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haRangeModule', []);

	module.directive('haRange', ['$window', 'haConfig', function ($window, haConfig) {

		var link = function($scope, $elem) {
			// internal start/end values prevent triggering any external
			// start/end watchers until the slider is released
			$scope.internalStart = $scope.start || $scope.min;
			$scope.internalEnd = $scope.end || $scope.max;

			// init slider
			var sliderEl = $elem.find('.slider')[0];
			var sliderObj = $window.noUiSlider.create(sliderEl, {
				start: [$scope.internalStart, $scope.internalEnd],
				connect: true,
				range: {
					'min': $scope.min || 0,
					'max': $scope.max || 100
				},
				step: 1
			});

			// on slide, update internal start/end
			sliderObj.on('slide', function() {
				var currentRange = sliderObj.get();
				$scope.internalStart = parseInt(currentRange[0]);
				$scope.internalEnd = parseInt(currentRange[1]);
				$scope.$apply();
			});

			// when the values change, then update the external start/end
			sliderObj.on('change', function() {
				var currentRange = sliderObj.get();
				$scope.internalStart = $scope.start = parseInt(currentRange[0]);
				$scope.internalEnd = $scope.end = parseInt(currentRange[1]);
				$scope.$apply();
			});

			// change handler for text inputs
			$scope.inputChanged = function(){
				var inputValues = [$scope.internalStart, $scope.internalEnd];
				sliderObj.set(inputValues);
				$scope.start = $scope.internalStart;
				$scope.end = $scope.internalEnd;
			};

			// watch external start/end
			$scope.$watchCollection('[start, end]', function() {
				// check for falsy values
				$scope.start = $scope.start || $scope.min;
				$scope.end = $scope.end || $scope.max;

				// check for valid ranges
				$scope.start = Math.max(Math.min($scope.start, $scope.end), $scope.min);
				$scope.end = Math.min(Math.max($scope.start, $scope.end), $scope.max);

				// Set internal state
				$scope.internalStart = $scope.start;
				$scope.internalEnd = $scope.end;
				sliderObj.set([$scope.start, $scope.end]);
			});

			// watch min/max in order to update slider
			$scope.$watchCollection('[min, max]', function(newValue) {
				newValue[0] = isNaN(parseInt(newValue[0])) ? 0 : parseInt(newValue[0]);
				newValue[1] = isNaN(parseInt(newValue[1])) ? 0 : parseInt(newValue[1]);

				if (newValue[0] >= newValue[1]) {
					return;
				}

				// calculate the new start and end values
				var currentRange = sliderObj.get();
				var rangeStart;
				var rangeEnd;
				if (newValue[0] >= currentRange[1] || newValue[1] <= currentRange[0]) {
					// new min/max exceeds current range
					// reset the start/end values to the min/max
					rangeStart = newValue[0];
					rangeEnd = newValue[1]
				} else {
					// otherwise allow start/end to be "pushed" by moving
					// min/max values
					rangeStart = Math.max(newValue[0], currentRange[0]);
					rangeEnd = Math.min(newValue[1], currentRange[1]);
				}

				// set start
				$scope.internalStart = $scope.start = rangeStart;
				$scope.internalEnd = $scope.end = rangeEnd;
				sliderObj.set([rangeStart, rangeEnd]);

				sliderObj.updateOptions({
					range: {
						min: newValue[0],
						max: newValue[1]
					},
					step: 1
				})
			});
		};

		return {
			scope: {
				start: '=',
				end: '=',
				min: '=',
				max: '='
			},
			link: link,
			templateUrl: haConfig.getTemplateUrl('ha-range-base-template.html')
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Toggle Directive
	// --------------------------------------------
	//
	// * **Class:** HaTableReflow
	// * **Author:** Mari Yamaha
	//
	// Enable reflow of table for responsive

	'use strict';

	var module = angular.module('haTableReflowModule', []);

	module.directive('haTableReflow', [ '$timeout', '$compile',
		function ($timeout, $compile) {
			return {
				restrict: 'A',
				scope: false,
				link: function ($scope, $el) {

					function updateTableMarkup(el) {
						var thead = el.find('thead');
						var tbody = el.find('tbody');
						var headers = [];

						// get list of headers
						thead.find('th').each(function() {
							var i18nContent = $(this).attr('i18n-content');
							if (i18nContent) {
								// if th has i18n-content, copy it to the label since the content might not be loaded yet
								headers.push('<b class="cell-label" i18n-content="' + i18nContent + '"></b>');
							} else {
								// otherwise add the html content
								var html = $(this).html();
								if (html) {
									headers.push('<b class="cell-label">' + html + '</b>');
								} else {
									headers.push('<b class="cell-label">&nbsp;</b>');
								}
							}
						});

						// add header to each td
						tbody.find('tr').each(function() {
							$(this).find('th,td').each(function(index) {
								// remove the cell-label if it was previously added
								$(this).find('.cell-label').remove();

								// add cell-label with the appropriate label content
								if (headers[index]) {
									// compile it to make sure the content such as sitecore string is loaded
									$(this).prepend($compile(headers[index])($(this).scope()));
								} else {
									// add blank if header is missing, usually when th has col span
									$(this).prepend('<b class="cell-label">&nbsp;</b>');
								}
							});
						});

						$timeout(function() {
							updateCellHeight(el);
						}, 0);
					}

					// adjust td height to avoid label exceeding the td
					function updateCellHeight(el) {
						var tbody = el.find('tbody');
						tbody.find('tr').each(function() {
							$(this).find('td').each(function() {
								$(this).css('height', 'auto');
								var labelHeight = $(this).find('.cell-label').height();
								var cellHeight = $(this).height();
								if (labelHeight > cellHeight) {
									$(this).height(labelHeight);
								} else {
									$(this).height(cellHeight); // fix the issue of table exceeding the window size when going from landscape to portrait
								}
							});
						});
					}

					if ($scope.$root.isMobile) {
						$timeout(function() {
							var watchList = [];

							// find ng-repeat to determine model to watch
							$el.find('[ng-repeat]').each(function() {
								var repeatStr = $(this).attr('ng-repeat');
								var repeatList = repeatStr.split(' ');
								for (var i = 0; i < repeatList.length; i++) {
									// find the model name followed by the word 'in'
									if (repeatList[i] === 'in') {
										// add to list of arrays to watch
										var watchModel = repeatList[i + 1];
										if (watchList.indexOf(watchModel) === -1) {
											watchList.push(watchModel);
										}
										break;
									}
								}
							});

							// add watchCollection for each array
							angular.forEach(watchList, function(watchItem) {
								$scope.$watchCollection(watchItem, function() {
									updateTableMarkup($el);
								});
							});

							var updateCellPromise;

							$(window).on('resize orientationchange', function () {
								if (updateCellPromise) {
									$timeout.cancel(updateCellPromise);
								}
								updateCellPromise = $timeout(function() {
									updateCellHeight($el);
								}, 100);
							});
						}, 0);
					}
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Disable on Click
	// --------------------------------------------
	//
	// * **Class:** haDisableOnClick
	// * **Author:** Mark Hagelberg
	//
	// Disable anchor tag (link) after first click, and add a spinner to the right.
	// Useful for eliminating repeated IIS requests (multi-clicks) when page response is slow.
	// To be used only on <a href="/url"> elements with a button-like UI.

	'use strict';

	var module = angular.module('haDisableOnClickModule', []);

	module.directive('haDisableOnClick', ['$timeout', '$window', function ($timeout, $window) {
		return {
			restrict: 'A',
			link: linkFn,
			scope: true,
			template: '<a ng-mouseup="disableHref()" ng-transclude></a>',
			transclude: true,
			replace: true
		};

		function linkFn(scope, element, attrs) {
			var whiteOrDefault = attrs.white !== undefined ? 'white ' : '';
			var spinner = '<div class="ha-loading-spinner ' + whiteOrDefault + 'small"><div><div></div></div></div>';

			scope.disableHref = function () {
				if (!scope.hrefIsDisabled && !!attrs['href']) {
					var hrefValue = attrs.href;
					scope.hrefIsDisabled = true;
					element.append(spinner).focus().attr('href', '#');

					$timeout(function () {
						$window.location = hrefValue;

						$timeout(function () {
							// restore href if user has waited a while
							element.attr('href', hrefValue);
							scope.hrefIsDisabled = false;
						}, 60000);
					}, 300);
				}	
			};

			element.on('$destroy', function () {
				scope.hrefIsDisabled = false;
			});
		};

	}]);

})(angular);
;
(function (angular) {

	//
	// --------------------------------------------
	//
	// * **Class:** Launchdarkly Feature Flags
	// * **Author:** Francis Oyeniyi
	//
	// Get flags from  LaunchDarkly or C# session if already retrived. For pages without C# view Model object.
	// -Parameters-
	// Key:		Config value key for featureFlag key
	// type:	Type of Feature flag variant
	// variant:	value to assert for feature to display
	// useCache(optional): Do not unclude to prevent posibility of Evaluation commin from session.
	//
	// Example: {key:'ABGPhase2Features', type:'boolean',variant:true,useCache:true}
	// WARNING! Could create false positive result if variant matches HA.FeatureFlag DLL default values.
	//Make sure correct Feature Key is in Web.Config.
	'use strict';

	var module = angular.module('haLaunchDarklyModule');


	module.directive('launchDarklyFeature', ["haLaunchDarklyService", function (haLaunchDarklyService) {

		return {
			restrict: 'A',
			controller: ['$scope', function($scope) {
				$scope.launchDarklyFlagReady = false;
			}],
			scope: true,
			link: function ($scope, element, attrs) {
				var attributes = $scope.$eval(attrs.launchDarklyFeature);
				var cacheEvaluation = attributes.useCache == undefined || attributes.useCache !== true ? false : true;
				element.addClass("hidden");
				haLaunchDarklyService.GetFlagVariant(attributes.key, attributes.type, attributes.variant, cacheEvaluation).then(
					function (response) {
						if (response.data === "DISABLED") {
							element.remove();
						}
						if (response.data === "ENABLED") {
							$scope.launchDarklyFlagReady = true;
							element.removeClass("hidden");
						}
					}
				, function(error) {
					console.error("Launch Darkly Error: "+error+"	KEY: "+attributes.key);
					element.remove();
				});
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Timed Hide
	// --------------------------------------------
	//
	// * **Class:** haTimedHide
	// * **Author:** Michael Toymil
	//
	// Prevent the display of the directive element if a cookie is set.

	'use strict';

	var module = angular.module('haTimedHideModule', ['ngCookies']);
	var COOKIE_BASE = 'HA_TIMED_HIDE_';
	module.directive('haTimedHide', ['$cookies', function ($cookies) {
		return {
			restrict: 'A',
			scope: true,
			link: function ($scope, element, attrs) {
				var minutes = parseInt(attrs.hideMinutes);
				minutes = isNaN(minutes) ? 0 : minutes;
				var id = attrs.hideId;
				if (!id) {
					element.addClass('shown');
					console.log("No id for ha-timed-hide");
					return;
				}
				var cookieName = COOKIE_BASE + id;
				if (!$cookies.get(cookieName)) {
					// No hide cookie found, show the element
					element.addClass('shown');
				} else if ($cookies.get(cookieName) != minutes) {
					// The expiration value changed, bust the old cookie.
					$cookies.remove(cookieName);
					element.addClass('shown');
				}

				$scope.timedClose = function() {
					element.removeClass('shown');
					if (minutes && id) {
						var d = new Date();
						d.setMinutes(d.getMinutes() + minutes)
						$cookies.put(cookieName, minutes, {expires: d, path: '/'});
					}
				}
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haResponsiveAttributeModule', []);

	module.directive('haResponsiveAttribute', ['$window', '$parse', function ($window, $parse) {

		return {
			restrict: 'A',
			link: function ($scope, $el, $attrs) {
				// grab attrs
				var attributeName = $attrs.attributeName;			// string
				var baseValue = $attrs.baseValue;					// css value string (e.g '800px');
				var breakpoints = $parse($attrs.breakpoints)();     // 2d array [[(num)deviceWidthStart, (num)deviceWidthEnd, (num)scalar]]

				var calculateAttribute = function() {
					// find our device width in the breakpoint array
					var dw = $window.innerWidth;
					var scalar = 1;
					for (var i = 0; i < breakpoints.length; i++) {
						if (dw > breakpoints[i][0] && dw < breakpoints[i][1]) {
							scalar = breakpoints[i][2];
							break;
						}
					} 
					$el.css(attributeName, "calc(" + baseValue + " * " + scalar + ")");
				}
				calculateAttribute();

				// bind to resize event;
				angular.element($window).on('resize', calculateAttribute);
				$scope.$on('$destroy', function() {
					angular.element($window).off('resize', calculateAttribute);
				});
			}
		};
	}]);

})(angular);;
(function (angular) {
	'use strict';

	var module = angular.module('haClampModule', []);

	module.directive('haClamp', function () {
		return {
			restrict: 'A',
			link: function ($scope, element, attrs) {
				if (!attrs["haClamp"]) {
					throw "ha-clamp requires a scope property name via the base attribute"
				}
				var observer = new ResizeObserver(function(entries) {
					entries.forEach(function(entry) {
						$scope[attrs["haClamp"]] = entry.target.scrollHeight > entry.contentRect.height;
						$scope.$apply();
					});
				});

				observer.observe(element[0]);
			}
		};
	});

})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haDelayAutoplayModule', []);

	module.directive('haDelayAutoplay', [function () {
		return {
			link: function($scope, $el) {
				console.log('haDelayAutoplay Link');
				var el = $el[0];
				var video = $el.find('video')[0];

				// Video and support checks
				if (!video) {
					return;
				}
				if (!IntersectionObserver) {
					video.play();
				}

				// Listen for video end (not applicable when looping)
				var ended = false;
				video.addEventListener('ended', function myHandler(e) {
					ended = true;
				}, false);

				var observer = new IntersectionObserver( function(entries, observer) {
					angular.forEach(entries, function(entry) {
						if (entry.intersectionRatio > 0.5 && !ended) {
							video.play();
						}
					});
				}, {threshold: 0.5});
				observer.observe(el);
			}
		};
	}]);

})(angular);
;
(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
		typeof define === 'function' && define.amd ? define(factory) :
			(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.LazyLoad = factory());
}(this, (function () {
	'use strict';

	function _extends() {
		_extends = Object.assign || function (target) {
			for (var i = 1; i < arguments.length; i++) {
				var source = arguments[i];

				for (var key in source) {
					if (Object.prototype.hasOwnProperty.call(source, key)) {
						target[key] = source[key];
					}
				}
			}

			return target;
		};

		return _extends.apply(this, arguments);
	}

	var runningOnBrowser = typeof window !== "undefined";
	var isBot = runningOnBrowser && !("onscroll" in window) || typeof navigator !== "undefined" && /(gle|ing|ro)bot|crawl|spider/i.test(navigator.userAgent);
	var supportsIntersectionObserver = runningOnBrowser && "IntersectionObserver" in window;
	var supportsClassList = runningOnBrowser && "classList" in document.createElement("p");
	var isHiDpi = runningOnBrowser && window.devicePixelRatio > 1;

	var defaultSettings = {
		elements_selector: ".lazy",
		container: isBot || runningOnBrowser ? document : null,
		threshold: 300,
		thresholds: null,
		data_src: "src",
		data_srcset: "srcset",
		data_sizes: "sizes",
		data_bg: "bg",
		data_bg_hidpi: "bg-hidpi",
		data_bg_multi: "bg-multi",
		data_bg_multi_hidpi: "bg-multi-hidpi",
		data_bg_set: "bg-set",
		data_poster: "poster",
		class_applied: "applied",
		class_loading: "loading",
		class_loaded: "loaded",
		class_error: "error",
		class_entered: "entered",
		class_exited: "exited",
		unobserve_completed: true,
		unobserve_entered: false,
		cancel_on_exit: true,
		callback_enter: null,
		callback_exit: null,
		callback_applied: null,
		callback_loading: null,
		callback_loaded: null,
		callback_error: null,
		callback_finish: null,
		callback_cancel: null,
		use_native: false,
		restore_on_error: false
	};
	var getExtendedSettings = function getExtendedSettings(customSettings) {
		return _extends({}, defaultSettings, customSettings);
	};

	/* Creates instance and notifies it through the window element */
	var createInstance = function createInstance(classObj, options) {
		var event;
		var eventString = "LazyLoad::Initialized";
		var instance = new classObj(options);

		try {
			// Works in modern browsers
			event = new CustomEvent(eventString, {
				detail: {
					instance: instance
				}
			});
		} catch (err) {
			// Works in Internet Explorer (all versions)
			event = document.createEvent("CustomEvent");
			event.initCustomEvent(eventString, false, false, {
				instance: instance
			});
		}

		window.dispatchEvent(event);
	};
	/* Auto initialization of one or more instances of lazyload, depending on the 
		options passed in (plain object or an array) */


	var autoInitialize = function autoInitialize(classObj, options) {
		if (!options) {
			return;
		}

		if (!options.length) {
			// Plain object
			createInstance(classObj, options);
		} else {
			// Array of objects
			for (var i = 0, optionsItem; optionsItem = options[i]; i += 1) {
				createInstance(classObj, optionsItem);
			}
		}
	};

	var SRC = "src";
	var SRCSET = "srcset";
	var SIZES = "sizes";
	var POSTER = "poster";
	var ORIGINALS = "llOriginalAttrs";
	var DATA = "data";

	var statusLoading = "loading";
	var statusLoaded = "loaded";
	var statusApplied = "applied";
	var statusEntered = "entered";
	var statusError = "error";
	var statusNative = "native";

	var dataPrefix = "data-";
	var statusDataName = "ll-status";
	var getData = function getData(element, attribute) {
		return element.getAttribute(dataPrefix + attribute);
	};
	var setData = function setData(element, attribute, value) {
		var attrName = dataPrefix + attribute;

		if (value === null) {
			element.removeAttribute(attrName);
			return;
		}

		element.setAttribute(attrName, value);
	};
	var getStatus = function getStatus(element) {
		return getData(element, statusDataName);
	};
	var setStatus = function setStatus(element, status) {
		return setData(element, statusDataName, status);
	};
	var resetStatus = function resetStatus(element) {
		return setStatus(element, null);
	};
	var hasEmptyStatus = function hasEmptyStatus(element) {
		return getStatus(element) === null;
	};
	var hasStatusLoading = function hasStatusLoading(element) {
		return getStatus(element) === statusLoading;
	};
	var hasStatusError = function hasStatusError(element) {
		return getStatus(element) === statusError;
	};
	var hasStatusNative = function hasStatusNative(element) {
		return getStatus(element) === statusNative;
	};
	var statusesAfterLoading = [statusLoading, statusLoaded, statusApplied, statusError];
	var hadStartedLoading = function hadStartedLoading(element) {
		return statusesAfterLoading.indexOf(getStatus(element)) >= 0;
	};

	var safeCallback = function safeCallback(callback, arg1, arg2, arg3) {
		if (!callback) {
			return;
		}

		if (arg3 !== undefined) {
			callback(arg1, arg2, arg3);
			return;
		}

		if (arg2 !== undefined) {
			callback(arg1, arg2);
			return;
		}

		callback(arg1);
	};

	var addClass = function addClass(element, className) {
		if (supportsClassList) {
			element.classList.add(className);
			return;
		}

		element.className += (element.className ? " " : "") + className;
	};
	var removeClass = function removeClass(element, className) {
		if (supportsClassList) {
			element.classList.remove(className);
			return;
		}

		element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " ").replace(/^\s+/, "").replace(/\s+$/, "");
	};

	var addTempImage = function addTempImage(element) {
		element.llTempImage = document.createElement("IMG");
	};
	var deleteTempImage = function deleteTempImage(element) {
		delete element.llTempImage;
	};
	var getTempImage = function getTempImage(element) {
		return element.llTempImage;
	};

	var unobserve = function unobserve(element, instance) {
		if (!instance) return;
		var observer = instance._observer;
		if (!observer) return;
		observer.unobserve(element);
	};
	var resetObserver = function resetObserver(observer) {
		observer.disconnect();
	};
	var unobserveEntered = function unobserveEntered(element, settings, instance) {
		if (settings.unobserve_entered) unobserve(element, instance);
	};

	var updateLoadingCount = function updateLoadingCount(instance, delta) {
		if (!instance) return;
		instance.loadingCount += delta;
	};
	var decreaseToLoadCount = function decreaseToLoadCount(instance) {
		if (!instance) return;
		instance.toLoadCount -= 1;
	};
	var setToLoadCount = function setToLoadCount(instance, value) {
		if (!instance) return;
		instance.toLoadCount = value;
	};
	var isSomethingLoading = function isSomethingLoading(instance) {
		return instance.loadingCount > 0;
	};
	var haveElementsToLoad = function haveElementsToLoad(instance) {
		return instance.toLoadCount > 0;
	};

	var getSourceTags = function getSourceTags(parentTag) {
		var sourceTags = [];

		for (var i = 0, childTag; childTag = parentTag.children[i]; i += 1) {
			if (childTag.tagName === "SOURCE") {
				sourceTags.push(childTag);
			}
		}

		return sourceTags;
	};

	var forEachPictureSource = function forEachPictureSource(element, fn) {
		var parent = element.parentNode;

		if (!parent || parent.tagName !== "PICTURE") {
			return;
		}

		var sourceTags = getSourceTags(parent);
		sourceTags.forEach(fn);
	};
	var forEachVideoSource = function forEachVideoSource(element, fn) {
		var sourceTags = getSourceTags(element);
		sourceTags.forEach(fn);
	};

	var attrsSrc = [SRC];
	var attrsSrcPoster = [SRC, POSTER];
	var attrsSrcSrcsetSizes = [SRC, SRCSET, SIZES];
	var attrsData = [DATA];
	var hasOriginalAttrs = function hasOriginalAttrs(element) {
		return !!element[ORIGINALS];
	};
	var getOriginalAttrs = function getOriginalAttrs(element) {
		return element[ORIGINALS];
	};
	var deleteOriginalAttrs = function deleteOriginalAttrs(element) {
		return delete element[ORIGINALS];
	}; // ## SAVE ##

	var setOriginalsObject = function setOriginalsObject(element, attributes) {
		if (hasOriginalAttrs(element)) {
			return;
		}

		var originals = {};
		attributes.forEach(function (attribute) {
			originals[attribute] = element.getAttribute(attribute);
		});
		element[ORIGINALS] = originals;
	};
	var saveOriginalBackgroundStyle = function saveOriginalBackgroundStyle(element) {
		if (hasOriginalAttrs(element)) {
			return;
		}

		element[ORIGINALS] = {
			backgroundImage: element.style.backgroundImage
		};
	}; // ## RESTORE ##

	var setOrResetAttribute = function setOrResetAttribute(element, attrName, value) {
		if (!value) {
			element.removeAttribute(attrName);
			return;
		}

		element.setAttribute(attrName, value);
	};

	var restoreOriginalAttrs = function restoreOriginalAttrs(element, attributes) {
		if (!hasOriginalAttrs(element)) {
			return;
		}

		var originals = getOriginalAttrs(element);
		attributes.forEach(function (attribute) {
			setOrResetAttribute(element, attribute, originals[attribute]);
		});
	};
	var restoreOriginalBgImage = function restoreOriginalBgImage(element) {
		if (!hasOriginalAttrs(element)) {
			return;
		}

		var originals = getOriginalAttrs(element);
		element.style.backgroundImage = originals.backgroundImage;
	};

	var manageApplied = function manageApplied(element, settings, instance) {
		addClass(element, settings.class_applied);
		setStatus(element, statusApplied); // Instance is not provided when loading is called from static class

		if (!instance) return;

		if (settings.unobserve_completed) {
			// Unobserve now because we can't do it on load
			unobserve(element, settings);
		}

		safeCallback(settings.callback_applied, element, instance);
	};
	var manageLoading = function manageLoading(element, settings, instance) {
		addClass(element, settings.class_loading);
		setStatus(element, statusLoading); // Instance is not provided when loading is called from static class

		if (!instance) return;
		updateLoadingCount(instance, +1);
		safeCallback(settings.callback_loading, element, instance);
	};
	var setAttributeIfValue = function setAttributeIfValue(element, attrName, value) {
		if (!value) {
			return;
		}

		element.setAttribute(attrName, value);
	};
	var setImageAttributes = function setImageAttributes(element, settings) {
		setAttributeIfValue(element, SIZES, getData(element, settings.data_sizes));
		setAttributeIfValue(element, SRCSET, getData(element, settings.data_srcset));
		setAttributeIfValue(element, SRC, getData(element, settings.data_src));
	};
	var setSourcesImg = function setSourcesImg(imgEl, settings) {
		forEachPictureSource(imgEl, function (sourceTag) {
			setOriginalsObject(sourceTag, attrsSrcSrcsetSizes);
			setImageAttributes(sourceTag, settings);
		});
		setOriginalsObject(imgEl, attrsSrcSrcsetSizes);
		setImageAttributes(imgEl, settings);
	};
	var setSourcesIframe = function setSourcesIframe(iframe, settings) {
		setOriginalsObject(iframe, attrsSrc);
		setAttributeIfValue(iframe, SRC, getData(iframe, settings.data_src));
	};
	var setSourcesVideo = function setSourcesVideo(videoEl, settings) {
		forEachVideoSource(videoEl, function (sourceEl) {
			setOriginalsObject(sourceEl, attrsSrc);
			setAttributeIfValue(sourceEl, SRC, getData(sourceEl, settings.data_src));
		});
		setOriginalsObject(videoEl, attrsSrcPoster);
		setAttributeIfValue(videoEl, POSTER, getData(videoEl, settings.data_poster));
		setAttributeIfValue(videoEl, SRC, getData(videoEl, settings.data_src));
		videoEl.load();
	};
	var setSourcesObject = function setSourcesObject(object, settings) {
		setOriginalsObject(object, attrsData);
		setAttributeIfValue(object, DATA, getData(object, settings.data_src));
	};
	var setBackground = function setBackground(element, settings, instance) {
		var bg1xValue = getData(element, settings.data_bg);
		var bgHiDpiValue = getData(element, settings.data_bg_hidpi);
		var bgDataValue = isHiDpi && bgHiDpiValue ? bgHiDpiValue : bg1xValue;
		if (!bgDataValue) return;
		element.style.backgroundImage = "url(\"".concat(bgDataValue, "\")");
		getTempImage(element).setAttribute(SRC, bgDataValue);
		manageLoading(element, settings, instance);
	}; // NOTE: THE TEMP IMAGE TRICK CANNOT BE DONE WITH data-multi-bg
	// BECAUSE INSIDE ITS VALUES MUST BE WRAPPED WITH URL() AND ONE OF THEM
	// COULD BE A GRADIENT BACKGROUND IMAGE

	var setMultiBackground = function setMultiBackground(element, settings, instance) {
		var bg1xValue = getData(element, settings.data_bg_multi);
		var bgHiDpiValue = getData(element, settings.data_bg_multi_hidpi);
		var bgDataValue = isHiDpi && bgHiDpiValue ? bgHiDpiValue : bg1xValue;

		if (!bgDataValue) {
			return;
		}

		element.style.backgroundImage = bgDataValue;
		manageApplied(element, settings, instance);
	};
	var setImgsetBackground = function setImgsetBackground(element, settings, instance) {
		var bgImgSetDataValue = getData(element, settings.data_bg_set);

		if (!bgImgSetDataValue) {
			return;
		}

		var imgSetValues = bgImgSetDataValue.split("|");
		var bgImageValues = imgSetValues.map(function (value) {
			return "image-set(".concat(value, ")");
		});
		element.style.backgroundImage = bgImageValues.join(); // Temporary fix for Chromeium with the -webkit- prefix

		if (element.style.backgroundImage === '') {
			bgImageValues = imgSetValues.map(function (value) {
				return "-webkit-image-set(".concat(value, ")");
			});
			element.style.backgroundImage = bgImageValues.join();
		}

		manageApplied(element, settings, instance);
	};
	var setSourcesFunctions = {
		IMG: setSourcesImg,
		IFRAME: setSourcesIframe,
		VIDEO: setSourcesVideo,
		OBJECT: setSourcesObject
	};
	var setSourcesNative = function setSourcesNative(element, settings) {
		var setSourcesFunction = setSourcesFunctions[element.tagName];

		if (!setSourcesFunction) {
			return;
		}

		setSourcesFunction(element, settings);
	};
	var setSources = function setSources(element, settings, instance) {
		var setSourcesFunction = setSourcesFunctions[element.tagName];

		if (!setSourcesFunction) {
			return;
		}

		setSourcesFunction(element, settings);
		manageLoading(element, settings, instance);
	};

	var elementsWithLoadEvent = ["IMG", "IFRAME", "VIDEO", "OBJECT"];
	var hasLoadEvent = function hasLoadEvent(element) {
		return elementsWithLoadEvent.indexOf(element.tagName) > -1;
	};
	var checkFinish = function checkFinish(settings, instance) {
		if (instance && !isSomethingLoading(instance) && !haveElementsToLoad(instance)) {
			safeCallback(settings.callback_finish, instance);
		}
	};
	var addEventListener = function addEventListener(element, eventName, handler) {
		element.addEventListener(eventName, handler);
		element.llEvLisnrs[eventName] = handler;
	};
	var removeEventListener = function removeEventListener(element, eventName, handler) {
		element.removeEventListener(eventName, handler);
	};
	var hasEventListeners = function hasEventListeners(element) {
		return !!element.llEvLisnrs;
	};
	var addEventListeners = function addEventListeners(element, loadHandler, errorHandler) {
		if (!hasEventListeners(element)) element.llEvLisnrs = {};
		var loadEventName = element.tagName === "VIDEO" ? "loadeddata" : "load";
		addEventListener(element, loadEventName, loadHandler);
		addEventListener(element, "error", errorHandler);
	};
	var removeEventListeners = function removeEventListeners(element) {
		if (!hasEventListeners(element)) {
			return;
		}

		var eventListeners = element.llEvLisnrs;

		for (var eventName in eventListeners) {
			var handler = eventListeners[eventName];
			removeEventListener(element, eventName, handler);
		}

		delete element.llEvLisnrs;
	};
	var doneHandler = function doneHandler(element, settings, instance) {
		deleteTempImage(element);
		updateLoadingCount(instance, -1);
		decreaseToLoadCount(instance);
		removeClass(element, settings.class_loading);

		if (settings.unobserve_completed) {
			unobserve(element, instance);
		}
	};
	var loadHandler = function loadHandler(event, element, settings, instance) {
		var goingNative = hasStatusNative(element);
		doneHandler(element, settings, instance);
		addClass(element, settings.class_loaded);
		setStatus(element, statusLoaded);
		safeCallback(settings.callback_loaded, element, instance);
		if (!goingNative) checkFinish(settings, instance);
	};
	var errorHandler = function errorHandler(event, element, settings, instance) {
		var goingNative = hasStatusNative(element);
		doneHandler(element, settings, instance);
		addClass(element, settings.class_error);
		setStatus(element, statusError);
		safeCallback(settings.callback_error, element, instance);
		if (settings.restore_on_error) restoreOriginalAttrs(element, attrsSrcSrcsetSizes);
		if (!goingNative) checkFinish(settings, instance);
	};
	var addOneShotEventListeners = function addOneShotEventListeners(element, settings, instance) {
		var elementToListenTo = getTempImage(element) || element;

		if (hasEventListeners(elementToListenTo)) {
			// This happens when loading is retried twice
			return;
		}

		var _loadHandler = function _loadHandler(event) {
			loadHandler(event, element, settings, instance);
			removeEventListeners(elementToListenTo);
		};

		var _errorHandler = function _errorHandler(event) {
			errorHandler(event, element, settings, instance);
			removeEventListeners(elementToListenTo);
		};

		addEventListeners(elementToListenTo, _loadHandler, _errorHandler);
	};

	var loadBackground = function loadBackground(element, settings, instance) {
		addTempImage(element);
		addOneShotEventListeners(element, settings, instance);
		saveOriginalBackgroundStyle(element);
		setBackground(element, settings, instance);
		setMultiBackground(element, settings, instance);
		setImgsetBackground(element, settings, instance);
	};

	var loadRegular = function loadRegular(element, settings, instance) {
		addOneShotEventListeners(element, settings, instance);
		setSources(element, settings, instance);
	};

	var load = function load(element, settings, instance) {
		if (hasLoadEvent(element)) {
			loadRegular(element, settings, instance);
		} else {
			loadBackground(element, settings, instance);
		}
	};
	var loadNative = function loadNative(element, settings, instance) {
		element.setAttribute("loading", "lazy");
		addOneShotEventListeners(element, settings, instance);
		setSourcesNative(element, settings);
		setStatus(element, statusNative);
	};

	var removeImageAttributes = function removeImageAttributes(element) {
		element.removeAttribute(SRC);
		element.removeAttribute(SRCSET);
		element.removeAttribute(SIZES);
	};

	var resetSourcesImg = function resetSourcesImg(element) {
		forEachPictureSource(element, function (sourceTag) {
			removeImageAttributes(sourceTag);
		});
		removeImageAttributes(element);
	};

	var restoreImg = function restoreImg(imgEl) {
		forEachPictureSource(imgEl, function (sourceEl) {
			restoreOriginalAttrs(sourceEl, attrsSrcSrcsetSizes);
		});
		restoreOriginalAttrs(imgEl, attrsSrcSrcsetSizes);
	};
	var restoreVideo = function restoreVideo(videoEl) {
		forEachVideoSource(videoEl, function (sourceEl) {
			restoreOriginalAttrs(sourceEl, attrsSrc);
		});
		restoreOriginalAttrs(videoEl, attrsSrcPoster);
		videoEl.load();
	};
	var restoreIframe = function restoreIframe(iframeEl) {
		restoreOriginalAttrs(iframeEl, attrsSrc);
	};
	var restoreObject = function restoreObject(objectEl) {
		restoreOriginalAttrs(objectEl, attrsData);
	};
	var restoreFunctions = {
		IMG: restoreImg,
		IFRAME: restoreIframe,
		VIDEO: restoreVideo,
		OBJECT: restoreObject
	};

	var restoreAttributes = function restoreAttributes(element) {
		var restoreFunction = restoreFunctions[element.tagName];

		if (!restoreFunction) {
			restoreOriginalBgImage(element);
			return;
		}

		restoreFunction(element);
	};

	var resetClasses = function resetClasses(element, settings) {
		if (hasEmptyStatus(element) || hasStatusNative(element)) {
			return;
		}

		removeClass(element, settings.class_entered);
		removeClass(element, settings.class_exited);
		removeClass(element, settings.class_applied);
		removeClass(element, settings.class_loading);
		removeClass(element, settings.class_loaded);
		removeClass(element, settings.class_error);
	};

	var restore = function restore(element, settings) {
		restoreAttributes(element);
		resetClasses(element, settings);
		resetStatus(element);
		deleteOriginalAttrs(element);
	};

	var cancelLoading = function cancelLoading(element, entry, settings, instance) {
		if (!settings.cancel_on_exit) return;
		if (!hasStatusLoading(element)) return;
		if (element.tagName !== "IMG") return; //Works only on images

		removeEventListeners(element);
		resetSourcesImg(element);
		restoreImg(element);
		removeClass(element, settings.class_loading);
		updateLoadingCount(instance, -1);
		resetStatus(element);
		safeCallback(settings.callback_cancel, element, entry, instance);
	};

	var onEnter = function onEnter(element, entry, settings, instance) {
		var dontLoad = hadStartedLoading(element);
		/* Save status 
		before setting it, to prevent loading it again. Fixes #526. */

		setStatus(element, statusEntered);
		addClass(element, settings.class_entered);
		removeClass(element, settings.class_exited);
		unobserveEntered(element, settings, instance);
		safeCallback(settings.callback_enter, element, entry, instance);
		if (dontLoad) return;
		load(element, settings, instance);
	};
	var onExit = function onExit(element, entry, settings, instance) {
		if (hasEmptyStatus(element)) return; //Ignore the first pass, at landing

		addClass(element, settings.class_exited);
		cancelLoading(element, entry, settings, instance);
		safeCallback(settings.callback_exit, element, entry, instance);
	};

	var tagsWithNativeLazy = ["IMG", "IFRAME", "VIDEO"];
	var shouldUseNative = function shouldUseNative(settings) {
		return settings.use_native && "loading" in HTMLImageElement.prototype;
	};
	var loadAllNative = function loadAllNative(elements, settings, instance) {
		elements.forEach(function (element) {
			if (tagsWithNativeLazy.indexOf(element.tagName) === -1) {
				return;
			}

			loadNative(element, settings, instance);
		});
		setToLoadCount(instance, 0);
	};

	var isIntersecting = function isIntersecting(entry) {
		return entry.isIntersecting || entry.intersectionRatio > 0;
	};

	var getObserverSettings = function getObserverSettings(settings) {
		return {
			root: settings.container === document ? null : settings.container,
			rootMargin: settings.thresholds || settings.threshold + "px"
		};
	};

	var intersectionHandler = function intersectionHandler(entries, settings, instance) {
		entries.forEach(function (entry) {
			return isIntersecting(entry) ? onEnter(entry.target, entry, settings, instance) : onExit(entry.target, entry, settings, instance);
		});
	};

	var observeElements = function observeElements(observer, elements) {
		elements.forEach(function (element) {
			observer.observe(element);
		});
	};
	var updateObserver = function updateObserver(observer, elementsToObserve) {
		resetObserver(observer);
		observeElements(observer, elementsToObserve);
	};
	var setObserver = function setObserver(settings, instance) {
		if (!supportsIntersectionObserver || shouldUseNative(settings)) {
			return;
		}

		instance._observer = new IntersectionObserver(function (entries) {
			intersectionHandler(entries, settings, instance);
		}, getObserverSettings(settings));
	};

	var toArray = function toArray(nodeSet) {
		return Array.prototype.slice.call(nodeSet);
	};
	var queryElements = function queryElements(settings) {
		return settings.container.querySelectorAll(settings.elements_selector);
	};
	var excludeManagedElements = function excludeManagedElements(elements) {
		return toArray(elements).filter(hasEmptyStatus);
	};
	var hasError = function hasError(element) {
		return hasStatusError(element);
	};
	var filterErrorElements = function filterErrorElements(elements) {
		return toArray(elements).filter(hasError);
	};
	var getElementsToLoad = function getElementsToLoad(elements, settings) {
		return excludeManagedElements(elements || queryElements(settings));
	};

	var retryLazyLoad = function retryLazyLoad(settings, instance) {
		var errorElements = filterErrorElements(queryElements(settings));
		errorElements.forEach(function (element) {
			removeClass(element, settings.class_error);
			resetStatus(element);
		});
		instance.update();
	};
	var setOnlineCheck = function setOnlineCheck(settings, instance) {
		if (!runningOnBrowser) {
			return;
		}

		instance._onlineHandler = function () {
			retryLazyLoad(settings, instance);
		};

		window.addEventListener("online", instance._onlineHandler);
	};
	var resetOnlineCheck = function resetOnlineCheck(instance) {
		if (!runningOnBrowser) {
			return;
		}

		window.removeEventListener("online", instance._onlineHandler);
	};

	var LazyLoad = function LazyLoad(customSettings, elements) {
		var settings = getExtendedSettings(customSettings);
		this._settings = settings;
		this.loadingCount = 0;
		setObserver(settings, this);
		setOnlineCheck(settings, this);
		this.update(elements);
	};

	LazyLoad.prototype = {
		update: function update(givenNodeset) {
			var settings = this._settings;
			var elementsToLoad = getElementsToLoad(givenNodeset, settings);
			setToLoadCount(this, elementsToLoad.length);

			if (isBot || !supportsIntersectionObserver) {
				this.loadAll(elementsToLoad);
				return;
			}

			if (shouldUseNative(settings)) {
				loadAllNative(elementsToLoad, settings, this);
				return;
			}

			updateObserver(this._observer, elementsToLoad);
		},
		destroy: function destroy() {
			// Observer
			if (this._observer) {
				this._observer.disconnect();
			} // Clean handlers


			resetOnlineCheck(this); // Clean custom attributes on elements

			queryElements(this._settings).forEach(function (element) {
				deleteOriginalAttrs(element);
			}); // Delete all internal props

			delete this._observer;
			delete this._settings;
			delete this._onlineHandler;
			delete this.loadingCount;
			delete this.toLoadCount;
		},
		loadAll: function loadAll(elements) {
			var _this = this;

			var settings = this._settings;
			var elementsToLoad = getElementsToLoad(elements, settings);
			elementsToLoad.forEach(function (element) {
				unobserve(element, _this);
				load(element, settings, _this);
			});
		},
		restoreAll: function restoreAll() {
			var settings = this._settings;
			queryElements(settings).forEach(function (element) {
				restore(element, settings);
			});
		}
	};

	LazyLoad.load = function (element, customSettings) {
		var settings = getExtendedSettings(customSettings);
		load(element, settings);
	};

	LazyLoad.resetStatus = function (element) {
		resetStatus(element);
	}; // Automatic instances creation if required (useful for async script loading)


	if (runningOnBrowser) {
		autoInitialize(LazyLoad, window.lazyLoadOptions);
	}

	return LazyLoad;

})));
;
(function (angular) {
	'use strict';

	var module = angular.module('haAccordionModule', []);

	module.directive('haAccordion', function () {
		return {
			restrict: 'A',
			link: function ($scope, element, attrs) {
				var linkPanels = attrs["linkPanels"]; // Should the panels close when another is opened?
				var $allPanels = element.find('[accordion-panel]');
				var animating = false;
				$allPanels.each(function(i, $panel) {
					$panel = angular.element($panel);
					var $panelHeader = $panel.find('[accordion-panel-header]');
					var $panelBody = $panel.find('[accordion-panel-body]');
					$panelHeader.click(function(e) {
						if (animating) {
							return;
						}
						animating = true;
						if (linkPanels) {
							$allPanels.not($panel).addClass('collapsed').removeClass('expanded').find('[accordion-panel-body]').slideUp({
								duration: 300,
								queue: false
							});
						}
						$panelBody.slideToggle({
							duration: 300,
							queue: false,
							start: function() {
								$panel.toggleClass('expanded');
								$panel.toggleClass('collapsed');
							},
							always: function() {
								animating = false;
							}
						});
					});
				});
			}
		};
	});

})(angular);
;
(function (angular) {

	// Ha Localize Name Directive
	// --------------------------------------------
	//
	// * **Class:** HaLocalizeName
	// * **Author:** Cinthia Miller
	//
	// Filters for names and dates that formats it for different countries

	'use strict';

	var module = angular.module('haLocalizeNameModule', []);

	module.filter('localName', [function () {
		var formats = {
			'en': '{{ firstName }} {{ lastName }}',
			'en-au': '{{ firstName }} {{ lastName }}',
			'en-nz': '{{ firstName }} {{ lastName }}',
			'zh-cn': '{{ lastName }} {{ firstName }}',
			'ko-kr': '{{ lastName }} {{ firstName }}',
			'ja-jp': '{{ lastName }} {{ firstName }}',
			'zh-tw': '{{ lastName }} {{ firstName }}'
		};

		return function (firstName, lastName, code) {

			if (formats[code]) {
				var localFormatted = formats[code].replace(/\{\{ firstName \}\}/, firstName);
				localFormatted = localFormatted.replace(/\{\{ lastName \}\}/, lastName);
				return localFormatted;
			}
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Localize Date Directives
	// --------------------------------------------
	//
	// * **Class:** HaLocalizeDates
	// * **Author:** Cinthia Miller
	//
	// A filter for currency that formats it for different countries

	'use strict';

	var module = angular.module('haLocalizeDateModule', []);

	module.filter('localDate', ['$filter', function ($filter) {
		var formats = {
			'en': 'EEE M/d',
			'en-au': 'EEE M/d',
			'en-nz': 'EEE M/d',
			'zh-cn': 'M月d日 EEE',
			'ko-kr': 'M월 d일 EEE',
			'ja-jp': 'M月d日(EEE)',
			'zh-tw': 'M月d日 EEE'
		};

		return function (date, code) {
			var typeDate = new Date();
			var standardDateFilterFn = $filter('date');

			if (formats[code]) {
				if (date instanceof Date) {
					typeDate = date;
				}
				else {
					typeDate = new Date(date);
				}

				return standardDateFilterFn(typeDate, formats[code]);
			}
		};
	}]);

	module.filter('localShortDate', ['$filter', function ($filter) {
		var formats = {
			'en': 'MM/dd/yyyy',
			'en-au': 'MM/dd/yyyy',
			'en-nz': 'MM/dd/yyyy',
			'zh-cn': 'yyyy/MM/dd',
			'ko-kr': 'yyyy/MM/dd',
			'ja-jp': 'yyyy/MM/dd',
			'zh-tw': 'yyyy/MM/dd'
		};

		return function (date, code) {
			var typeDate = new Date();
			var standardDateFilterFn = $filter('date');

			if (formats[code]) {
				if (date instanceof Date) {
					typeDate = date;
				}
				else if (/^\d{4}-\d{1,2}-\d{1,2}/gi.test(date)) {
					var dateSplit = date.split('T')[0].split('-');

					typeDate = (dateSplit.length === 3) ? new Date(parseInt(dateSplit[0], 10), parseInt(dateSplit[1], 10) - 1, parseInt(dateSplit[2], 10)) : new Date();
				} else {
					typeDate = new Date(date);
				}

				return standardDateFilterFn(typeDate, formats[code]);
			}
		};
	}]);

	module.filter('dateWithDayButNoYear', ['$filter', function ($filter) {
		var defaultFormat = 'EEE, MMM d';
		var formats = {
			'en': 'EEE, MMM d',
			'en-au': 'EEE, MMM d',
			'en-nz': 'EEE, MMM d',
			'zh-cn': 'M月d日 EEE',
			'ko-kr': 'M월 d일 EEE',
			'ja-jp': 'M月d日(EEE)',
			'zh-tw': 'M月d日 EEE'
		};

		return function (date, code) {
			return $filter('date')(date, formats[code] || defaultFormat);
		};
	}]);

	module.filter('localShortDateWithMonthandDay', ['$filter', function ($filter) {
		var defaultFormat = 'MMM d';
		var formats = {
			'en': 'MMM d',
			'en-au': 'MMM d',
			'en-nz': 'MMM d',
			'zh-cn': 'M月d日',
			'ko-kr': 'M월 d일',
			'ja-jp': 'M月d日',
			'zh-tw': 'M月d日'
		};

		return function (date, code) {
			return $filter('date')(date, formats[code] || defaultFormat);
		};
	}]);

	module.filter('localTime', ['$filter', function ($filter) {
		var defaultFormat = 'hh:mma';
		var formats = {
			'en': 'h:mma',
			'en-au': 'h:mma',
			'en-nz': 'h:mma',
			'zh-cn': 'H:mm',
			'ko-kr': 'H:mm',
			'ja-jp': 'H:mm',
			'zh-tw': 'H:mm'
		};


		return function (time, code) {
			return $filter('date')(time, formats[code] || defaultFormat);
		};

	}]);

	//Sun Jan 16,2021
	module.filter('localFullDateAbbreviated', ['$filter', function ($filter) {
		var defaultFormat = 'EEE MMM d,yyyy';
		var formats = {
			'en': 'EEE MMM d,yyyy'
		};

		return function (date, code) {
			return $filter('date')(date, formats[code] || defaultFormat);
		};
	}]);

})(angular);
;
(function (angular) {

	// Ha Currency Directive
	// --------------------------------------------
	//
	// * **Class:** HaCurrency
	// * **Author:** Josh Nielsen
	//
	// A filter for currency that formats it for different countries

	'use strict';

	var module = angular.module('haCurrencyModule', []);

	module.filter('localCurrency', ['$compile', function () {
		var types = {
			USD: ' <span class="currency-type">USD</span>',
			AUD: ' <span class="currency-type">AUD</span>',
			NZD: ' <span class="currency-type">NZD</span>',
			TWD: '<span class="currency-type">NT</span>',
			MILES: ' <span class="currency-type">' + angular.element('#SC_MilesText_Display').html() + '</span>'
		};

		var formats = {
			USD: '<span class="currency-symbol">$</span>{{ amount }}{0}',
			AUD: '<span class="currency-symbol">$</span>{{ amount }}{0}',
			NZD: '<span class="currency-symbol">$</span>{{ amount }}{0}',
			CNY: '<span class="currency-symbol">¥</span>{{ amount }}',
			KRW: '<span class="currency-symbol">₩</span>{{ amount }}',
			JPY: '<span class="currency-symbol">¥</span>{{ amount }}',
			TWD: '{0}<span class="currency-symbol">$</span>{{ amount }}',
			MILES: '{{ amount }}{0}'
		};

		var replaceNumberWithCommas = function (yourNumber) {
			//Seperates the components of the number
			var n = yourNumber.toString().split('.');
			//Comma-fies the first part
			n[0] = n[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
			//Combines the two sections
			return n.join('.');
		};

		var signCurrency = function (rawAmount, formatted) {
			if (rawAmount > -1) {
				return formatted + ' ' + angular.element('#MoreText').val();
			} else {
				return formatted.replace(/-([1-9]+)/g, function ($1, $2) {
						return $2;
					}) + ' ' + angular.element('#LessText').val();
			}
		};

		var localRoundUp = function (amount, code) {
			if (code === 'CNY') {
				return Math.ceil(amount / 10) * 10;
			}
			else if (code === 'KRW' || code === 'JPY') {
				return Math.ceil(amount / 100) * 100;
			} else {
				return Math.ceil(amount);
			}
		}

		return function (amount, code, noDecimal, signed, disableRoundOff, disableType, roundUp, needsUSDLabel) {
			var rounded;
			var commaFormatted;

			//validate decimal round off based on currency.
			if (code && !noDecimal) {
				noDecimal = ('USD AUD NZD'.indexOf(code.toUpperCase()) === -1);
			}

			if (!disableRoundOff) {
				if (noDecimal) {
					rounded = (roundUp) ? localRoundUp(amount, code) : Math.round(amount);
				} else {
					rounded = (Math.round(amount * 100) / 100).toFixed(2);
				}
			}
			else {
				rounded = amount;
			}

			if (typeof (rounded) !== 'undefined' && rounded != null) {
				commaFormatted = replaceNumberWithCommas(rounded);
			}

			// get rid of decimal places if MILES
			if (code === 'MILES') {
				commaFormatted = commaFormatted.split('.')[0];
			}

			if (formats[code]) {
				// Add or omit currency type
				var typeFormatted = formats[code].format(disableType ? '' : (types[code] || ''));
				if (code == 'USD' && !needsUSDLabel) {
					typeFormatted = formats[code].format('');
				}
				// Inject amount
				var denominationFormatted = typeFormatted.replace(/\{\{.*\}\}/, commaFormatted);
				if (signed) {
					return signCurrency(amount, denominationFormatted);
				} else {
					//console.log(denominationFormatted);
					return denominationFormatted;
				}
			}
		};
	}]);

	module.filter('superCurrency', [
		'$log',
		'$filter',
		'$locale',
		'$sce',

		function ($log, $filter, $locale, $sce) {
			var formats = {
				USD: '<span class="currency-symbol">$</span><span class="currency-dollars">{{ dollars }}</span><span class="currency-cents">{{ cents }}</span>',
				AUD: '<span class="currency-symbol">$</span><span class="currency-dollars">{{ dollars }}</span><span class="currency-cents">{{ cents }}</span> <span class="currency-type">AUD</span>',
				NZD: '<span class="currency-symbol">$</span><span class="currency-dollars">{{ dollars }}</span><span class="currency-cents">{{ cents }}</span> <span class="currency-type">NZD</span>',
				CNY: '<span class="currency-symbol">¥</span><span class="currency-dollars">{{ dollars }}</span><span class="currency-cents">{{ cents }}</span>',
				KRW: '<span class="currency-symbol">₩</span><span class="currency-dollars">{{ dollars }}</span><span class="currency-cents">{{ cents }}</span>',
				JPY: '<span class="currency-symbol">¥</span><span class="currency-dollars">{{ dollars }}</span><span class="currency-cents">{{ cents }}</span>',
				TWD: '<span class="currency-type">NT</span><span class="currency-symbol">$</span><span class="currency-dollars">{{ dollars }}</span><span class="currency-cents">{{ cents }}</span>'
			};

			var buildHtml = function (code, dollars, cents) {
				var html = formats[code].replace('{{ dollars }}', dollars).replace('{{ cents }}', cents);
				return $sce.trustAsHtml(html);
			};

			return function (amount, code, noDecimal) {
				// $log.debug('superCurrency:', arguments);
				code = code || 'USD';
				var formats = $locale.NUMBER_FORMATS;
				var formattedAmount = $filter('currency')(amount, '');

				// Extract "dollars" and "cents" parts
				var dollars = formattedAmount.substring(0, formattedAmount.indexOf(formats.DECIMAL_SEP));
				var cents = formattedAmount.substring(formattedAmount.indexOf(formats.DECIMAL_SEP));

				if (noDecimal === true) {
					cents = '';
				}

				// In 'roundPrice' mode, display cents if no whole dollars
				if ((noDecimal === 'roundPrice') && (amount >= 1.0 || amount <= -1.0)) {
					cents = '';
				}

				// In 'roundSavings' mode, display cents if no whole dollars
				if ((noDecimal === 'roundSavings') && (amount >= 1.0 || amount <= -1.0)) {
					cents = '';
				}

				return buildHtml(code, dollars, cents);
			};
		}
	]);

	// currency without any fractional values.
	module.filter('noFractionCurrency', ['$filter', '$locale',	function(filter, locale) {
		var currencyFilter = filter('currency');
		var formats = locale.NUMBER_FORMATS;
		return function(amount, currencySymbol) {
			var value = currencyFilter(amount, currencySymbol);
			var sep = value.indexOf(formats.DECIMAL_SEP);
			return value.substring(0, sep);
		};
	}]);

	// makes view value be prefixed with currency symbol, model value without.
	// also restricts value to whole numbers
	module.directive('wholeCurrency', function () {
	    return {
	        require: 'ngModel',
	        link: function(scope, elem, attrs, modelCtrl) {
	        	var currencyCode = scope.$root.$currency;
	        	var currencySymbols = {
					USD: '$',
					AUD: '$',
					NZD: '$',
					CNY: '¥',
					KRW: '₩',
					JPY: '¥',
					TWD: 'NT$'
				};

	        	function formatDisplay(val){
	        		if (val) {
		            	val = val+'';
		            	if (val.match(/\d/g)) {
			            	var wholeNumber = Number(val.match(/\d/g).join('')).toFixed();
					        var withCurrency = currencySymbols[currencyCode] +''+ wholeNumber;

			            	if (withCurrency != val) {
				            	modelCtrl.$setViewValue(withCurrency);
				            	modelCtrl.$render();
				            }
			                return parseInt(wholeNumber);
			            } else {

			            	if (currencySymbols[currencyCode] != val) {
				            	modelCtrl.$setViewValue(currencySymbols[currencyCode]);
				            	modelCtrl.$render();
				            }
			            	return 0;
			            }
			        }
	            }
	            function formatModel(val){
	        		return currencySymbols[currencyCode] + val;
	            }
	            modelCtrl.$parsers.unshift(formatDisplay);
	            modelCtrl.$formatters.unshift(formatModel); // format value with currency on page load
	        }
	    }
	});

})(angular);
;
(function (angular) {

	// Currency No Decimals Directive
	// --------------------------------------------
	//
	// * **Class:** CurrencyNoDecimals
	// * **Author:** Cory Shaw
	//
	// Formats number as currency and removes decimals

	'use strict';

	var module = angular.module('currencyNoDecimalsFilter', []);

	module.filter('currencyNoDecimalsFilter', ['$filter', '$locale', function (filter, locale) {
		var currencyFilter = filter('currency');
		var formats = locale.NUMBER_FORMATS;
		return function (amount, currencySymbol) {
			var value = currencyFilter(amount, currencySymbol);
			var sep = value.indexOf(formats.DECIMAL_SEP);
			if (amount >= 0) {
				return value.substring(0, sep);
			}
			return value.substring(0, sep) + ')';
		};
	}]);

})(angular);
;
(function (angular) {

	// Add Icon Filter
	// --------------------------------------------
	//
	// * **Class:** addIconFilter
	// * **Author:** Cory Shaw
	//
	// Pass a string with an [icon] placeholder, and an icon name and the filter will replace the icon name with the proper html in the desired place for localization.

	'use strict';

	var module = angular.module('addIconFilter', []);

	module.filter('addIconFilter', ['$filter',
		function () {
			return function (string, icon) {
				var iconHTML = '<i class="ha-icon ' + icon + '"></i>';
				var updatedString = string.replace('[icon]', iconHTML);
				return updatedString;
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Rounding Filters
	// --------------------------------------------
	//
	// * **Class:** HARoundingFilters
	// * **Author:** Nathan Probst
	//
	// Filters for rounding prices and savings.

	'use strict';

	var module = angular.module('haRoundingFiltersModule', []);

	module.filter('roundPrice', [function () {
		return function (price) {
			// If more than $1.00, round up
			if ((price > 1.0) || (price < -1.0)) {
				return Math.ceil(price);
			}
			// Else leave unchanged
			return price;
		};
	}]);

	module.filter('roundSavings', [function () {
		return function (price) {
			// If more than $1.00, round down
			if ((price > 1.0) || (price < -1.0)) {
				return Math.floor(price);
			}
			// Else leave unchanged
			return price;
		};
	}]);

})(angular);
;
(function (angular) {

	// Add Icon Filter
	// --------------------------------------------
	//
	// * **Class:** addIconFilter
	// * **Author:** Cory Shaw
	//
	// Pass a string with an [icon] placeholder, and an icon name and the filter will replace the icon name with the proper html in the desired place for localization.

	'use strict';

	var module = angular.module('uinqueFilter', []);

	module.filter('unique', [function () {
		return function (collection, keyname) {
			var output = [];
			var keys = [];

			angular.forEach(collection, function (item) {
				var key = item[keyname];
				if (keys.indexOf(key) === -1) {
					keys.push(key);
					output.push(item);
				}
			});

			return output;
		};
	}]);

})(angular);
;
(function (angular) {
	'use strict';

	var module = angular.module('haDurationModule', []);

	module.filter('formatMinutes', [function () {
		return function (minutes) {
			var hours = Math.floor(minutes / 60);
			minutes = minutes % 60;
			var hourStr = hours ? hours + 'h' : '';
			var minuteStr = minutes ? minutes + 'm' : '';
			var returnStr = hourStr + ' ' + minuteStr;
			return returnStr.trim();
		};
	}]);

	// formats duration from "hh:mm:ss"
	module.filter('formatTimeString', [function () {
		return function (timeString) {
			var values = timeString.split(":");
			if (values.length <= 1) {
				return timeString;
			}
			var hours = Number(values[0]);
			var minutes = Number(values[1]);
			var hourStr = hours ? hours + 'h' : '';
			var minuteStr = minutes ? minutes + 'm' : '';
			var returnStr = hourStr + ' ' + minuteStr;
			return returnStr.trim();
		}
	}]);

})(angular);
;
(function (angular) {

	// Title Case Directive
	// --------------------------------------------
	//
	// * **Class:** TitleCase
	// * **Author:** Francis Oyeniyi
	//
	// Returns String as title case

	'use strict';

	var module = angular.module('TitleCaseModule', []);

	module.filter('TitleCase', [function () {

		return function titleCase(str) {
			return str.toLowerCase().split(' ').map(function (word, index) {
				// add more as needed
				return (((word == "or" ||
						word == "and" ||
						word == "but") &&
					index != 0) ? word  : word.charAt(0).toUpperCase() + word.slice(1));
			}).join(' ');
		}
	}]);

})(angular);
;
(function (angular) {

    // Ha Hotels Add On
    // --------------------------------------------
    //
    // * **Class:** HaHotelsAddOn
    // * **Author:** Nathan Probst
    //
    // This module implements the Orbitz Hotels Add On

    'use strict';

    var module;
    try {
        module = angular.module('haAncillariesModule');
    } catch (e) {
        module = angular.module('haAncillariesModule', ['haHttpService', 'haFeatureFlagsModule']);
    }


    module.directive('haAncillaries', function () {
        return {
            restrict: 'A',
            scope: true,
            controller: 'haAncillariesCtrl as Ancillaries'
        };
    });

    module.config(['haFeatureFlagsProvider', function (flags) {
        flags.setDefault('AncillariesTimeoutMs', 20 * 1000);      // 20 seconds
		flags.setDefault('HotelListTimeoutMs', 20 * 1000);        // 20 seconds
		flags.setDefault('HotelRepriceTimeoutMs', 20 * 1000);     // 20 seconds
		flags.setDefault('CarRentalForOneWayTimeoutMs', 10 * 1000);   // 10 seconds
    }]);

    module.controller('haAncillariesCtrl', [
		'$log',
		'$scope',
		'haGlobals',
		'haHotelCommonService',
		'haAncillariesSvc',
		'haFeatureFlags',
		'$rootScope',


		function ($log, $scope, globals, hotelSvc, ancillariesSvc, flags,$rootScope) {

		    var ctrl = {
		        loading: true,
		        error: false
		    };

		    // Which ancillaries to are eligible
		    var eligible = window.eligibleAncillaries,
				messages = window.HA.messages;

		    // If nothing eligible return
		    if (!eligible || !eligible.length) {
		        ctrl.loading = false;
		        return;
		    }

		    var ancillaryRequestArray = [],
				hasAncillaryType = function (typeString) {
				    return eligible.some(function (anc) { return anc.Description === typeString; });
				},
				time = moment();

		    // Push items on the array
		    if (hasAncillaryType(messages.vacationPkgString)) {

		        if ($scope.TripSummary.SelectedHotel === null) {
		            document.body.dispatchEvent(new CustomEvent('VacationPackageEligible'));
		        }
		        ancillaryRequestArray.push('1');
			}
			if (hasAncillaryType(messages.rentalCarString)) {
				document.body.dispatchEvent(new CustomEvent('RentalCarEligible'));
				ancillaryRequestArray.push('2');
			}
		    if (hasAncillaryType(messages.shuttleHnlString)) { ancillaryRequestArray.push('3'); }
		    if (hasAncillaryType(messages.shuttleLasString)) { ancillaryRequestArray.push('4'); }
		    if (hasAncillaryType(messages.leiGreetingString)) { ancillaryRequestArray.push('5'); }
		    if (hasAncillaryType(messages.tripInsuranceString)) { ancillaryRequestArray.push('6'); }

			var timeout = $scope.$parent.selectedSegments.length > 1 ? flags.get('AncillariesTimeoutMs') : flags.get('CarRentalForOneWayTimeoutMs');

		    // go get the eligible ancillaries in parallel
			ancillariesSvc.getAncillariesAsync(ancillaryRequestArray, timeout).then(
				function (promises) {
					// Log response time
					document.body.dispatchEvent(new CustomEvent('AncillaryResponseTime', { 'detail': { time: moment().diff(time, 'milliseconds') } }));
					$log.debug('ancillary response time: ' + moment().diff(time, 'milliseconds'));

					var vacationPackagesState = (promises['1']) ? promises['1'].state : undefined,
						vacationPackages = (vacationPackagesState === 'fulfilled' && promises['1'].value.data.success) ? promises['1'].value.data.ancillary : undefined,

						rentalCarsState = (promises['2']) ? promises['2'].state : undefined,
						rentalCars = (rentalCarsState === 'fulfilled' && promises['2'].value.data.success) ? promises['2'].value.data.ancillary : undefined,

						airportShuttleState = ~ancillaryRequestArray.indexOf('3') ? promises['3'].state : ~ancillaryRequestArray.indexOf('4') ? promises['4'].state : undefined,
						airportShuttleHnl = (~ancillaryRequestArray.indexOf('3') && promises['3'].state === 'fulfilled' && promises['3'].value.data.success) ? promises['3'].value.data.ancillary : undefined,
						airportShuttleLas = (~ancillaryRequestArray.indexOf('4') && promises['4'].state === 'fulfilled' && promises['4'].value.data.success) ? promises['4'].value.data.ancillary : undefined,
						airportShuttle = airportShuttleHnl ? airportShuttleHnl : airportShuttleLas ? airportShuttleLas : undefined;

				    // Throw custom event if eligible and promise rejection or no data returned
				    if (vacationPackagesState === 'rejected' || (!vacationPackages && vacationPackagesState)) {
				        $log.error('Vacation package failure when eligible');

				        document.body.dispatchEvent(new CustomEvent('EligibleVacationPackageError'));

				        if (window.BOOMR && BOOMR.version) {
				            window.BOOMR.sendMetric("Hot_Res_SchFail", 1);
				        }
				    }
				    else if (vacationPackagesState === 'fulfilled' && vacationPackages && vacationPackages.HotelSolutions && vacationPackages.HotelSolutions[0]) {

				        if (window.BOOMR && BOOMR.version) {
				            window.BOOMR.sendMetric("Hot_Res_Search", 1);
				        }
                    }
				    else if (vacationPackagesState === 'fulfilled' && vacationPackages && vacationPackages.HotelSolutions.length == 0) {
				        if (window.BOOMR && BOOMR.version) {
				            window.BOOMR.sendMetric("Hot_Res_SchFail", 1);
				        }
				    }
				    if (rentalCarsState === 'rejected' || (!rentalCars && rentalCarsState)) {
				        $log.error('Rental Car failure when eligible');
				        if (window.BOOMR && BOOMR.version) {
				            window.BOOMR.sendMetric("CarsLoadFailed", 1);
				        }
						document.body.dispatchEvent(new CustomEvent('EligibleRentalCarError'));
				    }
					else if (rentalCarsState === 'fulfilled' && rentalCars) {
						window.digitalData.rentalCarRequest = {};
						window.digitalData.rentalCarResponse = {};

						window.digitalData.rentalCarRequest = {
							name: 'rentaCarRequest',
							timestamp: new Date().getTime(),
							requestFrom: 'In-Path',
							requestSent: true
						};

						window.digitalData.rentalCarResponse = {
							name: 'rentalCarResponse',
							timestamp: new Date().getTime()
						};

                        if (window.BOOMR && BOOMR.version) {
				        window.BOOMR.sendMetric("CarsLoaded", 1);
						}
				}
				    if (airportShuttleState === 'rejected' || ((~ancillaryRequestArray.indexOf('3') || ~ancillaryRequestArray.indexOf('4')) && !airportShuttle)) {
				        $log.error('Shuttle failure when eligible');
				        if (window.BOOMR && BOOMR.version) {
				            window.BOOMR.sendMetric("ShtlLoadFailed", 1);
				        }
				        document.body.dispatchEvent(new CustomEvent('EligibleShuttleError'));
				    }
				    else if (airportShuttleState ==='fulfilled' && airportShuttle)
				    {
                        if (window.BOOMR && BOOMR.version) {
				        window.BOOMR.sendMetric("ShuttleLoaded", 1);
				    }
				}

				    // Set local scope
				    $scope.ancillariesData = {
				        OrbitzPackage: (vacationPackages === null) ? undefined : vacationPackages,
				        AirportShuttle: (airportShuttle === null) ? undefined : airportShuttle
				    };

				    var hasHotelData = $scope.ancillariesData.OrbitzPackage && $scope.ancillariesData.OrbitzPackage.HotelSolutions.length,
						hasAirportShuttleData = $scope.ancillariesData.AirportShuttle && $scope.ancillariesData.AirportShuttle.shuttleDetails.isEligible;

				    // Set hotel scope
				    $scope.HasHotelData = hasHotelData;

				    //PBI:141495 Expedia Disable Ancillaries
				    $scope.$on('hidehoteldetails', function () {
				        $scope.HasHotelData = false;
				    });

				    //PBI:141495 Expedia Disable Ancillaries
				    $scope.$on('showhoteldetails', function () {
				        if ($scope.ancillariesData.OrbitzPackage.HotelSolutions.length) {
				            $scope.HasHotelData = true;
				        }
				    });

					setRentalCarData(rentalCars);

				    // set shuttle scope
				    $scope.HasAirportShuttleData = hasAirportShuttleData;
				    $scope.airportShuttleData = $scope.ancillariesData.AirportShuttle;

				    //PBI:141495 Expedia Disable Ancillaries
				    if (hasHotelData && $scope.TripSummary.SelectedHotel != null && window.isExpediaEnabled) {

				        $scope.HasRentalCarsData = false;
				        $scope.HasAirportShuttleData = false;
				    }

				    //PBI:141495 Expedia Disable Ancillaries changes
				    if ($scope.TripSummary.SelectedShuttleDetails != null || $scope.TripSummary.SelectedCar.LastName != null) {
				        $scope.HasHotelData = false;
				    }

				    $scope.$emit('ancillariesOffered', {
				        rentalCar: $scope.HasRentalCarsData && ($scope.TripSummary.SelectedCar === null || !$scope.TripSummary.SelectedCar.LastName),
				        airportShuttle: $scope.HasAirportShuttleData && $scope.TripSummary.SelectedShuttleDetails === null,
				        hotels: $scope.ancillariesData.OrbitzPackage && $scope.ancillariesData.OrbitzPackage.HotelSolutions.length && $scope.TripSummary.SelectedHotel === null
				    });

				},
				function () {
				    // Only log if promise rejected and eligible
				    if (eligible.length) {
				        $log.error('No ancillaries eligible');
				        document.body.dispatchEvent(new CustomEvent('NoAncillariesOfferedWhenEligible'));
				    }
				}
			).finally(function () {
			    ctrl.loading = false;
				});

		    var getABGOfferBanner = function () {
			    var bannerPromise = null;
			    switch ($rootScope.user.haMilesEliteStatus) {
			    case "PLT":
						bannerPromise = $scs.get('Car_Ancillary.specialoffertextplatinum');
						break;
			    case "GLD":
						bannerPromise = $scs.get('Car_Ancillary.specialoffertextgold');
						break;
			    case "PRM":
						bannerPromise = $scs.get('Car_Ancillary.specialoffertextbasic');
						break;
			    case "HML":
						bannerPromise = $scs.get('Car_Ancillary.specialoffertextbasic');
						break;
					default:
						bannerPromise = $scs.get('Car_Ancillary.specialoffertextnonmember');
						break;
				}
			    return bannerPromise;
		    }

		    $scope.$on('ancillariesOffered', function (e, offers) {

		        var url = location.href;

		        if ((/book\/itinerary/i).test(url)) {
		            if (offers.rentalCar) {
						document.body.dispatchEvent(new CustomEvent('RentalCarOffered'));

						//ABGII 9, ABG 69 BEGIN
						getABGOfferBanner().then(function(content) {
							$scope.ABGOfferText = content;
						});
						//ABGII 9, ABG 69 END
		            }
		            if (offers.airportShuttle) {
		                document.body.dispatchEvent(new CustomEvent('ShuttleServiceOffered'));
		            }
		            if (offers.hotels) {
		                document.body.dispatchEvent(new CustomEvent('VacationPackageOffered'));
		            }
		        }
			});

			function setDigitalDataForRentalCar(rentalCars) {
				
			}


		    function setRentalCarData (rentalCars, showWidget) {
				$scope.rentalCarsData = $scope.ancillariesData.RentalCars = !rentalCars ? undefined : rentalCars;

				// This property is used to show/hide the car rental form.
				// After a search yielding no results, we need the form to remain.
				$scope.HasRentalCarsData = showWidget || (rentalCars && rentalCars.DisplayCount > 0);

				if (rentalCars) {
					var carsAll = rentalCars.CarList || [];
					var carsTopFour = rentalCars.CarListForInitialDisplay || [];

					angular.forEach(carsAll.concat(carsTopFour), function (value) {
						value.Vendor.ImageUrl = value.Vendor.ImageUrl.replace("/vendor", "/vendor/large");
					});


					let cars = [];
					angular.forEach(rentalCars.CarList, function (value) {
						if (value.IsInitialDisplaySelected) {
							let car = {
								carPrice: value.CostPerDay,
								carType: value.Vehicle.Class,
								carSelected: 'Initial'
							}
							cars.push(car);
						}						
					});

					window.digitalData.carsOffered = {};
					window.digitalData.carsOffered = {
						name: 'InitialRentalCarsOffered',
						timestamp: new Date().getTime(),
						eligiblePassenger: true,
						cars: cars,
						displayedSuccesfully: $scope.HasRentalCarsData
						
					};							
				}
			}

			$scope.searchCarRentals = function (query) {
				return ancillariesSvc.searchCarRentals(query).then(function (results) {
					setRentalCarData(results.data.ancillary, true);
					return results.data;
				});
			};

		    return ctrl;
		}
    ]);

})(angular);
;
(function (angular) {

	// Ha Hotels Add On
	// --------------------------------------------
	//
	// * **Class:** HaHotelsAddOn
	// * **Author:** Nathan Probst
	//
	// This module implements the Orbitz Hotels Add On

	'use strict';

	var module;
	try {
		module = angular.module('haAncillariesModule');
	} catch (e) {
		module = angular.module('haAncillariesModule', ['haHttpService', 'haFeatureFlagsModule']);
	}

	var trimZ = function (dateTimeStr) {
		return dateTimeStr.replace(/Z$/, '');
	};

	module.directive('haAncillariesRentalCar', function () {
		return {
			restrict: 'A',
			scope: true,
			controller: 'haAncillariesRentalCarCtrl as ctrl'
		};
	});


	module.controller('haAncillariesRentalCarCtrl', [
		'$q',
		'$log',
		'$scope',
		'$rootScope',
		'$window',
		'$templateCache',
		'haModal',
		'haAncillariesSvc',
		'haPaymentTypesService',
		'haCitiesSvc',
		'haSitecoreStrings',
		'haTravelersAPI',
		'haUtils',

		function ($q, $log, $scope, $rootScope, $window, $templateCache, haModal, ancillariesSvc, haPaymentTypesSvc, haCitiesSvc, $scs, haTravelersAPI, haUtils) {

			var handleExceptions = function (result) {
				if (result !== 'jsError') {
					switch (result.code) {
						case 'SessionTimeOut':
							window.location.href = '/book/error?ErrorType=SessionTimeOut';
							break;
						default:
							if (result.code !== undefined) {
								window.location.href = result.RedirectURL;
							} else {
								return false;
							}
							break;
					}
				} else {
					window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
				}
			};

			var trackingAddRemoveEvents = function (action) {
				switch (action) {
					case 'add':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('RentalCarAdded'));
						break;
					case 'remove':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('RentalCarRemoved'));
						break;
				}
			};

			// strings

			$scs.get('Car_Ancillary').then(function (data) {
				$scope.scContent = data;
			});

			var getABGOfferBanner = function () {
				var offerText = "";
				switch ($rootScope.user.haMilesEliteStatus) {
					case "PLT":
						offerText = $scope.scContent.specialoffermodaltextplatinum;
						break;
					case "GLD":
						offerText = $scope.scContent.specialoffermodaltextgold;
						break;
					case "PRM":
						offerText = $scope.scContent.specialoffermodaltextbasic;
						break;
					case "HML":
						offerText = $scope.scContent.specialoffermodaltextbasic;
						break;
					default:
						offerText = $scope.scContent.specialoffermodaltextnonmember;
						break;
				}
				return offerText;
			}

			var ctrl = {
				rentalCars: null,
				agencies: [],
				classes: [],
				sortBy: 'PRICE',
				selectCar: function (car) {
					$log.debug('Select', car);
				},

				showRentalCarsModal: function () {
					var scope = $rootScope.$new(true, $scope);
					scope.ctrl = ctrl;
					scope.rentalCars = ctrl.rentalCars.CarList;
					scope.agencies = ctrl.agencies;
					scope.classes = ctrl.classes;
					scope.pickup = _.extend(ctrl.rentalCars.PickUpLocation, { date: moment(ctrl.rentalCars.StartDate).toDate() });
					scope.dropoff = _.extend(ctrl.rentalCars.DropLocation, { date: moment(ctrl.rentalCars.EndDate).toDate() });

					if (ctrl.rentalCars.CarList.length > 0) {
						scope.pickup = _.extend(ctrl.rentalCars.PickUpLocation, { date: moment(ctrl.rentalCars.CarList[0].PickUpDateTime).toDate() });
						scope.dropoff = _.extend(ctrl.rentalCars.DropLocation, { date: moment(ctrl.rentalCars.CarList[0].ReturnDateTime).toDate() });
					}

					scope.ABGOfferModalText = getABGOfferBanner();

						haModal({
							id: 'RentalCarsModal',
							backdrop: 'true',
							scope: scope,
							template: $templateCache.get('RentalCarsModal.html')
						});
				},
				showReserveVehicleModal: function (car) {
					$q.all([
						haCitiesSvc.getCarLocationById(car.PickUpLocation.LocationCode),
						haCitiesSvc.getCarLocationById(car.DropLocation.LocationCode)
					]).then(function (results) {
						var scope = $rootScope.$new(true, $scope);
						car.PickUpLocation.Address = results[0].address;
						car.PickUpLocation.Airport = results[0].airport;
						car.DropLocation.Address = results[1].address;
						car.DropLocation.Airport = results[1].airport;
						scope.car = car;
						scope.ctrl = ctrl;
						scope.hasInvalidPax = false;

						//ABGII-10 INPATH 4
						if ($rootScope.isLoggedIn) {
							haTravelersAPI.GetTravelers("self").then(function (response) {
								response.data.TravelersList.forEach(function (traveler) {

									if (traveler.HMAccountNo === $rootScope.user.haMiles)
									{
										scope.car.FirstName = traveler.FirstName;
										scope.car.LastName = traveler.LastName;
										scope.car.HMNumber = traveler.HMAccountNo;
									}
								})
							});
						}
						//ABGII-10 INPATH 4					

						scope.doReserve = function () {
							if (scope.validatePax(car)) {
								ancillariesSvc.addRentalCar(car).then(function () {
									scope.hasInvalidPax = false;
									$scope.TripSummary.SelectedCar = car;
									$scope.TripSummary.HasSelectedCar = true;
									trackingAddRemoveEvents('add');							

									window.digitalData.rentalCarAdded.push({
										name: 'rentalCarAdded',
										timestamp: new Date().getTime(),
										cartype: car.Vehicle.Class,
										carprice: car.CostPerDay,
										carSelected: car.IsInitialDisplaySelected ? 'Initial' : 'ShowMore'
									});
									
									scope.$modalCancel();
									
									//PBI:141495 Expedia Disable Ancillaries
									if (window.isExpediaEnabled && !window.enableAncillariesForExpedia)
										$rootScope.$broadcast('hidehoteldetails');
									// $window.location.reload(true);
								},
									handleExceptions);
							}
							else {
								ReserveVehicleForm.$dirty = true;/* ReserveVehicleForm not in scope */ // jshint ignore:line
								scope.hasInvalidPax = true;
							}							
						};

						scope.validatePax = function (car) {
							var isValidPax = true;
							angular.forEach($scope.TripSummary.Passengers, function (pax) {
								if (car.FirstName.toLowerCase() === pax.FirstName.toLowerCase() && car.LastName.toLowerCase() === pax.LastName.toLowerCase() && pax.Type.toLowerCase() === 'child') {
									isValidPax = false;
								}
							});
							return isValidPax;
						};

						haModal({
							id: 'ReserveVehicleModal',
							backdrop: 'true',
							scope: scope,
							template: $templateCache.get('ReserveVehicleModal.html')
						});
					});
				},

				showFAQ: function () {
					haModal({
						id: 'RentalCarFaqModal',
						backdrop: 'true',
						template: $templateCache.get('RentalCarFaqModal.html')
					});
				},
				showTerms: function (car) {
					var scope = $rootScope.$new(true, $scope);
					scope.car = car;
					scope.ctrl = ctrl;
					scope.terms = {};
					scope.showTermsfromCT = false;
					scope.showTermsError = false;

					ancillariesSvc.getRentalTerms(car).then(
						function (data) {
							if (data != null) {
								scope.terms = data;
								scope.showTermsfromCT = true;
							}
						}, function (err) {
							$log.error('getRentalTerms', err);
							scope.showTermsError = true;
						});

					haModal({
						id: 'RentalCarTermsModal',
						backdrop: 'true',
						scope: scope,
						template: $templateCache.get('RentalCarTermsModal.html')
					});
				},
				showRemoveRentalCarModal: function () {
					var scope = $rootScope.$new(true, $scope);
					scope.doRemove = function () {
						ancillariesSvc.removeRentalCar($scope.TripSummary.SelectedCar).then(function () {
							trackingAddRemoveEvents('remove');
							
							window.digitalData.rentalCarRemoved.push({
								name: 'rentalCarRemoved',
								timestamp: new Date().getTime(),
								cartype: $scope.TripSummary.SelectedCar.Vehicle.Class,
								carprice: $scope.TripSummary.SelectedCar.CostPerDay,
								carSelected: $scope.TripSummary.SelectedCar.IsInitialDisplaySelected ? 'Initial' : 'ShowMore'
							});

							$scope.TripSummary.SelectedCar = null;
							$scope.TripSummary.HasSelectedCar = false;
							haPaymentTypesSvc.updatePaymentTypes();
							$rootScope.$broadcast('ancelaryStateChange');					

						    //PBI:141495 Expedia Disable Ancillaries
							if (window.isExpediaEnabled && !window.enableAncillariesForExpedia)
							    $rootScope.$broadcast('showhoteldetails');
						},
							handleExceptions);
					};

					haModal({
						id: 'RemoveRentalCarModal',
						backdrop: 'true',
						scope: scope,
						template: $templateCache.get('RemoveRentalCarModal.html')
					});
				},
				UpdateRentalCarDefaultLoadTimeMark: function () {
					window.performance.clearMarks("mark_end_DefLoadTime_RentalCar");
					window.performance.mark("mark_end_DefLoadTime_RentalCar");
					if (window.performance.getEntriesByName("mark_end_DefLoadTime_RentalCar") && window.performance.getEntriesByName("mark_end_DefLoadTime_RentalCar")[0]) {
						var rentalCarMeasure = window.performance.getEntriesByName("mark_end_DefLoadTime_RentalCar")[0].startTime;
						if (window.BOOMR && BOOMR.version) {
							window.BOOMR.sendTimer("CTDefault_Timer", rentalCarMeasure);
						}
					}
				},
				redirectToCarSupplierSite: function () {
					var formGetUrl = $scope.scContent.supplierloyaltywebsiteurl;
					var pid = $scope.scContent.supplierpartnerid; // PID given by supplier ABG
					var profileAddition = true; // Always true - required in order to show the profile section
					var confirmation = $scope.TripSummary.SelectedCar.SupplierConfirmationCode;
					var lastName = $scope.TripSummary.SelectedCar.LastName;
					var locale = haUtils.getLocale($scope.$root.$language);

					var queryObject = {
						pid: pid,
						profileAddition: profileAddition,
						locale: locale,
						lastName: lastName,
						confNumber: confirmation
					}
					
					var formGetUrl = $scope.scContent.supplierloyaltywebsiteurl;

					formGetUrl += haUtils.createQueryString(queryObject);

					window.location.href = formGetUrl;
				},
				scrollToReservedCar: function () {
					var id = "carRentalData";
					var docId = document.getElementById(id);
					if (docId) {
						var reservedCar = docId.offsetTop;
						window.scrollTo(0, reservedCar);
					}
				}
			};
			$scope.getLocationDetails = function(car) {
				return $q.all([augmentLocation(car.PickUpLocation),augmentLocation(car.DropLocation)]).then(function(){return car;});
			};
			function augmentLocation(location) {
				if (location.Address) {
					return $q.resolve(location);
				}
				return haCitiesSvc.getCarLocationById(location.LocationCode).then(function(details) {
					location.Address = details.address;
					location.Airport = details.airport;
					return location;
				});
			}

			$scope.$watch('rentalCarsData', function (data) {
				ctrl.rentalCars = data;
				ctrl.tripType = data && data.TripType;
				var i;
				var car;
				if (data != null && data.CarListForInitialDisplay != null) {
					for (i = 0; i < data.CarListForInitialDisplay.length; i++) {
						car = data.CarListForInitialDisplay[i];
						car.PickUpDateTime = trimZ(car.PickUpDateTime);
						car.ReturnDateTime = trimZ(car.ReturnDateTime);
					}
				}

				if (data != null && data.CarList != null) {
					var vendors = {};
					var classes = {};
					for (i = 0; i < data.CarList.length; i++) {
						car = data.CarList[i];
						var vendor = car.Vendor;
						var vehicle = car.Vehicle;

						if (vendor && vendor.Name && vendor.Name !== '') {
							vendors[vendor.Name] = vendor;
						}

						if (vehicle && vehicle.Class && vehicle.Class !== '') {
							classes[vehicle.Class] = vehicle.Class;
						}

						car.PickUpDateTime = trimZ(car.PickUpDateTime);
						car.ReturnDateTime = trimZ(car.ReturnDateTime);
					}
					ctrl.agencies = [];
					angular.forEach(vendors, function (v) {
						ctrl.agencies.push(v);
					});
					ctrl.classes = [];
					angular.forEach(classes, function (v) {
						ctrl.classes.push(v);
					});
				}
			});

			return ctrl;
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module;
	try {
		module = angular.module('haAncillariesModule');
	} catch (e) {
		module = angular.module('haAncillariesModule', ['haHttpService', 'haFeatureFlagsModule']);
	}

	module.directive('haAncillariesAirportShuttle', function () {
		return {
			restrict: 'A',
			scope: true,
			controller: 'haAncillariesAirportShuttleCtrl as ctrl'
		};
	});


	module.controller('haAncillariesAirportShuttleCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'$window',
		'$templateCache',
		'haModal',
		'haAncillariesSvc',
		'haPaymentTypesService',

		function ($log, $scope, $rootScope, $window, $templateCache, haModal, ancillariesSvc, haPaymentTypesSvc) {

			var handleExceptions = function (result) {
				if (result !== 'jsError') {
					switch (result.code) {
						case 'SessionTimeOut':
							window.location.href = '/book/error?ErrorType=SessionTimeOut';
							break;
						default:
							if (result.code !== undefined) {
								window.location.href = result.RedirectURL;
							} else {
								return false;
							}
							break;
					}
				} else {
					window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
				}
			};

			var trackingAddRemoveEvents = function (action) {
				switch (action) {
					case 'add':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('AirportShuttleAdded'));
						break;
					case 'remove':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('AirportShuttleRemoved'));
						break;
				}
			};

			var ctrl = {
				//to do
				AirportShuttle: null,
				addAirportShuttle: function () {
					ancillariesSvc.addAirportShuttle(ctrl.AirportShuttle)
					.then(function (data) {
						if (data) {
							$scope.TripSummary.SelectedShuttleDetails = ctrl.AirportShuttle;
							$scope.TripSummary.IsAirportShuttleSelected = true;
							trackingAddRemoveEvents('add');
							$rootScope.$broadcast('ancelaryStateChange');
						    ////PBI:141495 Expedia Disable Ancillaries
							if (window.isExpediaEnabled && !window.enableAncillariesForExpedia)
							    $rootScope.$broadcast('hidehoteldetails');
						}
					},
						handleExceptions);
				},
				showRemoveAirportShuttleModal: function () {
					var scope = $rootScope.$new(true, $scope);
					scope.doRemove = function () {
						ancillariesSvc.removeAirportShuttle()
						.then(function () {
							$scope.TripSummary.SelectedShuttleDetails = null;
							$scope.TripSummary.IsAirportShuttleSelected = false;
							haPaymentTypesSvc.updatePaymentTypes();
							trackingAddRemoveEvents('remove');
							$rootScope.$broadcast('ancelaryStateChange');
						    //PBI:141495 Expedia Disable Ancillaries
							if (window.isExpediaEnabled && !window.enableAncillariesForExpedia)
							    $rootScope.$broadcast('showhoteldetails');
						},
							handleExceptions);
					};

					haModal({
						id: 'RemoveAirportShuttleModal',
						backdrop: 'true',
						scope: scope,
						template: $templateCache.get('RemoveAirportShuttleModal.html')
					});
				}
			};
			$scope.$watch('airportShuttleData', function (data) {
				if (data != null) {
					ctrl.AirportShuttle = data.shuttleDetails;
				}
			});

			return ctrl;
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module;
	try {
		module = angular.module('haAncillariesModule');
	} catch (e) {
		module = angular.module('haAncillariesModule', ['haHttpService', 'haFeatureFlagsModule']);
	}

	module.directive('haAncillariesLeiGreeting', function () {
		return {
			restrict: 'A',
			controller: 'haAncillariesLeiGreetingCtrl as ctrl'
		};
	});


	module.controller('haAncillariesLeiGreetingCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'$window',
		'$templateCache',
		'haModal',
		'haAncillariesSvc',
		'haPaymentTypesService',

		function ($log, $scope, $rootScope, $window, $templateCache, haModal, ancillariesSvc, haPaymentTypesSvc) {

			var handleExceptions = function (result) {
				if (result !== 'jsError') {
					switch (result.code) {
						case 'SessionTimeOut':
							window.location.href = '/book/error?ErrorType=SessionTimeOut';
							break;
						default:
							if (result.code !== undefined) {
								window.location.href = result.RedirectURL;
							} else {
								return false;
							}
							break;
					}
				} else {
					window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
				}
			};

			var trackingAddRemoveEvents = function (action) {
				var eventLocation = (/book\/payment/gi).test(location.href) ? 'Payment' : 'Confirmation';
				switch (action) {
					case 'add':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('LeiGreetingAdded'));
						break;
					case 'remove':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('LeiGreetingRemoved'));
						break;
				}
			};

			var ctrl = {
				// LeiGreeting: null,
				addLeiGreeting: function (lg) {
					ancillariesSvc.addLeiGreeting(lg)
					.then(function (data) {
						if (data) {
							$scope.TripSummary.SelectedLeiGreeting = lg;
							$scope.TripSummary.isLeiGreetingsSelected = true;
							haPaymentTypesSvc.updatePaymentTypes();
							trackingAddRemoveEvents('add');
							$scope.$emit('ancelaryStateChange');
						}
					},
						handleExceptions);
				},
				showRemoveLeiGreetingModal: function () {
					var scope = $rootScope.$new(true, $scope);
					scope.doRemove = function () {
						ancillariesSvc.removeLeiGreeting()
						.then(function () {
							trackingAddRemoveEvents('remove');
							$scope.TripSummary.SelectedLeiGreeting = null;
							$scope.TripSummary.isLeiGreetingsSelected = false;
							haPaymentTypesSvc.updatePaymentTypes();
							$scope.$emit('ancelaryStateChange');
						},
							handleExceptions);
					};

					haModal({
						id: 'RemoveLeiGreetingModal',
						backdrop: 'true',
						scope: scope,
						template: $templateCache.get('RemoveLeiGreetingModal.html')
					});
				},
				showLegendOfTheLeiModal: function () {
					haModal({
						id: 'LegendOfTheLeiModal',
						backdrop: 'true',
						template: $templateCache.get('LegendOfTheLeiModal.html')
					});
				}
			};
			// $scope.$watch('airportShuttleData', function (data) {
			//     if (data != null) {
			//         ctrl.AirportShuttle = data.shuttleDetails;
			//     }
			// });

			return ctrl;
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module;
	try {
		module = angular.module('haAncillariesModule');
	} catch (e) {
		module = angular.module('haAncillariesModule', ['haHttpService', 'haFeatureFlagsModule']);
	}

	module.directive('haAncillariesTripInsurance', function () {
		return {
			restrict: 'A',
			controller: 'haAncillariesTripInsuranceCtrl as ctrl'
		};
	});


	module.controller('haAncillariesTripInsuranceCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'$window',
		'$templateCache',
		'haModal',
		'haAncillariesSvc',
		'haPaymentTypesService',
		'haUtils',
		function($log, $scope, $rootScope, $window, $templateCache, haModal, ancillariesSvc, haPaymentTypesSvc, haUtils) {

			var isPaymentPage = (/book\/payment/gi).test(location.href) ? 'true' : 'false';
			var ctrl;

			// Mpulse tracking - trip insurance quote response.
			if (isPaymentPage === 'true') {
				ctrl = {
					updateAllianzQuoteResponseMetric: function (isPaymentTripInsuranceEnabled) {
						if ($scope.TripSummary.IsTripInsuranceEligible &&
								$scope.TripSummary.TripInsurance.QuoteID === undefined) {
							// Mpulse tracking: trip insurance quote response failed.
							if (window.BOOMR && BOOMR.version) {
								window.BOOMR.sendMetric("Alnz_Quote_FL", 1);
							}
						}

						if ($scope.TripSummary.IsTripInsuranceEligible &&
								!isPaymentTripInsuranceEnabled) {
							// Mpulse tracking: trip insurance is disabled in sitecore, payment page. Display failed.
							if (window.BOOMR && BOOMR.version) {
								window.BOOMR.sendMetric("Alnz_Pt_Disp_FL", 1);
							}
						}

					}
				};
			}
			
			// Mpulse tracking - trip insurance purchase response and failure tracking
			var isConfirmationPage = (/book\/Confirmation/gi).test(location.href) ? 'true' : 'false';
			if (isConfirmationPage === 'true') {
				ctrl = {
					updateAllianzPurchaseResFailureMetric: function () {
						// Mpulse tracking trip insurance purchase response failure.
						if (window.BOOMR && BOOMR.version) {
							window.BOOMR.sendMetric("Alnz_Purchs_FL", 1);
						}
					}
				};

			};
			// Mpulse trucking - end

			var handleExceptions = function (result) {
				if (result !== 'jsError') {
					switch (result.code) {
						case 'SessionTimeOut':
							window.location.href = '/book/error?ErrorType=SessionTimeOut';
							break;
						default:
							if (result.code !== undefined) {
								window.location.href = result.RedirectURL;
							} else {
								return false;
							}
							break;
					}
				} else {
					window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
				}
			};

			var trackingAddRemoveEvents = function (action) {
				switch (action) {
					case 'add':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('TripInsuranceAdded'));
						break;
					case 'remove':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('TripInsuranceRemoved'));
						break;
				}
			};


			// These are only consumed by Allianz. PBI 151724
			$scope.tripInsuranceRadioButtonsDisabled = function () {
				if ($scope.$switch('payment:EnableGiftCardsWhenAncillariesPresent')) return false;
				return haPaymentTypesSvc.paymentMethod === 'giftCard';
			}
			$scope.tripInsuranceRadioButtonsRequired = function () {
				if ($scope.$switch('payment:EnableGiftCardsWhenAncillariesPresent')) return true;
				return haPaymentTypesSvc.paymentMethod !== 'giftCard';
			}

			// functions for add/remove/toggle insurance
			// these are meant to accommodate a variety of controls by simply attaching ng-click
			$scope.addTripInsurance = function (ti) {

				ancillariesSvc.addTripInsurance(ti)
				.then(function (data) {
					if (data) {
						$scope.TripSummary.TripInsurance = data.TripInsurance;
						if ($scope.TripSummary.TripInsurance.TravelStartDate != null) {
							$scope.TripSummary.TripInsurance.TravelStartDate = new Date(parseInt($scope.TripSummary.TripInsurance.TravelStartDate.substr(6)));
						}
						if ($scope.TripSummary.TripInsurance.TravelEndDate) {
							$scope.TripSummary.TripInsurance.TravelEndDate = new Date(parseInt($scope.TripSummary.TripInsurance.TravelEndDate.substr(6)));
						}
						$scope.TripSummary.TripInsurance.IsTripInsuranceSelected = true;
						haPaymentTypesSvc.updatePaymentTypes();
						trackingAddRemoveEvents('add');
						$scope.$emit('ancelaryStateChange');
					}
				},
					handleExceptions);
			};

			$scope.removeTripInsurance = function () {
				ancillariesSvc.removeTripInsurance()
				.then(function (data) {
					$scope.TripSummary.TripInsurance = data.TripInsurance;
					$scope.TripSummary.TripInsurance.IsTripInsuranceSelected = false;
					haPaymentTypesSvc.updatePaymentTypes();
					trackingAddRemoveEvents('remove');
					$scope.$emit('ancelaryStateChange');
				},
					handleExceptions);
			};

			$scope.toggleTripInsurance = function () {
				if ($scope.TripSummary.TripInsurance.IsTripInsuranceSelected === false) {
					$rootScope.addTripInsurance();
				} else {
					$rootScope.removeTripInsurance();
				}
			};

			return ctrl;
		}
	]);
	module.directive('bindHtmlCompile', [
		'$compile', function ($compile) {
			return {
				restrict: 'A',
				link: function (scope, element, attrs) {

					scope.$watch(function () {
						return scope.$eval(attrs.bindHtmlCompile);
					}, function (value) {
						element.html(value);
						$compile(element.contents())(scope);
					});
				}
			};
		}]);

})(angular);
;
(function (ng) {

	// Hotel Added Controller
	// --------------------------------------------
	//
	// * **Class:** haHotelAddedCtrl
	// * **Author:** Nathan Probst
	//
	// Controller for the "Hotel Added" partial

	'use strict';

	var module;
	try {
		module = ng.module('haHotelPackagesModule');
	} catch (e) {
		module = ng.module('haHotelPackagesModule', ['haUtilsModule', 'haGlobalsModule', 'haFeatureFlagsModule', 'haCurrencyModule', 'haAncillariesModule', 'haModalService', 'haEqualHeightModule', 'haPassengersService', 'haRoundingFiltersModule', 'haSitecoreModule']);
	}


	module.controller('haHotelAddedCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'$window',
		'$templateCache',
		'haConfig',
		'haModal',
		'haAncillariesSvc',

		function ($log, $scope, $rootScope, $window, $templateCache, haConfig, haModal, ancillariesSvc) {

			var trackingAddRemoveEvents = function (action) {
				switch (action) {
					case 'remove':
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('VacationPackageRemoved'));
						break;
				}
			};

			var ctrl = {
				detailUrl: function (hotel) {
					return '/Book/Itinerary/GetVacationPackageList#?hotelId=' + (hotel.id || hotel.HotelId);
				},

				remove: function (hotel) {
					$rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off
					ancillariesSvc.removeHotel(hotel)
					.then(function () {
						$window.location.reload(true);
					}, function (data) {
						handleExceptions(data);

					});
				},

				showRemoveModal: function (hotel) {
					var scope = $rootScope.$new(true, $scope);
					scope.removeHotel = function () {
						trackingAddRemoveEvents('remove');
						ctrl.remove(hotel);
					};

					
					haModal(haConfig.getTemplateUrl('RemoveHotelModal.html'), {
						id: 'RemoveHotelModal',
						backdrop: 'true',
						scope: scope
					});
				}
			};

			var handleExceptions = function (result) {
				if (result !== 'jsError') {
					switch (result.code) {
						case 'SessionTimeOut':
							window.location.href = '/book/error?ErrorType=SessionTimeOut';
							break;
						default:
							if (result.code !== undefined) {
								window.location.href = result.RedirectURL;
							} else {
								return false;
							}
							break;
					}
				} else {
					window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
				}
			};

			return ctrl;
		}
	]);

})(angular);
;
(function (ng) {

    // Hotel Details Controller
    // --------------------------------------------
    //
    // * **Class:** haHotelDetailCtrl
    // * **Author:** Nathan Probst
    //
    // Controller for Hotel Details view

    'use strict';


    var module;
    try {
        module = ng.module('haHotelPackagesModule');
    } catch (e) {
        module = ng.module('haHotelPackagesModule', ['haUtilsModule', 'haGlobalsModule', 'haFeatureFlagsModule', 'haCurrencyModule', 'haAncillariesModule', 'haModalService', 'haEqualHeightModule', 'haPassengersService', 'haRoundingFiltersModule', 'haSitecoreModule']);
    }


    module.controller('haHotelDetailCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'$templateCache',
		'$location',
		'$timeout',
		'$document',
		'$window',
		'$filter',
		'haModal',
		'haConfig',
		'haHotelRoomService',
		'haHotelCommonService',
		'haAncillariesSvc',
		'haFeatureFlags',
		'haSitecoreStrings',

		function ($log, $scope, $rootScope, $templateCache, $location, $timeout, $document, $window, $filter, haModal, haConfig, roomSvc, hotelSvc, ancillariesSvc, flags, $scs) {

		    window.scrollTo(0, 0);

		    var hotelId = $location.search().hotelId;
		    var selectedRooms = [];

		    var trackingAddRemoveEvents = function (action, options) {
		        switch (action) {
		            case 'add':
		                // Custom Event for Analytics
		                document.body.dispatchEvent(new CustomEvent('VacationPackageAdded'));
		                break;
		        }
		    };

		    var ctrl = {
		        INITIAL_ROW_LIMIT: 3,
		        loadingRooms: true,
		        airportCode: null,
		        destinationCity: null,
		        hotel: null,
		        checkIn: null,
		        checkOut: null,
		        travelers: [],
		        adultTravelers: [],
		        nights: 1,
		        description: '',
		        fullDescription: '',
		        amenities: {
		            general: [],
		            room: [],
		            parking: [],
		            internet: []
		        },
		        visuals: [],
		        lat: null,
		        lon: null,

		        rowLimit: [],
		        checkInTraveler: [],
		        guestPreferences: [],

		        showGuestPrefs: false,
		        toggleGuestPrefs: function () {
		            ctrl.showGuestPrefs = !ctrl.showGuestPrefs;
		        },

		        showFullDescriptionModal: function () {
		            haModal({
		                id: 'FullDescriptionModal',
		                backdrop: 'true',
		                scope: $scope,
		                template: $templateCache.get('FullDescriptionModal.html')
		            });
		        },

		        openChangeRoomsModal: function () {
		            roomSvc.openChangeRoomsModal();
		        },

		        incRowLimitTo: function (index, toValue, everyMs) {
		            // $log.debug('incRowLimitTo()', index, toValue, everyMs);
		            // $log.debug('ctrl.rowLimit[index]', ctrl.rowLimit[index]);

		            if (ctrl.rowLimit[index] < toValue) {
		                ctrl.rowLimit[index] += 1;
		                $timeout(function () {
		                    ctrl.incRowLimitTo(index, toValue, everyMs);
		                }, everyMs);
		            }
		        },

		        resetRowLimit: function (index) {
		            ctrl.rowLimit[index] = ctrl.INITIAL_ROW_LIMIT;
		        },

		        canAddToItinerary: function () {
		            return selectedRooms && selectedRooms.length > 0;
		        },

		        addToItinerary: function () {
		            var handleError = function (data) {
		                $log.error('addToItinerary(): ERROR:', data);
		                handleExceptions(data);
					};
		            $rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off
		            ancillariesSvc.addHotel(buildAddHotelPayload(ctrl))
					.then(function (data) {
					    if (data.Success) {
					        // Return to Itinerary
					        trackingAddRemoveEvents('add');
					        $window.location.assign('/Book/Itinerary/Index');
					    } else {
					        handleError(data);
					    }
					}, handleError);
		        },
		        markerData: function () {
		            return [{
		                coords: ctrl.hotel.coords
		            }];
		        },
		        getFilteredSavings: function (savings) {
		            var savingsFilter = $filter('superCurrency')(savings, $scope.$currency, 'roundSavings');
		            return $scs('Ancillary.savexwiththispackagetext').replace('{0}', savingsFilter);
		        },
		        initRoomPicker: function () {
		            var svc = roomSvc;
		            var roomPicker = {};
		            roomPicker.rooms = svc._rooms.map(function (r) {
		                return {
		                    RoomIndex: r.RoomIndex,
		                    AdultCount: r.AdultCount + r.SeniorCount,
		                    ChildCount: r.ChildCount + r.InfantCount,
		                    SeniorCount: 0,
		                    InfantCount: 0
		                };
		            });
		            roomPicker.roomCount = roomPicker.rooms.length;
		            roomPicker.changeRoomCount = function (e, v) {
		                var diff = roomPicker.roomCount - roomPicker.rooms.length;
		                if (diff < 0) {
		                    // removing rooms
		                    roomPicker.rooms.splice(roomPicker.roomCount);
		                } else {
		                    // adding rooms
		                    for (var i = 0; i < diff; i++) {
		                        svc.addRoom(roomPicker.rooms);
		                    }
		                }

		            };
		            roomPicker.addRoom = function () {
		                svc.addRoom(roomPicker.rooms);
		            };
		            roomPicker.canAddRoom = function () {
		                return svc.canAddRoom(roomPicker.rooms);
		            };
		            var range = function (start, max) {
		                var a = [];
		                for (var i = start; i <= max; i++) {
		                    a.push(i);
		                }
		                return a;
		            };
		            roomPicker.getAdultOptions = function () {
		                var min = Math.min(svc._travelers.length, svc.getMaxAdultsPerRoom(), svc.getNumAdults());
		                return range(1, min);
		            };
		            roomPicker.getChildOptions = function () {
		                var min = Math.min(svc._travelers.length, svc.getMaxGuestsPerRoom(), svc.getNumChildren());
		                return range(0, min);
		            };
		            roomPicker.getRoomOptions = function () {
		                var min = Math.min(svc.getMaxRooms(), svc.getNumAdults());
		                return range(1, min);
		            };

		            roomPicker.updateRooms = function () {
		                var errors = svc.validate(roomPicker.rooms);
		                if (errors == null) {
		                    var seniors = svc._numSeniors;
		                    var infants = svc._numInfants;

		                    // I would use a map() here, but we want to process
		                    // the array from tail to head...
		                    angular.copy(roomPicker.rooms, svc._rooms);
		                    for (var idx = roomPicker.rooms.length - 1; idx >= 0; idx--) {
		                        var r = roomPicker.rooms[idx];
		                        var s = 0;
		                        var a = r.AdultCount;
		                        var c = r.ChildCount;
		                        var i = 0;

		                        if (seniors > 0) {
		                            s = Math.min(a, seniors);
		                            a = a - s;
		                            seniors -= s;
		                        }

		                        if (infants > 0) {
		                            i = Math.min(c, infants);
		                            c = c - i;
		                            infants -= i;
		                        }

		                        svc._rooms[idx] = {
		                            RoomIndex: r.RoomIndex,
		                            SeniorCount: s,
		                            AdultCount: a,
		                            ChildCount: c,
		                            InfantCount: i
		                        };
		                    }
		                    roomPicker.errorMessage = null;

		                    $rootScope.$broadcast('haHotelRoomService:updateRooms', svc._rooms);
		                } else {
		                    roomPicker.errorMessage = errors.join('<br>');
		                }
		            };
		            $scope.roomPicker = roomPicker;
		        },
		        UpdateHotelDetailMark:  function()
		        {
		            window.performance.clearMarks("mark_end_HotelDetail");
		            window.performance.mark("mark_end_HotelDetail");

		            if (window.performance.getEntriesByName("mark_end_HotelDetail") && window.performance.getEntriesByName("mark_end_HotelDetail")[0]) {
		                var hotelMeasure = window.performance.getEntriesByName("mark_end_HotelDetail")[0].startTime;
		                if (window.BOOMR && BOOMR.version) {
		                    window.BOOMR.sendTimer("Hotel_Det_Time", hotelMeasure);
		                    window.BOOMR.sendMetric("UI_Detail_Load", 1);
		                }
		            }
		        },
		        UpdateRoomMark: function () {
		            window.performance.clearMarks("mark_end_RoomDetail");
		            window.performance.mark("mark_end_RoomDetail");
		            if (window.performance.getEntriesByName("mark_end_RoomDetail") && window.performance.getEntriesByName("mark_end_RoomDetail")[0]) {
		                var roomMeasure = window.performance.getEntriesByName("mark_end_RoomDetail")[0].startTime;
		                if (window.BOOMR && BOOMR.version) {
		                    window.BOOMR.sendTimer("Room_Det_Time", roomMeasure);
		                    window.BOOMR.sendMetric("UI_Room_Load", 1);
		                }
		            }
		        },
		        UpdateRoomFailMetric: function () {
		            if (window.BOOMR && BOOMR.version) {
		                window.BOOMR.sendMetric("UI_Room_Fail", 1);
		            }
		        }
		    };


		    // Utility Functions
		    ctrl.roomsRange = function () {
		        var range = [];
		        for (var i = 1; i <= ctrl.roomsCount() ; i++) {
		            range.push(i);
		        }
		        return range;
		    };

		    ctrl.select = function (room, number) {
		        if (typeof number === 'undefined') {
		            // select type for all rooms
		            for (var i = 1; i < selectedRooms.length; i++) {
		                selectedRooms[i] = room;
		            }
		        } else {
		            selectedRooms[number] = room;
		        }

		        recalcTotalPrice();
		    };

		    ctrl.isSelected = function (room, number) {
		        if (typeof number === 'undefined') {
		            throw 'Must specfiy room number.';
		        }

		        return room === selectedRooms[number];
		    };

		    ctrl.priceDelta = function (room, number) {
		        if (typeof number === 'undefined') {
		            throw 'Must specfiy room number.';
		        }

		        var delta = room.nightlyPriceDiff - selectedRooms[number].nightlyPriceDiff;
		        // $log.debug('priceDelta()', arguments, delta);

		        return delta;
		    };

		    var recalcTotalPrice = function () {
		        var totalCost = ctrl.hotel.basePrice + ctrl.hotel.taxes;
		        ctrl.hotel.totalCost = selectedRooms.reduce(function (sum, room) {
		            return sum + room.totalPriceDiff;
		        }, totalCost);
		        ctrl.hotel.resortFees = selectedRooms.reduce(function (sum, room) {
		            return sum + room.resortFees;
		        }, 0);
		        // angular.forEach(selectedRooms, function (r) {
		        //   if ((r != null) && (r.totalPriceDiff != null)) {
		        //     ctrl.hotel.resortFees += r.resortFees;
		        //     ctrl.hotel.totalCost += r.totalPriceDiff;
		        //   }
		        // });
		    };

		    var init = function () {
		        // Make sure we have data...
		        if (hotelSvc.hotels == null) {
		            $log.debug('hotelSvc.hotels == null');
		            $timeout(function () {
		                init();
		            }, 500);
		            return;
		        }

		        angular.forEach(hotelSvc.hotels, function (h) {
		            if (h.id === hotelId) {
		                ctrl.hotel = h;
		            }
		        });
		        ctrl.hotel.addressUrl = 'https://www.google.com/maps/embed/v1/place?q=' + ctrl.hotel.fullAddress + '&key=AIzaSyAsvniZHrYuaSY3T7n_LSlDd21i9HVJZAo';

		        var data = hotelSvc.data;
		        ctrl.airportCode = data.DestinationAirport;
		        ctrl.destinationCity = data.DestinationCityName;
		        ctrl.roomsCount = roomSvc.count;
		        ctrl.travelers = data.Travellers;
		        ctrl.checkOut = ctrl.hotel.checkOut;
		        ctrl.checkIn = ctrl.hotel.checkIn;

		        var desc = ctrl.hotel.description;
		        if (desc.length > 500) {
		            ctrl.fullDescription = desc;
		            ctrl.description = desc.slice(0, 500) + '...';  // First 500 chars...
		        } else {
		            ctrl.fullDescription = null;
		            ctrl.description = desc;
		        }

		        angular.forEach(ctrl.travelers, function (t) {
		            if (t.Type === 'Adult' || t.Type === 'ADT' || t.Type === 'SRC') {
		                t.FullName = t.FirstName + ' ' + t.LastName;
		                ctrl.adultTravelers.push(t);
		            }
		        });
		        ctrl.initRoomPicker();
		    };

		    var showChangeModal = function (data, strings) {
		        var scope = $rootScope.$new(true, $scope);
		        scope.back = $scope.back;
		        scope.data = data;
		        scope.strings = strings;

		        haModal(haConfig.getTemplateUrl('room-change-modal.html'), {
		            id: 'HotelChangeModal',
		            backdrop: true,
		            scope: scope,
		            modalLock: true
		        });
		    };

		    var handleAPHMessages = function (data) {
		        // Cancel if not displaying hotel...
		        if (!$location.search().hotelId || $location.search().hotelId.length === 0) {
		            return;
		        }

		        var NOT_SET = 0;
		        var NO_DP_SOLUTIONS_FOUND = 1;
		        var PRICE_INCREASE = 2;
		        var PRICE_DECREASE = 3;
		        var ROOM_NOT_AVAILABLE = 4;

		        if (data == null || data.MessageType === NOT_SET) {
		            return;
		        }

		        var strings = {};
		        switch (data.MessageType) {
		            case PRICE_DECREASE:
		                ctrl.showPriceDecreaseAlert = true;
		                return;
		            case NO_DP_SOLUTIONS_FOUND:
		                strings.ModalTitle = $scs('Ancillary.repriceerrormessage');
		                strings.ModalExplanation = $scs('Ancillary.repriceerrortext');
		                strings.ModalCancel = $scs('Ancillary.orreturntotext');
		                strings.ModalContinue = $scs('Ancillary.selectadifferenthoteltext');
		                if (window.BOOMR && BOOMR.version) {
		                    window.BOOMR.sendMetric("Room_Res_DtFail", 1);
		                }
		                break;
		            case PRICE_INCREASE:
		                data.showPriceChangeTable = true;
		                strings.ModalTitle = $scs('Ancillary.pricechangealertmsg');
		                strings.ModalExplanation = $scs('Ancillary.pricechangealerttext');
		                strings.ModalQuery = $scs('Ancillary.wouldyouliketocontinuetext', [ctrl.hotel.nights]);
		                strings.ModalCancel = $scs('Ancillary.selectadifferenthoteltext');
		                strings.ModalContinue = $scs('Ancillary.yescontinuebookingtext');
		                strings.OriginalPrice = $scs('Ancillary.originalpricetext');
		                strings.OriginalTotal = $scs('Ancillary.originaltotaltext');
		                strings.UpdatedPrice = $scs('Ancillary.updatedpricetext');
		                strings.UpdatedTotal = $scs('Ancillary.updatetotaltext');
		                break;
		            case ROOM_NOT_AVAILABLE:
		                data.showPriceChangeTable = true;
		                strings.ModalTitle = $scs('Ancillary.roomchangealertmsg');
		                strings.ModalExplanation = $scs('Ancillary.roomchangealerttext');
		                strings.ModalQuery = $scs('Ancillary.wouldyouliketocontinuetext');
		                strings.ModalCancel = $scs('Ancillary.selectadifferenthoteltext');
		                strings.ModalContinue = $scs('Ancillary.yescontinuebookingtext');
		                strings.OriginalPrice = $scs('Ancillary.originalpricetext');
		                strings.OriginalTotal = $scs('Ancillary.originaltotaltext');
		                strings.UpdatedPrice = $scs('Ancillary.updatedpricetext');
		                strings.UpdatedTotal = $scs('Ancillary.updatetotaltext');
		                break;
		        }

		        showChangeModal(data, strings);
		    };


		    var reprice = function (hotelId) {
		         ancillariesSvc.getRepriceHotelDetails(hotelId, { timeout: flags.get('HotelRepriceTimeoutMs') })
				.then(function (data) {
				    // Cancel if not displaying this hotel...
				    if (hotelId !== $location.search().hotelId) {
				        return;
				    }

				    if ((data == null) || (data.ErrorCode != null) || (data.ErrorType != null)) {
				        if (window.BOOMR && BOOMR.version) {
				            window.BOOMR.sendMetric("Room_Res_DtFail", 1);
				        }
				        return error(data);
				    }
				    if (window.BOOMR && BOOMR.version) {
				        window.BOOMR.sendMetric("Room_Res_Detail", 1);
				    }
				    handleAPHMessages(data.APHMessages);

				    var pkg = data.PackageSolution;
				    if (pkg != null) {
				        ctrl.hotel.basePrice = pkg.BasePrice.Amount;
				        ctrl.hotel.taxes = pkg.Taxes.Amount;
				        ctrl.hotel.savings = pkg.ServicePriceTotalSavings;
				        ctrl.hotel.total = pkg.Total;
				    }

				    var details = data.PkgHotelSolution && data.PkgHotelSolution.HotelDetails;
				    if (details != null) {
				        mapAmenities(details.Content.Amenities, ctrl.amenities);

				        ctrl.visuals = details.Content.Visuals;
				        ctrl.lat = details.Geocoded.Latitude.Value;
				        ctrl.lon = details.Geocoded.Longitude.Value;
				    }

				    initRooms(data);
				    recalcTotalPrice();

				    ctrl.loadingRooms = false;

				}, error);


		    };


		    var initRooms = function (data) {
		        ctrl.hotel.rooms = hotelSvc.mapOrbitzRepriceRooms(data, ctrl.hotel.taxes);

		        for (var i = 1; i <= ctrl.roomsCount() ; i++) {
		            ctrl.rowLimit[i] = ctrl.INITIAL_ROW_LIMIT;

		            // var roomRowLimit = 0;
		            var selectedRoomMatched = false;
		            if (data.selectedHotelDetail !== null &&
						angular.isDefined(data.selectedHotelDetail) &&
						angular.isDefined(data.selectedHotelDetail.SelectedRooms[i - 1])) {

		                for (var j = 0; j < ctrl.hotel.rooms[i - 1].length; j++) {
		                    var r = ctrl.hotel.rooms[i - 1][j];
		                    if ((r.roomCode === data.selectedHotelDetail.SelectedRooms[i - 1].RoomCode) &&
								(r.rateAccessCode === data.selectedHotelDetail.SelectedRooms[i - 1].RateAccessCode)) {
		                        selectedRoomMatched = true;
		                        selectedRooms[i] = r;

		                        if (j >= ctrl.INITIAL_ROW_LIMIT) {
		                            ctrl.rowLimit[i] = j + 1;
		                        }

		                        break;
		                    }
		                }
		            }

		            if (((selectedRooms[i] == null) || !selectedRoomMatched) &&
						ctrl.hotel.rooms[i - 1] != null) {
		                selectedRooms[i] = ctrl.hotel.rooms[i - 1][0];
		            }

		            if (angular.isDefined(data.selectedHotelDetail) && (data.selectedHotelDetail != null) &&
						angular.isDefined(data.selectedHotelDetail.SelectedRooms)) {
		                angular.forEach(data.selectedHotelDetail.SelectedRooms, function (r) {
		                    if (angular.isDefined(r.GuestPreferences)) {
		                        ctrl.guestPreferences[i] = {
		                            disabledAccess: r.GuestPreferences.DISABLED_ACCESS,
		                            lateCheckIn: r.GuestPreferences.LATE_CHECKIN,
		                            infantCot: r.GuestPreferences.INFANT_COT,
		                            extraBed: r.GuestPreferences.EXTRA_BED,
		                            nonSmoking: r.GuestPreferences.NON_SMOKING
		                        };
		                    }
		                });
		            } else {
		                ctrl.guestPreferences[i] = {};
		            }

		            ctrl.checkInTraveler[i] = ctrl.adultTravelers[0];
		        }

		    };

		    var error = function (data) {
		        ctrl.loadingRooms = false;

		        if (data.code != null) {
		            ctrl.error = data;
		            if (data.code === 'NO_DP_SOLUTIONS_FOUND') {
		                ctrl.error.redirectUrl = null;

		                data.APHMessages = data.APHMessages || {};
		                data.APHMessages.MessageType = 1;
		                handleAPHMessages(data.APHMessages);
		            }
		        } else if (data.status === 0) {
		            ctrl.error = {
		                type: 'TIMEOUT'
		            };
		        } else {
		            ctrl.error = true;
		        }

		        if (ctrl.error.redirectUrl != null) {
		            window.location.href = ctrl.error.redirectUrl;
		        } else if (data.RedirectURL != null) {
		            window.location.href = data.RedirectURL;
		        }
		    };

		    var buildAddHotelPayload = function (ctrl) {
		        var json = {
		            'HotelId': ctrl.hotel.id,
		            'Name': ctrl.hotel.name,
		            'ImageUrl': ctrl.hotel.propertyImageUrl,
		            'CheckInDate': ctrl.hotel.checkIn,
		            'CheckOutDate': ctrl.hotel.checkOut,
		            'Nights': ctrl.hotel.nights,
		            'PackageTaxes': ctrl.hotel.taxes,
		            'PackageSubtotal': (ctrl.hotel.totalCost - ctrl.hotel.taxes),
		            'PackageGrandTotal': ctrl.hotel.totalCost,
		            'PackageSavings': ctrl.hotel.savings,
		            'Address': ctrl.hotel.address,
		            'ResortFees': ctrl.hotel.resortFees,
		            'SelectedRooms': []
		        };

		        angular.forEach(selectedRooms, function (r, i) {
		            if (r == null) {
		                return;
		            }
		            json.SelectedRooms[i - 1] = {
		                'RoomIndex': i,
		                'RoomCode': r.roomCode,
		                'RateCode': r.rateCode,
		                'RateAccessCode': r.rateAccessCode,
		                'RoomDescription': r.description,
		                'CheckInTraveler': ctrl.checkInTraveler[i].FullName,
		                'GuestPreferences': {
		                    'DISABLED_ACCESS': ctrl.guestPreferences[i].disabledAccess || false,
		                    'LATE_CHECKIN': ctrl.guestPreferences[i].lateCheckIn || false,
		                    'INFANT_COT': ctrl.guestPreferences[i].infantCot || false,
		                    'EXTRA_BED': ctrl.guestPreferences[i].extraBed || false,
		                    'NON_SMOKING': ctrl.guestPreferences[i].nonSmoking || false
		                }
		            };
		        });

		        return json;
		    };


		    var handleExceptions = function (result) {
		        if (result !== 'jsError') {
		            switch (result.code) {
		                case 'SessionTimeOut':
		                    window.location.href = '/book/error?ErrorType=SessionTimeOut';
		                    break;
		                default:
		                    if (result.code !== undefined) {
		                        window.location.href = result.RedirectURL;
		                    } else {
		                        return false;
		                    }
		                    break;
		            }
		        } else {
		            window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
		        }
		    };

		    init();
		    reprice(hotelId);

		    $scope.$on('haHotelRoomService:updateRooms', function () {
		        ctrl.loadingRooms = true;
		    });

		    $scope.$on('haHotelListCtrl:update', function () {
		        ctrl.loadingRooms = true;
		        reprice(hotelId);
		    });

		    ctrl.updatePrefs = function (index) {
		        // Copy prefs to all rooms for expedia
		        for (var i = 1; i < ctrl.guestPreferences.length; i++) {
		            ctrl.guestPreferences[i] = ctrl.guestPreferences[index];
		        }
		    }

		    return ctrl;
		}
    ]);


    function mapAmenities(from, to) {
        var ROOM_CODES = [
			'AC120',
			'DC120',
			'DC220',
			'AC220',
			'AIRCON',
			'ALRMCK',
			'ANNEX',
			'AP',
			'BALCON',
			'BATH',
			'BAY',
			'BAYVW',
			'BB',
			'BCHVW',
			'BEACH',
			'BIDET',
			'BP',
			'BRKFST',
			'BTHTEL',
			'CANOPY',
			'CASTLE',
			'CB',
			'CITYVW',
			'COFMKR',
			'COMPUT',
			'CONN',
			'CP',
			'CRDTEL',
			'CRIB',
			'CRTYRD',
			'DINNER',
			'DOUBLE',
			'DOWN',
			'DWNIN',
			'DWNOUT',
			'EFFCY',
			'EP',
			'EXLEVL',
			'FAMILY',
			'FAP',
			'FIRELT',
			'FIREPL',
			'GARDEN',
			'GARDVW',
			'GOLFVW',
			'GRABBR',
			'GRECTB',
			'HAIR',
			'HEAT',
			'HNYSTE',
			'IRON',
			'IRONBD',
			'JACUZ',
			'KING',
			'KITETT',
			'KITFUL',
			'LAKE',
			'LAKEVW',
			'LANAI',
			'LOFT',
			'MADSVC',
			'MAP',
			'MARINA',
			'MARNVW',
			'MDMRM',
			'MICRWV',
			'MINBAR',
			'MODEM',
			'MOVIES',
			'MP',
			'MTNVW',
			'MURPHY',
			'NWSSTD',
			'OCEAN',
			'OCNVW',
			'OVRSZD',
			'PARK',
			'PARKVW',
			'PHNSRV',
			'POOLVW',
			'QUEEN',
			'RADIO',
			'RECLNR',
			'REFRIG',
			'RIVER',
			'RIVVW',
			'RLWY',
			'ROOMOR',
			'ROOMWE',
			'SAFDEP',
			'SAFE',
			'SHOWER',
			'SICO',
			'SINGLE',
			'SITTNG',
			'SLOPVW',
			'SMKDET',
			'SOFABD',
			'SPRINK',
			'STMBTH',
			'SUITE',
			'TDDTEL',
			'TEL',
			'TELEX',
			'TERACE',
			'THEMRM',
			'TMPCTL',
			'TOILET',
			'TOWER',
			'TPRCDR',
			'TRNDWN',
			'TV',
			'TVCAB',
			'TVRMOT',
			'TWIN',
			'UPSTRS',
			'VALTCG',
			'VCR',
			'VIDEO',
			'VILLA',
			'VIP',
			'WC',
			'WETBAR',
			'WTRBD',
			'FRCLLS',
			'CTYCTR',
			'HSTCTY',
			'BRKBUF',
			'CDPLYR',
			'DSKLMP',
			'DVD',
			'ELECDR',
			'EXSTAY',
			'MOUNT',
			'PHSPKR',
			'RURAL',
			'STEKID',
			'TVSAT',
			'ORBBRK',
			'SMOKING',
			'HALF_BOARD',
			'FULL_BOARD',
			'BREAKFAST_BOARD',
			'FOOD_WINE',
			'HAMMOCK',
			'DOUBLE_JACUZZI',
			'BREAK_FAST',
			'BUFFET_BREAK',
			'SHARED_LOUNGE',
			'SHARED_KITCHEN',
			'WATER_PARK',
			'SHARED_IN_BATHROOM',
			'VERANDAH',
			'BROBE',
			'BTHAM',
			'VMAIL',
			'SHBTH',
			'FLTSCR',
			'WRDSK',
			'CHDCR',
			'CPLYRM',
			'GRDNAR',
			'DINRM',
			'PHY2',
			'PHY34',
            '1', '132', '130', '144', '141', '2058', '26', '2166', '2544', '143', '1073743379', '1073743378', '145', '2545', '2381', '2026', '1073743291', '2059', '1073743371', '2378', '1073743370', '1073743311', '2370', '2398', '1073743284', '136', '3857', '2399', '2086', '1073743373', '2420', '146', '2423', '2030', '2655', '2183', '1073743312', '2057', '2653', '4951', '2396', '4642', '1073743823', '1073742816', '131', '4315', '4643', '147', '2150', '1073743821', '2034', '2055', '371', '2421',
            '4420', '3858', '2081', '2575', '331', '4137', '2162', '2038', '2037', '1073743289', '4216', '4233', '2180', '1073743830', '4321', '2379', '1073743704', '2630', '4327', '139', '2149', '2631', '1073743381', '4096', '1073742841', '4950', '4097', '128', '1073743828', '4158', '142', '2397', '1073743705', '340', '2168', '4952', '4948', '4954', '4316', '4187', '4098', '4120', '4303', '1073743714', '4953', '312', '1073743822', '4162', '1073743716', '4132', '4150', '4226', '4133', '4104',
            '4153', '2854', '2039', '2054', '4311', '4100', '1073743725', '1073743713', '1073743709', '6147', '2170', '1073743826', '2045', '3502', '4208', '4143', '1073743837', '1073743829', '4326', '2044', '135', '1073743844', '4224', '2025', '3501', '1073743723', '4183', '1073743718', '2559', '4127', '1073743707', '4949', '1073743824', '1073743835', '2389', '1073743285', '2571', '4319', '4103', '2130', '3911', '2181', '4121', '4177', '2035', '4205', '4310', '1073743805', '4218', '4130',
            '1073743722', '4313', '4302', '2572', '399', '2402', '4213', '1073742840', '1073742815', '3982', '4138', '1073743734', '1073743727', '2036', '4140', '2807', '1073743290', '4307', '4099', '4135', '1073743292', '4126', '3726', '4156', '1073743833', '2576', '2561', '4147', '4232', '4188', '1073743293', '1073743732', '4231', '4209', '4152', '6148', '1073742775', '4308', '1073743731', '4129', '1073743706', '1073743836', '4168', '1073743825', '4229', '2573', '1073743736', '1073743743',
            '1073743406', '4946', '5179', '1073743375', '1073743862', '4228', '4161', '4304', '1073743374', '1073743715', '4217', '3923', '2569', '4333', '1073743309', '1073743569', '1073743316', '1073743741', '1073743724', '1073743806', '6142', '5178', '4225', '1073743740', '4227', '1073743840', '4207', '1073743838', '4200', '6140', '1073743796', '1073743283', '1073743726', '1073743752', '4164', '1073743745', '1073743851', '6143', '1073743717', '1073743804', '1073743832', '1073743733',
            '1073743710', '4191', '1073743795', '4206', '5014', '6141', '5118', '3856', '1073743749', '2837', '4141', '4314', '1073743719', '1073743735', '1073743842', '1073743750', '4182', '1073742782', '1073743808', '1073743761', '2577', '4215', '4220', '3924', '1073743814', '1073743556', '1073743728', '1073743799', '1073743754', '1073743797', '2371', '1073743560', '1073743839', '1073743742', '5013', '1073743794', '1073743708', '1073743758', '1073743770', '4180', '2790', '1073742604',
            '1073743317', '1073743744', '2789', '1073743737', '1073743845', '4945', '1073743759', '1073743767', '1073743858', '1073743779', '1073743807', '1073743763', '4330', '1073743561', '1073743815', '1073743843', '1073743751', '1073743803', '1073743788', '1073743558', '4178', '1073743776', '1073743563', '1073743772', '1073743849', '1073743768', '1073743753', '1073743746', '1073743813', '1073743847', '1073743570', '1073743809', '1073743785', '1073743760', '1073743817', '1073743781',
            '1073743559', '1073743810', '1073743557', '1073743777', '1073743711', '1073743762', '1073743800', '1073743790', '1073743755', '1073743729', '1073743801', '1073743720', '1073743786', '1073743769', '1073743816', '1073743738', '1073743850', '1073743771', '1073743798', '1073743764', '1073743852', '1073743856', '1073743818', '1073743846', '1073743812', '1073743564', '1073743747', '1073743778', '1073743854', '1073742865', '1073743773', '1073743780', '1073743859', '1073743782',
            '1073743756', '1073743787', '1073743861', '1073743789', '1073743791', '1073743857', '1073743853', '1073743880', '1073742866', '1073743765', '1073743819', '4644', '6177', '1073743774', '1073743881'
        ];

        var PARKING_CODES = [
			'FPRKNG',
			'PRKBUS',
			'PRKIND',
			'PRKNG',
			'PRKOUT',
			'PRKRV',
			'PRKTRK',
			'PRKVAL',
			'FREE_PARKING',
			'PRKCST',
			'PHY50',
			'HAC116',
            '3861', '1073742906', '3862', '3761', '4454', '2783', '3863', '4452', '3864', '2554', '4450', '4455', '4451', '4456', '4453', '1073743260', '1073743261'
        ];

        var INTERNET_CODES = [
			'DATAPT',
			'INTHS',
			'INTRNT',
			'PHDATA',
			'ORBWLS',
			'HAC179',
			'HAC261',
			'HAC222',
			'RMA207',
            '2390', '2403', '1073742907', '2405', '2392', '4348', '2391', '4347', '4346', '2393', '2404', '3191', '3190', '3179', '4345', '3178', '1073742908', '3189', '2406', '3188', '2407', '2408', '3177', '3176', '4154', '4155', '1073743394', '3199', '1073743398', '3185', '1073743399', '1073743395', '1073743392', '1073743393', '3182', '3184', '3198', '1073743397', '1073743396', '3197', '3183', '1073743402', '3196', '1073743401', '1073743400', '1073743251'
        ];

        for (var i = 0; i < from.length; i++) {
            var a = from[i];
            if (INTERNET_CODES.indexOf(a.AmenityCode) > -1) {
                to.internet.push(a);
                continue;
            }
            if (PARKING_CODES.indexOf(a.AmenityCode) > -1) {
                to.parking.push(a);
                continue;
            }
            if (ROOM_CODES.indexOf(a.AmenityCode) > -1) {
                to.room.push(a);
                continue;
            }
            to.general.push(a);
        }
    }


})(angular);
;
(function (ng) {

	// Hotel List Controller
	// --------------------------------------------
	//
	// * **Class:** haHotelListCtrl
	// * **Author:** Nathan Probst
	//
	// Controller for Hotel List

	'use strict';

	var module;
	try {
		module = ng.module('haHotelPackagesModule');
	} catch (e) {
		module = ng.module('haHotelPackagesModule', ['haUtilsModule', 'haGlobalsModule', 'haFeatureFlagsModule', 'haCurrencyModule', 'haAncillariesModule', 'haModalService', 'haEqualHeightModule', 'haPassengersService', 'haRoundingFiltersModule', 'haSitecoreModule']);
	}

	module.run(['$rootScope', 'haModal', function ($rootScope, haModal) {
		$rootScope.hotelPromoTermsModal = function (promo) {
			if (!promo || (!promo.customerFulfillmentRequirement && !promo.termsAndConditions)) {
				return;
			}

			var template = '<div class="modal-template"><div class="modal-main containerFullBleed padded-main"><div class="container"><div class="row"><div class="col">';
			if (promo.customerFulfillmentRequirements) {
				template += '<p>' + promo.customerFulfillmentRequirements + '</p>';
			}
			if (promo.termsAndConditions && promo.termsAndConditions !== promo.customerFulfillmentRequirements) {
				template += '<p>' + promo.termsAndConditions + '</p>';
			}
			template += '<p></p></div></div></div></div></div>';

			haModal(null, {
				id: 'hotel-promo-tc-modal',
				backdrop: 'true',
				template: template
			});
		};
	}]);

	module.controller('haHotelListCtrl', [
		'$log',
		'$scope',
		'$window',
		'$rootScope',
		'$timeout',
		'$location',
		'$filter',
		'$templateCache',
		'haUtils',
		'haConfig',
		'haModal',
		'haGlobals',
		'haFeatureFlags',
		'haHotelRoomService',
		'haHotelCommonService',
		'haAncillariesSvc',
		'haPassengersService',
		'haSecondaryHeaderSvc',
		'haSitecoreStrings',

		function ($log, $scope, $window, $rootScope, $timeout, $location, $filter, $templateCache, haUtils, haConfig, haModal, haGlobals, flags, roomSvc, hotelSvc, ancillariesSvc, $pax, haSecondaryHeaderSvc, $scs) {

			haSecondaryHeaderSvc.template = '/HotelSecondaryStickyHeader.html';
			haSecondaryHeaderSvc.ctrl.showHelpModal = function () {
				haModal(haConfig.getTemplateUrl('ha-help-book-itinerary-modal.html'), {
					id: 'itinerary-help',
					backdrop: 'true'
				});
			};

			$scope.$on('haGoogleMap:markerClick', function () {
				if (!$rootScope.isMobile) {
				$scope.ctrl.largerMap = true;
				}
			});

			// HACK! Copying from HaItineraryController...itinerary data for sticky header
			haGlobals('itineraryDetails', function (itineraryDetails) {
				$.extend($scope, itineraryDetails);
				$scope.selectedSegments = [];
				$scope.currency = $scope.TripSummary.currency;
				$scope.PricingType = $scope.TripSummary.PricingType;
				angular.forEach($scope.PNRs, function (trip) {
					angular.forEach(trip.Trips, function (segment) {
						segment.TripOriginCity = segment.TripOriginCity.substring(0, segment.TripOriginCity.length - 5);
						segment.TripDestinationCity = segment.TripDestinationCity.substring(0, segment.TripDestinationCity.length - 5);
					});
				});
				angular.forEach($scope.TripSummary.Trips, function (trip) {
					var segment = trip.Flights[0];
					segment.IsMileagePricing = trip.IsMileagePricing;

					for (var i = 0; i < segment.AvailBookingFares.length; i++) {
						segment.selectedSeatClass = segment.AvailBookingFares[i].Name;
						segment[segment.selectedSeatClass] = segment.AvailBookingFares[i];
					}
					$scope.selectedSegments.push(segment);
					$scope.IsItineraryLoaded = true;
				});
				if (($scope.TripSummary.SelectedHotel != null) &&
					(typeof $scope.TripSummary.SelectedHotel.HotelId === 'string')) {
					$scope.SelectedHotel = $scope.TripSummary.SelectedHotel;
				}
			});

			// Constants
			var PAGE_SIZE = flags.get('HotelsPerPage', 10);

			var ctrl = {
				// Variables
				loading: true,
				airportCode: '',
				destinationCity: null,
				hotels: [],
				travelers: [],
				adultTravelers: [],
				checkIn: null,
				checkOut: null,
				roomsCount: roomSvc.count,
				nights: null,
				nameFilter: null,
				starRatingFilter: {'2': false, '3': false, '4': false, '5': false},
				hotelsBackup: [],

				// Flags
				limitedAvailabilityLevel: flags.get('HotelLimitedAvailabilityLevel', 10),
				savingsThreshold: flags.get('HotelShowSavingsThreshold', 20), // default min $10

				// Methods
				hotelId: function () {
					// Make sure we have data first...
					if (hotelSvc.data != null) {
						return $location.search().hotelId;
					} else {
						return null;
					}
				},
				showDetail: function (hotel, hotelId) {
					hotelId = hotelId || hotel.id;
				    ctrl.loading = true;
				    ctrl.hotelsBackup = angular.copy(ctrl.hotels);
				    ancillariesSvc
				        .getVacationPackageList(hotelId, { timeout: flags.get('HotelRepriceTimeoutMs') }).then(
				        	function(data) { init(data, true); }, error)
				        .then(
				            function() {
				                $location.search('hotelId', hotelId);
				                ctrl.loading = false;
				            });


				},
				clearDetail: function () {
					$location.search('hotelId', null);
				},
				openChangeRoomsModal: function () {
					roomSvc.openChangeRoomsModal();
				},
				count: function (list) {
					// var then;
					// if (typeof performance !== 'undefined') {
					//     then = performance.now();
					// }

					ctrl.starCounts = {'5': 0, '4': 0, '3': 0, '2': 0};
					ctrl.userCounts = {'5': 0, '4': 0, '3': 0, '2': 0};
					for (var h = 0; h < list.length; h++) {
						var hotel = list[h];
						for (var i = 5; i > 1; i--) {
							// Stars
							if ((hotel.starRating >= i) &&
								(hotel.starRating < (1 + i))) {
								ctrl.starCounts['' + i] += 1;
							}

							// User Scores
							if (hotel.userScore >= i) {
								ctrl.userCounts['' + i] += 1;
							}
						}
					}

					// if (typeof performance !== 'undefined') {
					//     $log.debug('ctrl.count ['+ctrl.hotels.length+']:',
					//         Math.round(1000*(performance.now()-then))+'μs');
					// }
				},

				filteredHotels: [],
				hasActiveFilters: false,
				filter: function () {
					// var then;
					// if (typeof performance !== 'undefined') {
					//     then = performance.now();
					// }

					var filters = [];
					var filtered = ctrl.hotels;

					filters.push(oneStarRatingFilter());
					filters.push(nameFilter());
					filters.push(starRatingFilter());
					filters.push(guestRatingFilter());
					filters.push(sortFilter());

					ctrl.hasActiveFilters = false;
					angular.forEach(filters, function (f) {
						ctrl.hasActiveFilters = ctrl.hasActiveFilters || f.active;
						filtered = f(filtered);
					});

					ctrl.pageTotal = filtered.length;
					ctrl.filteredHotels = filtered;
					ctrl.filters = filters;

					ctrl.resetPagination();
					ctrl.pageChange();

					// if (typeof performance !== 'undefined') {
					//     $log.debug('ctrl.filter ['+ctrl.hotels.length+' -> '+ctrl.filteredHotels.length+']:',
					//         Math.round(1000*(performance.now()-then))+'μs');
					// }
				},
				clearFilter: function (filter) {
					filter.clear();
					ctrl.filter();
				},
				clearAllFilters: function () {
					angular.forEach(ctrl.filters, function (filter) {
						if (filter.clear != null) {
							filter.clear();
						}
					});
					ctrl.filter();
				},
				currentPage: 1,
				pageSize: PAGE_SIZE,
				pageStart: 1,
				pageEnd: PAGE_SIZE,
				pageTotal: 0,
				pagedHotels: [],
				pageChange: function () {
					ctrl.pagedHotels = paginationFilter(ctrl.currentPage, ctrl.pageSize)(ctrl.filteredHotels);
				},
				resetPagination: function () {
					ctrl.currentPage = 1;
				},
				markerData: function () {
					return ctrl.pagedHotels.map(function (hotel) {
						var h = angular.copy(hotel);
						h.airportCode = ctrl.airportCode;
						h.detailUrl = '/Book/Itinerary/GetVacationPackageList#?hotelId=' + (hotel.id || hotel.HotelId);
						return {
							'coords': hotel.coords,
							'templateScope': {
								'hotel': h
							}
						};
					});
				},
				getFilteredSavings: function(savings) {
					var savingsFilter = $filter('superCurrency')(savings,$scope.$currency,'roundSavings');
					return $scs('Ancillary.savexwiththispackagetext').replace('{0}',savingsFilter);
				},
				UpdateHotelFailMetric: function () {
				  	if (window.BOOMR && BOOMR.version) {
				  		window.BOOMR.sendMetric("UI_Detail_Fail", 1);
				    }
				}

			};

			var noopFilter = function (list) {
				return list;
			};
			noopFilter.clear = function () {
			};
			noopFilter.active = false;

			ctrl.sortByOptions = [
				{Name: $scs('Ancillary.bestvaluetext'), Value: 'BestValueSortAsc'},
				{Name: $scs('Ancillary.pricelowtohightext'), Value: 'PriceSortAsc'},
				{Name: $scs('Ancillary.pricehightolowtext'), Value: 'PriceSortDesc'},
				{Name: $scs('Ancillary.starslowtohightext'), Value: 'StarsSortAsc'},
				{Name: $scs('Ancillary.starshightolowtext'), Value: 'StarsSortDesc'},
				{Name: $scs('Ancillary.hotelnameatoztext'), Value: 'HotelNameSortAsc'},
				{Name: $scs('Ancillary.distancefromairporttext'), Value: 'DistanceSortAsc'}
			];
			ctrl.sortBy = ctrl.sortByOptions[0].Value;

			var sortFns = {
				'BestValueSortAsc': '+owwRank',
				'PriceSortAsc': '+morePerNight',
				'PriceSortDesc': '-morePerNight',
				'StarsSortAsc': '+starRating',
				'StarsSortDesc': '-starRating',
				'UserScoreSortAsc': '+userScore',
				'UserScoreSortDesc': '-userScore',
				'HotelNameSortAsc': '+name',
				'DistanceSortAsc': '+distanceValue'
			};

			var sortFilter = function () {
				var sortBy = sortFns[ctrl.sortBy];
				if (sortBy != null) {
					return function (list) {
						return $filter('orderBy')(list, [sortBy, '+owwRank']);
					};
				}
				return function (list) {
					return list;
				};
			};

			var nameFilter = function () {
				if (ctrl.nameFilter == null || ctrl.nameFilter.length === 0) {
					return noopFilter;
				}

				var name = ctrl.nameFilter;
				var f = function (list) {
					var filtered = [];

					for (var i = 0; i < list.length; i++) {
						// Criteria: Results will include all hotels where entered search
						// term is at the beginning of any word in the Hotel Name,
						// e.g. "sun" would return Maui Sunset and Sunset Hotel

						// Also, to catch multi-word searches...
						if (name.split(' ').length > 1) {
							if (list[i].name.toLowerCase().indexOf(name.toLowerCase()) > -1) {
								filtered.push(list[i]);
								continue;
							}
						}

						var tokens = list[i].name.split(' ');
						for (var t = 0; t < tokens.length; t++) {
							if (tokens[t].toLowerCase().indexOf(name.toLowerCase()) === 0) {
								filtered.push(list[i]);
								break;
							}
						}
					}

					return filtered;
				};

				f.clear = function () {
					ctrl.nameFilter = undefined;
				};

				f.label = '"' + name + '"';
				f.active = true;

				return f;
			};

			var oneStarRatingFilter = function () {
				return function (list) {
					var filtered = [];

					for (var i = 0; i < list.length; i++) {
						if (list[i].starRating > 1) {
							filtered.push(list[i]);
						}
					}

					return filtered;
				};
			};

			var starRatingFilter = function () {
				var rating = ctrl.starRatingFilter;
				if (rating == null ||
					rating['5'] === false &&
					rating['4'] === false &&
					rating['3'] === false &&
					rating['2'] === false) {
					return noopFilter;
				}

				var stars = [];
				angular.forEach(rating, function (value, key) {
					if (value) {
						stars.push(key);
					}
				});

				var f = function (list) {
					var filtered = [];

					for (var i = 0; i < list.length; i++) {
						var hotel = list[i];
						for (var s = 0; s < stars.length; s++) {
							var star = parseInt(stars[s], 10);
							if ((hotel.starRating >= star) &&
								(hotel.starRating < (1 + star))) {
								filtered.push(list[i]);
								break;
							}

						}
					}

					return filtered;
				};

				f.clear = function () {
					angular.forEach(rating, function (value, key) {
						if (value) {
							ctrl.starRatingFilter[key] = false;
						}
					});
				};

				f.label = stars.slice(0, -1).join(', ');
				if (stars.length > 1) {
					f.label += ' or ';
				}
				f.label += stars[stars.length - 1] + ' stars';
				f.active = true;

				return f;
			};

			var guestRatingFilter = function () {
				if (ctrl.guestRatingFilter == null) {
					return noopFilter;
				}

				var rating = parseFloat(ctrl.guestRatingFilter);
				var f = function (list) {
					var filtered = [];

					for (var i = 0; i < list.length; i++) {
						if (list[i].userScore >= rating) {
							filtered.push(list[i]);
						}
					}

					return filtered;
				};

				f.clear = function () {
					ctrl.guestRatingFilter = undefined;
				};

				f.label = ctrl.guestRatingFilter;
				if (ctrl.guestRatingFilter !== '5.0') {
					f.label += ' or better';
				}
				f.active = true;

				return f;
			};

			var paginationFilter = function (currentPage, pageSize) {
				return function (list) {
					var filtered = [];

					ctrl.pageStart = (currentPage - 1) * pageSize + 1;
					ctrl.pageEnd = Math.min(currentPage * pageSize, list.length);
					for (var i = ctrl.pageStart; i <= ctrl.pageEnd; i++) {
						filtered.push(list[i - 1]);
					}

					return filtered;
				};
			};

			var init = function (data, isDetail) {
				// api.getNeatFallbackUrl().then(function (url) {
				//     ctrl.neatFallbackUrl = url;
				// });

				if ((data == null) ||
					(data.ErrorType != null) ||
					(data.HasServiceError === true)) {
					return error(data);
				}

				if (data.OrbitzPackage != null) {
					data = data.OrbitzPackage;
				}

				ctrl.hasPackages = data.HasRetrievedOrbitzPackage;
				ctrl.airportCode = data.DestinationAirport;
				$scope.destinationCity = ctrl.destinationCity = data.DestinationCityName;
				// if (!isDetail) {
					ctrl.hotels = hotelSvc.mapOrbitzPackages(data);
					ctrl.travelers = data.Travellers;
				// }
				angular.forEach(ctrl.travelers, function (t) {
					if (t.Type === 'Adult' || t.Type === 'ADT' || t.Type === 'SRC') {
						ctrl.adultTravelers.push(t);
					}
				});

				if (ctrl.hotels[0] != null) {
					ctrl.nights = ctrl.hotels[0].nights;
					ctrl.checkIn = ctrl.hotels[0].checkIn;
					ctrl.checkOut = ctrl.hotels[0].checkOut;
				}

				// Record natural sort order (OWW Best Value) with owwRank
				angular.forEach(ctrl.hotels, function (hotel, i) {
					hotel.owwRank = i + 1;
				});

				// For Hotel Detail Controller...
				hotelSvc.data = data;
				hotelSvc.hotels = ctrl.hotels;

				// For Rooms Modal...
				roomSvc.init(data);
				ctrl.roomsCount = roomSvc.count;


				if (!isDetail) {
					ctrl.count(ctrl.hotels);
					ctrl.filter();
					$scope.$watch('ctrl.starRatingFilter', ctrl.filter, true);
					$scope.$watch('ctrl.guestRatingFilter', ctrl.filter);


					// when going back from detail view to list view either by clicking on back link or on browser back
					// focus on the last selected hotel
					$scope.$watch(function () {
						return $location.search().hotelId;
					}, function (nv, ov) {
						if (nv === undefined && ov !== undefined) {
							ctrl.hotels = ctrl.hotelsBackup;
							// ctrl.filter();
							var selectedHotel = $('#hotel-' + ov);

							if (selectedHotel) {
								setTimeout(function () {
									var top = $('#hotel-' + ov).offset().top;
									$('html,body').animate({
										scrollTop: top - 180
									}, 350);
								}, 100);
							}

						}
					});
				}

				ctrl.loading = false;

				$rootScope.$broadcast('haHotelListCtrl:update');
			};

			var error = function (data) {
				if (data.ErrorType != null) {
					ctrl.error = {
						type: data.ErrorType,
						code: data.ErrorCodeHandle,
						message: data.ErrorMessage,
						pathType: data.PathType,
						redirectUrl: data.RedirectURL
					};
				} else if (data.HasServiceError === true) {
					ctrl.error = {
						type: 'SERVICE_ERROR',
						code: data.ServiceErrors
					};
				} else if (data.status === 0) {
					ctrl.error = {
						type: 'TIMEOUT'
					};
				}

				ctrl.loading = false;
				ctrl.clearDetail();

				ancillariesSvc.getNeatFallbackUrl().then(function (url) {
					ctrl.neatFallbackUrl = url;
				});
			};

			// Load in-page data...if exists
			haGlobals('HA', function (HA) {
				if (HA.vacationPackageList != null) {
					init(HA.vacationPackageList);
				}
			});


			ancillariesSvc.getVacationPackageList({timeout: flags.get('HotelRepriceTimeoutMs') }).then(init, error).then(function() {
				if ($location.search().hotelId) {
					ctrl.showDetail(null, $location.search().hotelId);
				}

			});




			//ancillariesSvc.getVacationPackageList({
			//    timeout: flags.get('HotelListTimeoutMs'),
			//    hotelId: $location.search().hotelId
			//}).then(init, error);

			$scope.$on('haHotelRoomService:updateRooms', function ($event, rooms) {
				ctrl.loading = true;
				ancillariesSvc.setRooms({Rooms: rooms})
				.then(function () {
				    return ancillariesSvc.getVacationPackageList($location.search().hotelId, {
					    timeout: flags.get('HotelListTimeoutMs')
				    }).then(function(data) { init(data, true); }, error);
				    ctrl.loading = false;
				})
				.then(init, error);
				ctrl.loading = false;
			});

			$scope.$pax = $pax;
			$scope.$pax.passengers = [];
			$scope.taxDetails = false;

			function addPassengerList(count, type) {
				for (var i = 0; i < count; i++) {
					$scope.$pax.add({type: type, isUser: true});
				}
			}

			if ($scope.TripSummary != null) {
				$scope.PricingType = $scope.TripSummary.PricingType;
				if ($scope.TripSummary.AdultCount > 0) {
					addPassengerList($scope.TripSummary.AdultCount, 'Adult');
				}
				if ($scope.TripSummary.ChildCount > 0) {
					addPassengerList($scope.TripSummary.ChildCount, 'Child');
				}
				if ($scope.TripSummary.InfantCount > 0) {
					addPassengerList($scope.TripSummary.InfantCount, 'Infant');
				}
			}

			return ctrl;
		}
	]);

})(angular);
;
'use strict';

var haDonateMilesModule = angular.module('haDonateMilesModule', []);


haDonateMilesModule.controller('DonateMilesController', [
	'$scope', 'haGlobals', '$timeout', 'haHttpService', 'haModal', 'haSitecoreStrings', '$filter', '$window',
	function ($scope, haGlobals, $timeout, haHttpService, haModal, $scs, $filter, $window) {

		var donateEP = '/MyAccount/DonateMiles/DonateMiles';

		$scope.DonateMiles = {
			ListCharities: null,
			SenderAccountNumber: '',
			CurrentMilesBalance: null
		};

		$scope.Error = '';

		$scope.initializeCharities = function () {
			for (var i = 0; i < $scope.DonateMiles.ListCharities.length; i++) {
				$scope.DonateMiles.ListCharities[i].state = 'display';
				$scope.DonateMiles.ListCharities[i].Terms = false;
				$scope.DonateMiles.ListCharities[i].Anonymous = '';
				if ($scope.DonateMiles.ListCharities[i].Amount == null) {
					if ($scope.DonateMiles.CurrentMilesBalance >= 50) {
						$scope.DonateMiles.ListCharities[i].Amount = '50';
					} else {
						$scope.DonateMiles.ListCharities[i].Amount = '0';
					}
				}
			}
		};

		haGlobals('donateMilesJson', function (donateMilesJson) {
			// console.log('donate miles json');
			// console.log(donateMilesJson);
			$scope.DonateMiles.SenderAccountNumber = donateMilesJson.SenderAccountNumber;
			$scope.DonateMiles.CurrentMilesBalance = donateMilesJson.CurrentMilesBalance;
			$scope.DonateMiles.ListCharities = donateMilesJson.Charities;

			$scope.initializeCharities();
		});

		haGlobals('ErrorMessage', function (ErrorMessage) {
			if (ErrorMessage !== '') {
				$scope.Error = ErrorMessage;
			}
		});

		$scope.getHelpContent = function () {
			haModal('', {
				id: 'donate-miles-help',
				backdrop: 'true',
				template: angular.element('.getHelpContent')
			});
		};

		$scope.showTerms = function () {
			haModal('', {
				id: 'terms-and-conditions',
				backdrop: 'true',
				template: angular.element('.showTerms')
			});
		};

		// pretty sure not used
		$scope.toggleAmount = function (amt, idx) {
			$scope.DonateMiles.ListCharities[idx].Amount = amt;
		};

		$scope.toggleForm = function (idx) {
			/* a select button was pressed, reset all states */
			$scope.initializeCharities();
			/* set clicked state to form */
			$scope.DonateMiles.ListCharities[idx].state = 'form';
		};

		$scope.toggleConfirm = function (idx) {
			/* validate form */
			var form = $scope.$eval('form' + idx);
			if (form && form.$invalid) {
				form.$validate(form);
				return;
			}

            // reset form submission state, since this is a multi-part form
            form.$submitted = false;
            $('form[name="' + form.$name + '"]').removeClass('submitted');

			/* set clicked state to confirm */
			$scope.DonateMiles.ListCharities[idx].state = 'confirm';
		};

		$scope.toggleSubmit = function (idx) {
			var milesData;

			if (!$scope.DonateMiles.ListCharities[idx].Terms) {
				$scope.TermsInvalid = true;
				return false;
			}

			$scope.TermsInvalid = false;

			$scope.DonateMiles.ListCharities[idx].state = 'spinning';

			milesData = {
				selectedMiles: $scope.DonateMiles.ListCharities[idx].Amount !== '0' ? $scope.DonateMiles.ListCharities[idx].Amount : $scope.DonateMiles.ListCharities[idx].AnotherAmount,
				recipientHMNumber: $scope.DonateMiles.ListCharities[idx].CharityHMAccountNumber,
				isAnonymous: !!$scope.DonateMiles.ListCharities[idx].Anonymous
			};

			haHttpService.POST(donateEP, JSON.stringify(milesData)).then(
				function (response) {
					if (response.data && response.data.MilesDonationSuccessful && !response.data.HasServiceError) {
						$scope.DonateMiles.CurrentMilesBalance = response.data.DonorMilesBalance;
						$scope.DonateMiles.ListCharities[idx].state = 'thankyou';
						// update miles in user dropdown nav per bug 141271
						var newBalanceText = $scs('DONATE_MILES.currentbalancetext') + ': ' + $filter('number')($scope.DonateMiles.CurrentMilesBalance);
						angular.element('#account-nav-list li:first-child').find('span.popover-link-secondary').text(newBalanceText);
						// console.log(response.data);
					} else {
						console.log(response.data.ServiceErrors || $window.DonateMilesGeneralErrorMessage);
						$scope.Error = $window.DonateMilesGeneralErrorMessage;
						$scope.DonateMiles.ListCharities[idx].state = 'error';
					}
				},
				function (err) {
					$scope.Error = $window.DonateMilesGeneralErrorMessage;
					$scope.DonateMiles.ListCharities[idx].state = 'error';
					console.log(err);
				}
			);
		};
	}
]);

haDonateMilesModule.directive('maxmiles', function () {
	return {
		require: 'ngModel',
		link: function (scope, elem, attr, ngModel) {
			var maxmiles = attr.maxmiles;

			//For DOM -> model validation
			ngModel.$parsers.push(function (value) {

				var valid = (value === '') || (parseInt(value) <= parseInt(maxmiles));// && parseInt(value) > 0);

				ngModel.$setValidity('maxmiles', valid);
				return valid ? value : undefined;
			});
		}
	};
});


;
'use strict';

var haMilesMaximizerModule = angular.module('haMilesMaximizerModule', []);

haMilesMaximizerModule.controller('MilesMaximizerConfirmationCodeController', [
	'$scope', 'haGlobals', 'haModal', function ($scope, haGlobals, haModal) {

		$scope.MilesMaximizer = {
			ConfirmationCode: '',
			LastName: ''
		};
		$scope.Error = '';
		$scope.submitting = false;

		haGlobals('milesMaximizerConfirmationCodeJson', function (milesMaximizerConfirmationCodeJson) {
			$scope.MilesMaximizer.ConfirmationCode = milesMaximizerConfirmationCodeJson.ConfirmationCode;
			$scope.MilesMaximizer.LastName = milesMaximizerConfirmationCodeJson.LastName;
		});

		haGlobals('errorMessage', function (errorMessage) {
			if (errorMessage !== '') {
				$scope.Error = errorMessage;
			}
		});

		$scope.getHelpContent = function () {
			haModal('', {
				id: 'miles-maximizer-help',
				backdrop: 'true',
				template: angular.element('.getHelpContent')
			});
		};

		$scope.submit = function () {
			if ($scope.MilesMaximizerForm.$valid) {
				$scope.submitting = true;
			}

		};
	}
]);

haMilesMaximizerModule.controller('MilesMaximizerMaximizeController', [
	'$scope', 'haGlobals', 'haModal', '$timeout', 'haItineraryAPI', '$window', function ($scope, haGlobals, haModal, $timeout, haItineraryAPI, $window) {

		$scope.MilesMaximize = {
			IsDoubleMyMiles: true,
			IsInterIslandMarket: false,
			ConfirmationCode: '',
			CurrencyCode: '',
			ListFlightInfo: null,
			ListPaxInfo: null,
			TotalCost: null

		};

		$scope.Error = '';
		$scope.emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
		$scope.ActivateValidationSpinner = false;
		$scope.ActivateEnrollSpinner = false;

		haGlobals('milesMaximizerMaximizeJson', function (milesMaximizerMaximizeJson) {
			$scope.MilesMaximize.IsInterIslandMarket = milesMaximizerMaximizeJson.IsInterIslandMarket;
			$scope.MilesMaximize.ConfirmationCode = milesMaximizerMaximizeJson.ConfirmationCode;
			$scope.MilesMaximize.CurrencyCode = milesMaximizerMaximizeJson.CurrencyCode;
			$scope.MilesMaximize.ListFlightInfo = milesMaximizerMaximizeJson.ListFlightInfo;
			$scope.MilesMaximize.ListPaxInfo = milesMaximizerMaximizeJson.ListPaxInfo;
		});

		$scope.radio = { amount: $scope.MilesMaximize.ListPaxInfo[0].BoostDoubleMiles };

		$scope.$watch('radio.amount', function () {
			if (parseInt($scope.radio.amount) === $scope.MilesMaximize.ListPaxInfo[0].BoostDoubleMiles) {
				$scope.MilesMaximize.IsDoubleMyMiles = true;
				$scope.MilesMaximize.TotalCost = parseFloat($scope.MilesMaximize.DoubleMilesTotalCost);
			} else {
				$scope.MilesMaximize.IsDoubleMyMiles = false;
				$scope.MilesMaximize.TotalCost = parseFloat($scope.MilesMaximize.TripleMilesTotalCost);
			}
		});

		haGlobals('errorMessage', function (errorMessage) {
			if (errorMessage !== '') {
				$scope.Error = errorMessage;
			}
		});

		$scope.getHelpContent = function () {
			haModal('', {
				id: 'miles-maximizer-help',
				backdrop: 'true',
				template: angular.element('.getHelpContent')
			});
		};

		$scope.openAddAccountNumber = function (idx) {
			$scope.currentPaxInfoIdx = idx;
			$scope.currentPaxInfo = $scope.MilesMaximize.ListPaxInfo[idx];
			$scope.currentPaxInfo.HMNumber = '';

			$timeout(function () {
				haModal({
					id: 'addAccountNumberModal',
					backdrop: 'true',
					scope: $scope,
					modalLock: true,
					template: angular.element('#addAccountNumber'),
					size: 'modal-lg'
				});
			});
		};

		$scope.accountNumberContinue = function () {
			$scope.ActivateValidationSpinner = true;
			haItineraryAPI.isValidHMAccount($scope.currentPaxInfo.HMNumber, $scope.currentPaxInfo.FirstName, $scope.currentPaxInfo.LastName).success(function (results) {
				if (results != null && results !== 'false' && results != false) {
					// Account is found
					$scope.ActivateValidationSpinner = false;
					$scope.$modalCancel();
					$scope.Error = '';
					$scope.addAccountNumberForm.$setPristine();
					//$scope.closeModal();
				} else {
					// Account was not found
					$scope.ActivateValidationSpinner = false;
					$scope.Error = $window.HMNotFoundMessage;
					return;
				}
			})
			.error(function (err) {
				$scope.ActivateValidationSpinner = false;
				$scope.Error = $window.HMAccountLookupGeneralErrorMessage;
				return;
			});
		};

		$scope.openEnrollAccount = function (idx) {
			$scope.currentPaxInfoIdx = idx;
			$scope.currentPaxInfo = $scope.MilesMaximize.ListPaxInfo[idx];

			$timeout(function () {
				haModal({
					id: 'enrollAccountNumberModal',
					backdrop: 'true',
					scope: $scope,
					modalLock: true,
					template: angular.element('#enrollAccountNumber'),
					size: 'modal-lg'

				});
			});
		};

		$scope.enrollAccountContinue = function () {
			var travelDate = new Date($scope.currentPaxInfo.TravelDate.match(/\d+/)[0] * 1);
			travelDate = (travelDate.getMonth() + 1) + '/' + travelDate.getDate() + '/' + travelDate.getFullYear();
			var paxDOB = new Date($scope.currentPaxInfo.DOB.match(/\d+/)[0] * 1);
			paxDOB = (paxDOB.getMonth() + 1) + '/' + paxDOB.getDate() + '/' + paxDOB.getFullYear();
			$scope.currentPaxInfo = angular.copy($scope.currentPaxInfo);
			$scope.currentPaxInfo.TravelDate = travelDate;
			$scope.currentPaxInfo.DOB = paxDOB;


			$scope.ActivateEnrollSpinner = true;
			haItineraryAPI.HMEnrollAndUpdatePNR($scope.MilesMaximize.ConfirmationCode, $scope.currentPaxInfo).success(function (results) {
				if (results !== null && results.IsEnrollmentSuccess !== 'false' && !$scope.accountIsEmpty(results.HMNumber)) {
					$scope.ActivateEnrollSpinner = false;
					$scope.currentPaxInfo.HMNumber = results.HMNumber;
					$scope.MilesMaximize.ListPaxInfo[$scope.currentPaxInfoIdx] = angular.copy($scope.currentPaxInfo);
					$scope.$modalCancel();
					$scope.Error = '';
					$scope.enrollAccountNumberForm.$setPristine();
					//$scope.closeModal();
				} else {
					$scope.ActivateEnrollSpinner = false;
					$scope.Error = results.EnrollmentErrorMessage || $window.EasyEnrollGeneralErrorMessage;
					return;
				}
			})
			.error(function (err, results) {
				$scope.ActivateEnrollSpinner = false;
				$scope.Error = results.EnrollmentErrorMessage || $window.EasyEnrollGeneralErrorMessage;
				return;
				});
		};

		$scope.closeModal = function () {
			$scope.$modalCancel();
			$scope.Error = '';
			if ($scope.addAccountNumberForm) {
				$scope.ActivateValidationSpinner = false;
				$scope.addAccountNumberForm.$setPristine();
			}
			if ($scope.enrollAccountNumberForm) {
				$scope.ActivateEnrollSpinner = false;
				$scope.enrollAccountNumberForm.$setPristine();
			}
			$timeout(function () {
				$scope.MilesMaximize.ListPaxInfo[$scope.currentPaxInfoIdx].HMNumber = '1';
			}, 1000);
		};

		// Show Missing Account Number Modal
		$scope.showMissingAccountNumberModal = function () {
			$timeout(function () {
				haModal('missingAccountNumber', {
					id: 'missingAccountNumberModal', scope: $scope, backdrop: 'true' });
			});
		};

		$scope.allAccountsAdded = function () {
			var accountsAdded = true;
			$scope.missingHmNumbers = new Array();
			for (var i = 0; i < $scope.MilesMaximize.ListPaxInfo.length; i++) {
				if ($scope.accountIsEmpty($scope.MilesMaximize.ListPaxInfo[i].HMNumber)) {
					$scope.missingHmNumbers.push($scope.MilesMaximize.ListPaxInfo[i]);
					accountsAdded = false;
				}
			}
			return accountsAdded;
		};

		$scope.submitMilesMaxCard = function (card) {
			if ($scope.allAccountsAdded()) {
				var form = document.getElementById("milesMaxForm");
				var input = document.createElement("input");
				input.name = "card";
				input.type = "hidden";
				input.id = "card";
				input.value = card;
				form.appendChild(input);
				document.getElementById("milesMaxForm").submit();
			} else {
				$scope.showMissingAccountNumberModal();
			}
		};

		// Check if the HM number has a value
		$scope.accountIsEmpty = function (accountNumber) {
			if (accountNumber !== null && accountNumber !== '' && accountNumber !== '1') {
				return false;
			}

			return true;
		}
	}
]);

haMilesMaximizerModule.controller('MilesMaximizerPaymentController', ['$scope', '$rootScope', 'haGlobals', 'haModal', '$timeout', function ($scope, $rootScope, haGlobals, haModal, $timeout) {
	$scope.MilesMaximizerPayment = {
		EmailAddress: '',
		ConfirmEmailAddress: ''
	};

	$scope.submitting = false;

	haGlobals('milesMaximizerPaymentJson', function (milesMaximizerPaymentJson) {
		$scope.MilesMaximizerPayment.EmailAddress = milesMaximizerPaymentJson.EmailAddress;
		$scope.MilesMaximizerPayment.ConfirmEmailAddress = milesMaximizerPaymentJson.ConfirmEmailAddress;
	});

	$scope.getHelpContent = function () {
		haModal('', {
			id: 'miles-maximizer-help',
			backdrop: 'true',
			template: angular.element('.getHelpContent')
		});
	};

	$scope.showPrivacyPolicy = function () {
		haModal('', {
			id: 'privacy-policy',
			backdrop: 'true',
			template: angular.element('.showPrivacyPolicy')
		});
	};

	$scope.showTerms = function () {
		haModal('', {
			id: 'terms-and-conditions',
			backdrop: 'true',
			template: angular.element('.showTerms')
		});
	};

	// for PCI Payment Authorization
	$scope.haPaymentTypes = {
		paymentMethod: 'creditDebit'
	};

	$scope.CDESubmit = function () {
		$rootScope.$broadcast('CDESubmit');
	};

	$scope.$on('CDEPaymentSubmitted', function() {
		$scope.submitting = true;
	});

	$scope.$on('CDEPaymentSubmitError', function() {
		$scope.submitting = false;
		$timeout(function() {
			$('html, body').animate({ scrollTop: $('#formIFrame').offset().top - 50 }, 'fast');
		}, 100);
	});
	// end PCI
}
]);


haMilesMaximizerModule.controller('MilesMaximizerConfirmationController', [
	'$scope', 'haGlobals', 'haModal', function ($scope, haGlobals, haModal) {

		haGlobals('errorMessage', function (errorMessage) {
			if (errorMessage !== '') {
				$scope.Error = errorMessage;
			}
		});

		$scope.getHelpContent = function () {
			haModal('', {
				id: 'miles-maximizer-help',
				backdrop: 'true',
				template: angular.element('.getHelpContent')
			});
		};
	}
]);
;
(function (ng) {

    // Hotel List Controller
    // --------------------------------------------
    //
    // * **Class:** haHotelListCtrl
    // * **Author:** Cory Shaw
    //
    // Controller for Hold Reservation Functionality

    'use strict';


	function FareDetail(id, dataKey, classname, MilesPricing, isFirstClassAvailable) {
        var self = this;
        self.id = id;
        self.FlightRefKey = dataKey;
        self.SelectedClass = classname;
		self.IsMileagePricing = MilesPricing;
		self.isFirstClassAvailable = isFirstClassAvailable;
    }

    function PriceOption(id, days, msrCode, price, miles, disableHoldOption) {
        var self = this;
        self.id = id;
        self.numberOfDays = days;
        self.msrCode = msrCode;
        self.price = price;
		self.miles = miles;
		self.disableHoldOption=disableHoldOption;
	}

	
	var module;
    module = ng.module('haBookFlightResultsModule');

    module.controller('holdReservationCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'$timeout',
		'$location',
		'$templateCache',
		'haUtils',
		'haConfig',
		'haModal',
		'haGlobals',
		'haFeatureFlags',
		'$http',
		'$filter',

		function ($log, $scope, $rootScope, $timeout, $location, $templateCache, haUtils, haConfig, haModal, haGlobals, flags, $http, $filter) {

			$scope.holdEligible = true;
			$rootScope.mcbHoldSelected = false;
			$scope.oneDayHoldOptionEmail = false;
			//$scope.FareholdCCApproval = false;

		    // Defaults

		    $rootScope.isEligible = false;
		    $scope.loadingHoldFareForm = false;
		    $scope.holdDetailsState = false;
		    $scope.remindersEndDate = '2015-12-23';
		    $rootScope.holdLoginSuccess = false;
		    $rootScope.selectedSegment = {};
		    $rootScope.tripSmry = {};
		    $rootScope.selectedHoldFare = {};
		    $rootScope.tripSmry = {};
		    $scope.formData = {};
		    var holdReservationsRQ = {};

		    // localization replacement for days
		    $scope.addDays = function (string, days) {
		        var updatedString = string.replace('[days]', days);
		        return updatedString;
		    };

		    $scope.getDescriptionSubText = function (days) {
		        var fareHoldOptionSubText = '';
		        switch (days) {
		            case 1:
		                fareHoldOptionSubText = holdStringsResponse.FareHoldOnedayOptionText;
		                break;
		            case 2:
		                fareHoldOptionSubText = holdStringsResponse.FareHoldTwodayOptionText;
		                break;
		            case 3:
		                fareHoldOptionSubText = holdStringsResponse.FareHoldThreedayOptionText;
		                break;
		            case 7:
		                fareHoldOptionSubText = holdStringsResponse.FareHoldSevendayOptionText;
		                break;
		        }
		        var updatedString = $scope.addDays(fareHoldOptionSubText, days);
		        return updatedString;
		    };


		    // Mock Maximum Hold Days
		    $scope.maxHoldDays = 4;

		    // Mock Hold Options
		    $rootScope.holdOptions = [];
		    $rootScope.NonMemberholdOptions = [];

		    // GET SITECORE STRINGS

		    $scope.holdStrings = [];
		    var holdStringsResponse = [];

		    // set feature flag to false by default
		    $scope.enableholdreservation = false;

		    haGlobals('HA', function (HA) {
		        if (HA.resources) {
		            holdStringsResponse = HA.resources.holdReservations;
		            $scope.holdStrings.heldDefaultBlockHeader1 = $scope.addDays(holdStringsResponse.daysholdfaretext, 'X');
		            $scope.holdStrings.heldDefaultBlockDescription = holdStringsResponse.holdyourseattext;
		            $scope.holdStrings.heldDefaultBlockBtnTxt = holdStringsResponse.pricelockholdfarebutton;
		            $scope.holdStrings.heldDetailHeader = holdStringsResponse.selectpricelockhead;
		            $scope.holdStrings.termsConditions = holdStringsResponse.pricelocktermsandconditions;
		            $scope.holdStrings.continueBtn = holdStringsResponse.buttoncontinuewithprice;
		            $scope.holdStrings.cancelBtn = holdStringsResponse.buttoncancel;
		            $scope.holdStrings.headerReminders = holdStringsResponse.reminderstext;
		            $scope.holdStrings.headerRemindersDetails = addDate(holdStringsResponse.automatedremindertext, $scope.remindersEndDate);
		            $scope.holdStrings.loginMessage = holdStringsResponse.goldandplatinummemberstext;
		            $scope.holdStrings.signInLink = holdStringsResponse.signintomilestext;
		            $scope.holdStrings.actionInstructions = $scope.addDays(holdStringsResponse.actionInstructions, $rootScope.selectedHoldFare.numberOfDays);
		            $scope.holdStrings.remindersEmail = holdStringsResponse.emailonceperday;
		            $scope.holdStrings.remindersTextMessage = holdStringsResponse.textmessageonceperday;
		            $scope.holdStrings.signInSuccess = holdStringsResponse.mahalosigningin;
		            $scope.holdStrings.priceLockOptionSubText = holdStringsResponse.pricelockoptionsubtext;
		            $scope.holdStrings.priceLockOption = holdStringsResponse.pricelockoption;
		            $scope.holdStrings.perPerson = holdStringsResponse.perpersontext;
					$scope.holdStrings.heldLearnMoreLinkText = holdStringsResponse.holdlearnmorelinktext;
					$scope.holdStrings.FareHoldOptionToolTipText = holdStringsResponse.FareHoldOptionToolTipText;
					$scope.holdStrings.FareholdCCApprovalMsg = holdStringsResponse.FareholdCCApprovalMsg;
		            // set feature flag from sitecore response
		            if (+holdStringsResponse.enableholdreservation === 0) {
		                $scope.enableholdreservation = false;
		            } else {
		                $scope.enableholdreservation = true;
		            }
		        }

		    });			

		    // watch the selected option and rebuild the action instructions string
		    if (holdStringsResponse.actionInstructions) {
		        $scope.$watch('formData.pricelockSelectedOption', function () {
		            setSelectedHoldFare();

		        });
		    }

		    // watch rootscope for checkHoldBookingEligibility = true
		    $rootScope.$watch('checkHoldBookingEligibility', function (runCheck) {
		        if (runCheck) {
		            $scope.checkEligibility(runCheck);
		        }
		    });

		    var holdOffered = false;
		    $scope.checkEligibility = function (runCheck, callback) {
		        if (runCheck && $scope.enableholdreservation) {
		            holdReservationsRQ.FareDetails = [];
		            var postUrl = '/Book/FlightResults/RetrieveFareLockOptions';

					var index = 0;									
		            if ($scope.selectedSegments && $scope.selectedSegments.length > 0) { // for non End-on-End
						angular.forEach($scope.selectedSegments, function (flight) {
							var isFirstClassAvailable = flight.FareDetails.findIndex(o => o.CabinType === 'FIRST') > 0 ? flight.FareDetails.find(o => o.CabinType === 'FIRST').IsAvailable : true;
							holdReservationsRQ.FareDetails.push(new FareDetail(index + 1, flight.UniquerefKey, flight.selectedSeatClass, flight.IsMileagePricing, isFirstClassAvailable));
		                    index = index + 1;
		                });
		            } else if ($scope.TripSummary && $scope.TripSummary.Trips && $scope.TripSummary.Trips.length > 0) { // for End-on-End
						angular.forEach($scope.TripSummary.Trips, function (trip) {
							var isFirstClassAvailable = trip.FareDetails.findIndex(o => o.CabinType === 'FIRST') > 0 ? trip.FareDetails.find(o => o.CabinType === 'FIRST').IsAvailable : true;
							holdReservationsRQ.FareDetails.push(new FareDetail(index + 1, trip.UniqueReferenceKey, trip.selectedSeatClass, false, isFirstClassAvailable)); // MileagePricing flag to come later.
		                    index = index + 1;
		                });
		                postUrl = '/Book/FlightResults/RetrieveFareLockOptionsEndOnEnd';
					}
					$rootScope.mcbHoldSelected = $scope.TripSummary.Trips.filter(function (trip) { return trip.selectedSeatClass === 'MAINCABINBASIC' }).length > 0;
		            $http.post(postUrl, { holdReservationsRQ: holdReservationsRQ }).success(function (data) {
		                $scope.loading = false;
		                $rootScope.isEligible = data.IsEligible;
		                if (data.IsEligible) {
		                    $rootScope.holdOptionsData = data;
		                    if (data.IsLoggedIn) {
		                        $rootScope.holdOptions = [];
		                        $rootScope.NonMemberholdOptions = [];
		                        angular.forEach(data.MemberPriceOptions, function (price) {
		                            $rootScope.holdOptions.push(new PriceOption(price.UniquePriceID, price.TimeLimitDays, price.MSRCode, price.HoldFee, price.HoldMiles, price.DisableHoldOption));
		                        });

		                        angular.forEach(data.NonMemberPriceOptions, function (price) {
		                            $rootScope.NonMemberholdOptions.push(new PriceOption(price.UniquePriceID, price.TimeLimitDays, price.MSRCode, price.HoldFee, price.HoldMiles, price.DisableHoldOption));
		                        });
		                    }
		                    else {
		                        $rootScope.holdOptions = [];
		                        $rootScope.NonMemberholdOptions = [];
		                        angular.forEach(data.NonMemberPriceOptions, function (price) {
		                            $rootScope.holdOptions.push(new PriceOption(price.UniquePriceID, price.TimeLimitDays, price.MSRCode, price.HoldFee, price.HoldMiles, price.DisableHoldOption));
		                        });
		                    }

		                    window.digitalDataLoaded.then(function () {
		                        if (!holdOffered) {
		                            holdOffered = true;
		                            // Custom Event for Analytics
		                            document.body.dispatchEvent(new CustomEvent('FareHoldOffered'));
		                        }
		                    });
		                }
		                if (typeof callback === 'function') {
		                    callback($rootScope.isEligible);
		                }
		            }).error(function () {
		                if (typeof callback === 'function') {
		                    callback($rootScope.isEligible);
		                }
		            });
		            $rootScope.checkHoldBookingEligibility = false;
		        } else {
		            $rootScope.isEligible = false;
		            if (typeof callback === 'function') {
		                callback($rootScope.isEligible);
		            }
		        }
			};
		    // localization replacement for date
		    function addDate(string, isoDate) {
		        var updatedString = string.replace('{{date}}', $filter('date')(isoDate, 'shortDate'));
		        return updatedString;
		    }

		    function setSelectedHoldFare() {
		        angular.forEach($rootScope.holdOptions, function (option) {
		            if (option.id === $scope.formData.pricelockSelectedOption) {
		                $rootScope.selectedHoldFare = option;
		                if (+option.numberOfDays === 1) {
		                    $scope.oneDayHoldOptionEmail = true;
		                } else {
		                    $scope.oneDayHoldOptionEmail = false;
		                }
		            }

		        });
		        $scope.holdStrings.actionInstructions = $scope.addDays(holdStringsResponse.actionInstructions, $rootScope.selectedHoldFare.numberOfDays);
		    }
			$scope.holdFare = function () {
		        setSelectedHoldFare();
				$scope.holdDetailsState = true;
				$scope.holdDefaultOption = _.findIndex($scope.holdOptions,{disableHoldOption: false});
				$rootScope.selectedHoldFare = $rootScope.holdOptions[$scope.holdDefaultOption];
			};

		    $scope.cancelHold = function () {
		        $scope.holdDetailsState = false;
		    };

		    $scope.showLoginForm = function () {
		        window.location = '/my-account/login?ReturnUrl=' + encodeURIComponent(window.location.pathname);
		    };

		    $scope.continuePriceLock = function () {
		        //console.log($scope.formData);
		    };

		    $scope.showTermsModal = function () {
		        haModal('', {
		            id: 'sign-in-modal',
		            backdrop: 'true',
		            template: angular.element('.terms-modal-content')
		        });
		    };

		}
    ]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haPremierClubModule', []);


	/* 	utility method:
	 takes "/Date(1420009200000)/"
	 returns date object
	 */
	function dateStringToDate(datestring) {
		//$log.debug('parse date string: '+datestring);
		var time = datestring.match(/[\d]+/);
		time = Number(time[0]);
		return new Date(time);
	}

	module.controller('PremierClubJoinController', [
		'$scope', 'haGlobals', 'haModal', 'haHttpService', '$filter', 'localCurrencyFilter', 'localShortDateFilter', '$window', '$log', 'haSitecoreStrings',
		function ($scope, haGlobals, haModal, haHttpService, $filter, localCurrencyFilter, localShortDateFilter, $window, $log, $scs) {

			$scope.Error = '';
			// for gifting
			$scope.lookingUp = false;
			$scope.recipientVM = {};
			$scope.recipientCandidate = {};
			$scope.eligibleRecipientExists = false;
			$scope.recipientEligibleHeader = '';
			$scope.recipientEligibleSubHeader = '';


			haGlobals('errorMessage', function (errorMessage) {
				if (errorMessage !== '') {
					$scope.Error = errorMessage;
				}
			});


			$scope.setForm = function (form) {
				$scope.pcForm = form;
			};

			// used by ng-submit for PremierClubForm
			// so that either the recipient eligibility is checked or the form is posted
			$scope.customSubmit = function (e) {
				e.preventDefault();
				if ($scope.pcForm.$valid) {
					if ($scope.showCheckEligibilityForm()) {
						$scope.lookup();
					} else {
						PremierClubForm.submit();/* how does this work? form not in scope. */ // jshint ignore:line
					}
				} else {
					$scope.Error = $scs('PREMIER_CLUB_DATA.pleasecorrecterrorstext');
				}
			};

			/* 	utility method:
			 changes "{0} membership will start on {1} and expire on {2}."
			 to "Jamie's membership will start on 11/27/14 and expire on 11/27/15"
			 */
			$scope.personalizeString = function (string, replacementArray) {
				if (string) {
					return string.replace(/({\d})/g, function (j) {
						if (j) {
							return replacementArray[j.replace(/{/, '').replace(/}/, '')];
						}
					});
				}
			};
			// for purchase option labels
			$scope.createEnrollString = function (product, who) {
				if (product) {
					if (parseInt(product.EnrollmentType) === 2) {
						// renew
						return $scs('PREMIER_CLUB_DATA.renewtext', [who, product.OfferTerm]);
					}
					else {
						// enroll
						return $scs('PREMIER_CLUB_DATA.enrolltext', [who, product.OfferTerm]);
					}
				}
			};
			// determine enrollment type - purchase or renew
			$scope.determineEnrollmentType = function (offers) {
				// defaults
				$scope.enrollmentType = 'gift only';
				// loop over offers
				$.each(offers, function (i, val) {
					if (!val.IsGiftable) {
						if (parseInt(val.EnrollmentType) === 2) {
							$scope.enrollmentType = 'renew';
						}
						else {
							$scope.enrollmentType = 'purchase';
						}
					}
				});
			};

			// get view model
			haGlobals('premierClubJson', function (premierClubJson) {
				// $log.debug('///////// premierClubJson');
				// $log.debug(premierClubJson);
				$scope.premierVM = premierClubJson;
				if (premierClubJson.ErrorMessage !== '') {
					$scope.Error = premierClubJson.ErrorMessage;
				}
				$scope.premierVM.currentProduct = 'gift';
				if (premierClubJson.PremierClubProductsList.length) {
					$scope.premierVM.currentProduct = premierClubJson.PremierClubProductsList[0].ProductID;
				}
				$scope.currencyCode = $scope.premierVM.currencyCode || 'USD';

				// enroll options header
				var enrollOptionsHeader;
				var enrollOptionsSubheader;
				var expiresDateString;
				if ($scope.premierVM.ExistingMemberExpirationDate) {
					expiresDateString = dateStringToDate($scope.premierVM.ExistingMemberExpirationDate);
					expiresDateString = $filter('localShortDate')(expiresDateString, $language);
				}

				$scope.determineEnrollmentType($scope.premierVM.PremierClubProductsList);

				if ($scope.enrollmentType === 'purchase') {
					enrollOptionsHeader = $scs('PREMIER_CLUB_DATA.enrolltodaytext');
					enrollOptionsSubheader = '';
				}
				else if ($scope.enrollmentType === 'renew') {
					enrollOptionsHeader = $scs('PREMIER_CLUB_DATA.renewtodaytext');
					enrollOptionsSubheader = $scs('PREMIER_CLUB_DATA.giftofmembershipsubtext', [expiresDateString]);
				}
				else if ($scope.enrollmentType === 'gift only') {
					enrollOptionsHeader = $scs('PREMIER_CLUB_DATA.giftofmembershiptext');
					if ($scope.premierVM.IsAlreadyEnrolled) {
						enrollOptionsSubheader = $scs('PREMIER_CLUB_DATA.giftofmembershipsubtext', [expiresDateString]);
					}
					else if (!$scope.premierVM.IsUserEligible || $scope.premierVM.IsGift) {
						enrollOptionsSubheader = $scs('PREMIER_CLUB_DATA.youarenoteligibletext');
					}
					else {
						enrollOptionsSubheader = '';
					}
				}

				$scope.enrolloptionsheader = enrollOptionsHeader;
				$scope.enrolloptionssubheader = enrollOptionsSubheader;

				// check if editing a gift order
				if ($scope.premierVM.IsGift) {
					$scope.recipientVM = angular.copy($scope.premierVM);
					$scope.eligibleRecipientExists = true;
				}
			});


			$scope.getHelpContent = function () {
				haModal('', {
					id: 'premier-club-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.lookup = function () {

				$scope.lookingUp = true;
				$scope.premierVM.currentProduct = 'gift';
				//$log.debug('lookingUp: '+$scope.lookingUp);

				var recipientEligibleEndpoint = '/MyAccount/PremierClub/GetRecipientEligibleProducts';

				//$log.debug('	last name: '+$scope.recipientCandidate.LastName);
				//$log.debug('	HM number: '+$scope.recipientCandidate.HMAccountNumber);

				haHttpService.POST(recipientEligibleEndpoint, {
					recipientHMNumber: $scope.recipientCandidate.HMAccountNumber,
					recipientLastName: $scope.recipientCandidate.LastName
				}).success(function (response) {
					// $log.debug('///////////////// recipient eligible response');
					// $log.debug(response);
					$scope.lookingUp = false;
					$('#lookup').blur();
					// eligible?
					if (!response.IsUserEligible) {
						$scope.Error = $scs('PREMIER_CLUB_DATA.hmaccountnotfoundineligibletext');
						$scope.eligibleRecipientExists = false;
					} else {
						$scope.recipientVM = response;
						$scope.eligibleRecipientExists = true;
						//Bug fix for 104804
						$scope.Error = false;
						// recipient is eligible header and subheader
						var recipFullName = $scope.recipientVM.EnrolleeDetails.FirstName + ' ' + $scope.recipientVM.EnrolleeDetails.LastName;
						$scope.recipientEligibleHeader = $scs('PREMIER_CLUB_DATA.iseligibleforpremiumclubtext', [recipFullName]);
						// other text
						$scope.emailWillBeSentString = $scs('PREMIER_CLUB_DATA.emailconfirmingpurchasetext', [$scope.recipientVM.EnrolleeDetails.FirstName]);

					}
				}).error(function (data, status) {
					$scope.lookingUp = false;
					$scope.eligibleRecipientExists = false;
					$('#lookup').blur();
					if (parseInt(status) === 403) {
						$scope.Error = $scs('PREMIER_CLUB_DATA.yoursessionhasexpiredtext');
					}
				});
			};


			$scope.makeRecipSubHeader = function () {
				var currentProductIndex = 0;
				$.each($scope.recipientVM.PremierClubProductsList, function (i, val) {
					if (val.ProductID === $scope.recipientVM.currentProduct) {
						currentProductIndex = i;
					}
				});
				//$log.debug('make recip sub header with product index: '+currentProductIndex);
				var date1 = dateStringToDate($scope.recipientVM.RecipientPremierClubProductsList[currentProductIndex].OfferStartDate);
				var date2 = dateStringToDate($scope.recipientVM.RecipientPremierClubProductsList[currentProductIndex].OfferEndDate);
				var recipSubHdrStrings = [
					$scope.recipientVM.EnrolleeDetails.FirstName + '\'s',
					$filter('localShortDate')(date1, $language),
					$filter('localShortDate')(date2, $language)
				];
				$scope.recipientEligibleSubHeader = $scs('PREMIER_CLUB_DATA.membershipexpiresbetweentext', recipSubHdrStrings);
			};

			$scope.notOkToContinue = function () {
				// someone else
				if (!$scope.premierVM.IsUserEligible || $scope.premierVM.currentProduct === 'gift') {
					if ($scope.eligibleRecipientExists) {
						return false;
					}
					else {
						return true;
					}
				}

				return $scope.premierVM.currentProduct && $scope.premierVM.currentProduct === 'gift';
			};

			$scope.showCheckEligibilityForm = function () {
				if (!$scope.premierVM.IsUserEligible || $scope.premierVM.currentProduct === 'gift') {
					if ($scope.eligibleRecipientExists) {
						return false;
					}
					else {
						return true;
					}
				} else {
					return false;
				}
			};

			$scope.goBack = function () {
				$scope.eligibleRecipientExists = false;
				$scope.recipientVM = {};
			};

			/*
			 SELECT SAVED TRAVELERS
			 picked up from ha-purchase-miles.js
			 */

			var travelersEndPoint = '/myaccount/travelerslist/gettravelerlist';

			if ($scope.premierVM.ShowSavedTravelers) {
				haHttpService.GET(travelersEndPoint).then(
					function (response) {
						// $log.debug('/////// got shared travelers');
						if (response.data.TravelersList) {
							$scope.FilteredTravelersList = response.data.TravelersList;
						}
					},
					function (err) {
						$log.debug(err);
						$scope.premierVM.ShowSavedTravelers = false;
					}
				);
			}
			$scope.openSharedTravelers = function () {
				haModal({
					id: 'sharedTravelersModal',
					backdrop: 'true',
					scope: $scope,
					template: angular.element('#sharedTravelers')
				});
			};
			$scope.travelerChosen = function (traveler) {
				$scope.$modalCancel();
				// $log.debug('//// traveler chosen');
				// $log.debug(traveler);
				$scope.recipientCandidate.LastName = traveler.LastName;
				$scope.recipientCandidate.HMAccountNumber = traveler.HMAccountNo || '';
			};

			$scope.$watch('premierVM.currentProduct', function (newVal, oldVal) {
				if ((oldVal === undefined && !newVal)) {
					return;
				}
				if (newVal !== oldVal) {
					$scope.Error = '';
				}
			});
		}
	]);

	module.controller('PremierClubPaymentController', [
		'$scope', '$rootScope', 'haGlobals', '$window', 'haModal', 'haSitecoreStrings', '$filter', '$timeout',
		function ($scope, $rootScope, haGlobals, $window, haModal, $scs, $filter, $timeout) {
			$scope.premierClubPayment = {};
			$scope.Error = '';
			$scope.submitting = false;

			/* 	utility method:
			 changes "{0} membership will start on {1} and expire on {2}."
			 to "Jamie's membership will start on 11/27/14 and expire on 11/27/15"
			 */
			$scope.personalizeString = function (string, replacementArray) {
				if (string) {
					return string.replace(/({\d})/g, function (j) {
						if (j) {
							return replacementArray[j.replace(/{/, '').replace(/}/, '')];
						}
					});
				} else {
					console.warn('MISSING STRING');
				}
			};
			// dynamic offer term text
			$scope.membershipTermText = $scs('PREMIER_CLUB_DATA.monthmembershiptext', [$window.premierClubJson.ProductDetails.OfferTerm]);

			haGlobals('premierClubJson', function (premierClubJson) {
				// $log.debug('///////// premierClubJson');
				// $log.debug(premierClubJson);
				$scope.premierVM = premierClubJson;
				// mock available special offers
				//$scope.premierVM.ProductDetails.SpecialOfferPrice = 149;
				$scope.currentProduct = !!premierClubJson.ProductDetails ? premierClubJson.ProductDetails : {};
				$scope.canPayWithMiles = $scope.premierVM.PurchaserDetail.MilesAvailable >= $scope.premierVM.ProductDetails.MilesCost;

				// create offer term text string
				var startDate = $scope.premierVM.ProductDetails.OfferStartDate;
				var endDate = $scope.premierVM.ProductDetails.OfferEndDate;
				var offerStartDate = $filter('localShortDate')(startDate, $language);
				var offerEndDate = $filter('localShortDate')(endDate, $language);
				$scope.offerTermText = $scs('PREMIER_CLUB_DATA.notetitletext') + ' ' + $scs('PREMIER_CLUB_DATA.notedescriptiontext') + ' ' + offerStartDate + ' ' + $scs('PREMIER_CLUB_DATA.endontext') + ' ' + offerEndDate;

			});

			haGlobals('errorMessage', function (errorMessage) {
				if (errorMessage !== '') {
					$scope.Error = errorMessage;
				}
			});

			$scope.getHelpContent = function () {
				haModal('', {
					id: 'premier-club-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.showTerms = function () {
				haModal('', {
					id: 'terms-and-conditions',
					backdrop: 'true',
					template: angular.element('.showTerms')
				});
			};

			// for PCI Payment Authorization
			$scope.haPaymentTypes = {
				paymentMethod: 'creditDebit'
			};

			// this is required to remove the unwanted input fields in _PaymentCDE.cshtml
			$scope.$watch('premierClubPayment.paymentType', function(nv) {
				if (nv === 'card') {
					$scope.haPaymentTypes.paymentMethod = 'creditDebit';
				} else {
					$scope.haPaymentTypes.paymentMethod = 'miles';
				}
			});

			$scope.CDESubmit = function () {
				$rootScope.$broadcast('CDESubmit');
			};

			$scope.$on('CDEPaymentSubmitted', function() {
				$scope.submitting = true;
			});

			$scope.$on('CDEPaymentSubmitError', function() {
				$scope.submitting = false;
				$timeout(function() {
					$('html, body').animate({ scrollTop: $('#formIFrame').offset().top - 50 }, 'fast');
				}, 100);
			});
			// end PCI
		}
	]);

	module.controller('PremierClubConfirmationController', [
		'$scope', 'haGlobals', '$window', 'haModal', '$filter',
		function ($scope, haGlobals, $window, haModal, $filter) {

			$scope.Error = '';

			haGlobals('premierClubJson', function (premierClubJson) {
				// $log.debug('///////// premierClubJson');
				// $log.debug(premierClubJson);
				$scope.premierVM = premierClubJson;
				var newBalanceText = 'Balance' + ': ' + $filter('number')(premierClubJson.CurrentMilesBalance);
				angular.element('#account-nav-list li:first-child').find('span.popover-link-secondary').text(newBalanceText);

				// create offer term text string
				var startDate = $scope.premierVM.OrderDetails.OfferStartDate;
				var endDate = $scope.premierVM.OrderDetails.OfferEndDate;
				var offerStartDate = $filter('localShortDate')(startDate, $language);
				var offerEndDate = $filter('localShortDate')(endDate, $language);
				$scope.offerTermText = $scs('PREMIER_CLUB_DATA.notetitletext') + ' ' + $scs('PREMIER_CLUB_DATA.notedescriptiontext') + ' ' + offerStartDate + ' ' + $scs('PREMIER_CLUB_DATA.endontext') + ' ' + offerEndDate;
			});

			haGlobals('errorMessage', function (errorMessage) {
				if (errorMessage !== '') {
					$scope.Error = errorMessage;
				}
			});

			$scope.getHelpContent = function () {
				haModal('', {
					id: 'premier-club-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.showTerms = function () {
				haModal('', {
					id: 'terms-and-conditions',
					backdrop: 'true',
					template: angular.element('.showTerms')
				});
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haRequestPastMilesModule', []);

	module.controller('requestPastMilesController', [
		'$scope', 'haGlobals', 'haModal', '$window', 'haDateUtils', '$document', '$timeout', 'haSitecoreStrings',
		function ($scope, haGlobals, haModal, $window, haDateUtils, $document, $timeout, $scs) {

			$scope.requestStatuses = {
				Pending: '<i class="ha-icon fontIcon-minus-circle"></i> ' + $scs('REQUEST_PAST_MILES_DATA.pendingtext'),
				Fulfilled: '<i class="ha-icon fontIcon-check-circle"></i> ' + $scs('REQUEST_PAST_MILES_DATA.approvedtext'),
				Rejected: '<i class="ha-icon fontIcon-times-circle"></i> ' + $scs('REQUEST_PAST_MILES_DATA.rejectedtext')
			};

			$scope.noConfirmationCode = false;
			$scope.submitAttempted = false;
			$scope.languageCode = $window.languageCode;

			$scope.model = {};
			//$scope.departDate = '';

			haGlobals('requestMilesJson', function (requestMilesJson) {
				// $log.debug('///////// requestMilesJson');
				// $log.debug(requestMilesJson);
				$scope.requestMilesVM = requestMilesJson;

				$scope.requestMilesVM.ClassOfServiceDropDown = ['O', 'U', 'S', 'Y', 'E', 'K', 'H', 'Q', 'L', 'V', 'R', 'M'];
				$scope.requestMilesVM.Airlines = 'HA';

				$scope.currentRequest = $scope.requestMilesVM.RecentRequestsList.RecentRequestsList[0];
				$scope.HAformSubmitAttempt = false;
				$scope.JBformSubmitAttempt = false;
				$scope.JALformSubmitAttempt = false;
			});

			/* 	utility method:
			 takes "/Date(1420009200000)/"
			 returns date object
			 */
			$scope.dateStringToDate = function (datestring) {
				//$log.debug('parse date string: '+datestring);
				var time = datestring.match(/[\d]+/);
				time = Number(time[0]);
				return new Date(time);
			};

			// show recent request status
			$scope.showRequest = function (requestIndex) {

				if ($scope.requestMilesVM.RecentRequestsList.RecentRequestsList[requestIndex].RequestStatus !== 'Pending') {
					// $log.debug('Show request status for request '+requestIndex);
					$scope.currentRequest = $scope.requestMilesVM.RecentRequestsList.RecentRequestsList[requestIndex];
					haModal({
						id: 'requestStatus',
						backdrop: 'true',
						templateUrl: '/requestStatus.html',
						scope: $scope
					});
				}
			};
			$scope.switchConfirmCode = function () {
				$scope.noConfirmationCode = !$scope.noConfirmationCode;
			};

			$scope.getHelpContent = function () {
				haModal({
					id: 'requestMilesHelp',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.onSubmit = function (e, form) {
		        e.preventDefault();
			    // $log.debug('custom submit');
			    // $log.debug(form);
		        $scope.submitting = false;
		        if (form.$valid) {
		            $scope.submitting = true;
		            if ($scope.requestMilesVM.Airlines === 'HA') {
		                // $log.debug('submit HA');
		                //$log.debug('IsHawaiianAirlines: '+HawaiianAirlinesForm.IsHawaiianAirlines.value);
		                $scope.HAformSubmitAttempt = true;
		                HawaiianAirlinesForm.submit();/* how does this work? form not in scope. */ // jshint ignore:line
		            } else if ($scope.requestMilesVM.Airlines === 'B6') {
		                // $log.debug('submit JB');
		                //$log.debug('IsHawaiianAirlines: '+JetBlueForm.IsHawaiianAirlines.value);
		                $scope.JBformSubmitAttempt = true;
		                JetBlueForm.submit();/* how does this work? form not in scope. */ // jshint ignore:line
		                }
		                else {
		                $scope.JALformSubmitAttempt = true;
		                JalForm.submit();
		                }
		        } else {
		            // $log.debug('ERRORS');
		            // $log.debug(form.$error);
		            $scope.submitting = false;
		            $scope.Error = $scs('PREMIER_CLUB_DATA.pleasecorrecterrorstext');
		            if($scope.requestMilesVM.Airlines === 'HA') {
		                $scope.HAformSubmitAttempt = true;
		            } else if ($scope.requestMilesVM.Airlines === 'B6') {
		                $scope.JBformSubmitAttempt = true;
		            }
		            else {
		                $scope.JALformSubmitAttempt = true;
		            }
		        }
			};
            //to trim the value from input on out focus
			$scope.trim = function (e) {
			    e.target.value = e.target.value.replace(/^\s+|\s+$/g, '');
			};
		}

	]);

	module.controller('requestMilesConfirmationController', [
		'$scope', 'haGlobals', 'haModal', '$window', function ($scope, haGlobals, haModal, $window) {

			haGlobals('requestMilesJson', function (requestMilesJson) {
				// $log.debug('///////// requestMilesJson');
				// $log.debug(requestMilesJson);
				$scope.requestMilesVM = requestMilesJson;
				$scope.languageCode = $window.languageCode;
			});

			$scope.openFAQModal = function () {
				haModal({
					id: 'requestMilesFAQ',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.openTermsModal = function () {
				haModal({
					id: 'requestMilesTerms',
					backdrop: 'true',
					template: angular.element('.showTerms')
				});
			};

			$scope.getHelpContent = function () {
				haModal({
					id: 'requestMilesHelp',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('formValidation', []);

	module.directive('showErrors', function ($timeout, showErrorsConfig) {
		var getShowSuccess;
		var linkFn;
		getShowSuccess = function (options) {
			var showSuccess;
			showSuccess = showErrorsConfig.showSuccess;
			if (options && options.showSuccess != null) {
				showSuccess = options.showSuccess;
			}
			return showSuccess;
		};
		linkFn = function (scope, el, attrs, formCtrl) {
			var blurred = false;
			var options = scope.$eval(attrs.showErrors);
			var showSuccess = getShowSuccess(options);
			var inputEl = el[0].querySelector('[name]');
			var inputNgEl = angular.element(inputEl);
			var inputName = inputNgEl.attr('name');
			if (!inputName) {
				throw 'show-errors element has no child input elements with a \'name\' attribute';
			}
			inputNgEl.bind('blur', function () {
				blurred = true;
				return toggleClasses(formCtrl[inputName].$invalid);
			});
			scope.$watch(function () {
				return formCtrl[inputName] && formCtrl[inputName].$invalid;
			}, function (invalid) {
				if (!blurred) {
					return;
				}
				return toggleClasses(invalid);
			});
			scope.$on('show-errors-check-validity', function () {
				return toggleClasses(formCtrl[inputName].$invalid);
			});

			var toggleClasses = function (invalid) {
				el.toggleClass('has-error', invalid);
				if (showSuccess) {
					return el.toggleClass('has-success', !invalid);
				}
			};

			return toggleClasses;
		};
		return {
			restrict: 'A',
			require: '^form',
			compile: function (elem) {
				if (!elem.hasClass('form-group')) {
					throw 'show-errors element does not have the \'form-group\' class';
				}
				return linkFn;
			}
		};
	});

	module.provider('showErrorsConfig', function () {
		var show = false;
		this.showSuccess = function (showSuccess) {
			show = showSuccess;
			return show;
		};
		this.$get = function () {
			return {showSuccess: show};
		};
	});

	module.controller('formController', function ($scope) {
		$scope.save = function () {
			$scope.$broadcast('show-errors-check-validity');
		};

		$scope.saveHA = function () {
			$scope.$broadcast('show-errors-check-validity');
		};
	});

})(angular);



;
(function (ng) {

	// Member Discounts Controller
	// --------------------------------------------
	//
	// * **Class:** haMemberDiscountsCtrl
	// * **Author:** Cory Shaw
	//

	'use strict';

	var module;
	module = ng.module('haMemberDiscountsModule', []);
	var etcoHref = '/book/home';

	module.controller('haMemberDiscountsCtrl', [
		'$log',
		'$scope',
		'haModal',
		'haConfig',
		'haGlobals',
		function ($log, $scope, haModal, haConfig, haGlobals) {
			haGlobals(['enableTCR'], function (enableTCR) {
				$scope.enableTCR = enableTCR;
			});
			$scope.applyDiscount = function (href) {

				if ($scope.enableTCR) {
					haModal(haConfig.getTemplateUrl('ecert-or-etco.html'), {
						backdrop: true,
						extendScope: {discountHref: href, etcoHref: etcoHref}
					});
				} else {
					window.location.href = href;
				}
			};
		}
	]);

})(angular);
;
(function (angular) {

	// Flight Schedule Controller
	// --------------------------------------------
	//
	// * **Class:** haFlightScheduleCtrl
	//
	// Controller for flight schedule.

	'use strict';

	angular.module('haFlightScheduleModule', [])
	.controller('haFlightScheduleCtrl', [
		'$scope', 'haHttpService', 'haGlobals', 'haModal', 'haHttpService', '$filter', '$timeout', 'haDateUtils', 'haUtils', 'haConfig', 'haCitiesSvc', 'haSitecoreStrings',
		function ($scope, http, haGlobals, haModal, haHttpService, $filter, $timeout, haDateUtils, haUtils, haConfig, haCitiesSvc, $scs) {

			var endpoints = {
				getFlights: '/flightschedule/getflightsschedules',
				getTinyUrl: '/flightschedule/gettinyurl'
			};
			var openModal = function (modalLock) {
				$scope.loading = false;
				$scope.modalData = {
					leg: angular.copy($scope.leg),
					dateType: angular.copy($scope.dateType),
					departureDay: angular.copy($scope.departureDay),
					monthChoice: angular.copy($scope.monthChoice)
				};
				haModal(haConfig.getTemplateUrl('ha-flightschedule-modal.html'), {
					id: 'flightScheduleModal',
					backdrop: 'true',
					scope: $scope,
					'modalLock': !!modalLock
				});
			};
			var buildMonths = function () {

				var now = new Date();
				var monthIdx = now.getMonth();
				var newDate;
				var dates = [];

				for (var i = 0; i < 11; i++) {
					newDate = (monthIdx < 12) ? new Date(now.getFullYear(), monthIdx, (monthIdx === now.getMonth() ? now.getDate() : 1)) : new Date(now.getFullYear() + 1, monthIdx - 12, 1);
					dates.push({
						name: $filter('date')(newDate, 'MMMM yyyy'),
						date: newDate
					});
					monthIdx++;
				}

				return dates;
			};
			var timeout;
			var tinyUrls = {};


			$scope.FlightScheduleOptions = [];
			$scope.dateTypes = [{ type: 'd', name: $scs('FlightScheduleSearch["one day text"]') }, {
				type: 'm',
				name: $scs('FlightScheduleSearch["one month text"]')
			}];
			$scope.dateType = $scope.dateTypes[0];
			$scope.months = buildMonths();
			$scope.monthChoice = $scope.months[0];
			$scope.leg = {};
			$scope.popover = false;
			$scope.currentSort = 'default'; // 'stops', 'depart', 'arrive', 'duration'
			$scope.flightFilter = 'all';
			$scope.error = false;
			$scope.monthDataDaySearch = false;

			$scope.editSearch = function () {
				openModal();
			};

			haGlobals('FlightScheduleOptions', function (flightScheduleOptions) {
				if (!flightScheduleOptions.length) {
					openModal(true);
				} else {
					var flightQueryCookie = haUtils.getFlightQueryModelCookie();

					if (flightQueryCookie !== null) {
						var segment = flightQueryCookie.FlightSearchSegmentList[0];

						if (flightQueryCookie && segment && segment.OriginCityCode && segment.DestinationCityCode && segment.DepartureDate) {
							haCitiesSvc.getCityByCode(segment.OriginCityCode).then(function (airport) {
								$scope.leg.origin = airport;
							});
							haCitiesSvc.getCityByCode(segment.DestinationCityCode).then(function (airport) {
								$scope.leg.destination = airport;
							});

							var dateString = segment.DepartureDate.substr(0, 10);
							var parts = dateString.split('-');

							$scope.departureDay = new Date(parts[0], parts[1] - 1, parts[2]);
						}
					}

					$scope.FlightScheduleOptions = flightScheduleOptions;
					$scope.monthDataDaySearch = hasMonthData(flightScheduleOptions);
				}
			});

			function hasMonthData(optionsList) {
				if ($scope.dateType.type === 'd' && optionsList.length) {
					var strDate = (($scope.departureDay).getMonth() + 1) + '/' + ($scope.departureDay).getDate() + '/' + ($scope.departureDay).getFullYear();
					var arrDates;
					for (var i = 0; i < optionsList.length; i++) {
						arrDates = optionsList[i].ValidDates;
						if (jQuery.inArray(strDate, arrDates) !== -1) {
							return false;
						}
					}
				}
				return true;
			}

			$scope.submitSearch = function () {

				var payload = {
					OriginCity: $scope.modalData.leg.origin.Code,
					DestinationCity: $scope.modalData.leg.destination.Code,
					FlightScheduleType: ($scope.modalData.dateType.type === 'd') ? 0 : 1,
					FlightScheduleStartDate: $filter('date')(($scope.modalData.dateType.type === 'd') ? $scope.modalData.departureDay : $scope.modalData.monthChoice.date, 'yyyy-MM-dd')
				};

				$scope.loading = true;
				haHttpService.POST(endpoints.getFlights, JSON.stringify(payload)).then(
					function (response) {
						if (response.data && response.data.Success && response.data.FlightScheduleOptions) {
							$scope.FlightScheduleOptions = response.data.FlightScheduleOptions;
							$scope.$modalCancel();
							angular.extend($scope, $scope.modalData); // Display selections from modal controls on success.
							$scope.error = false;
							$scope.monthDataDaySearch = ($scope.dateType.type === 'd') ? hasMonthData($scope.FlightScheduleOptions) : false;
						} else {
							$scope.error = true;
							$scope.loading = false;
							$scope.monthDataDaySearch = false;
							$scope.FlightScheduleOptions = [];
						}
					},
					function () {
						$scope.error = true;
						$scope.loading = false;
						$scope.monthDataDaySearch = false;
						$scope.FlightScheduleOptions = [];
					}
				);
			};

			$scope.flightSearch = function () {
				// searchDetails=o=LAX;d=HNL;dd=12/26/2014;a=1;c=0;rd=12/29/2014;tt=2

				var dateChoice = $filter('date')(($scope.dateType.type === 'd') ? $scope.departureDay : $scope.monthChoice.date, 'yyyy-MM-dd');
				var dateString = $filter('date')(($scope.dateType.type === 'd') ? $scope.departureDay : $scope.monthChoice.date, 'MM/dd/yyyy');
				var returnDate = $filter('date')(new Date(dateChoice).dateAdd('day', 7), 'MM/dd/yyyy');
				var searchString = ['searchDetails=o=', $scope.leg.origin.Code, ';d=', $scope.leg.destination.Code, ';dd=' + dateString, ';a=1;c=0;rd=', returnDate, ';tt=2'].join('');

				location.href = '/book/home?' + searchString;
			};

			$scope.flightsCount = function () {
				return $filter('filter')($scope.FlightScheduleOptions, $scope.filterResults).length;
			};

			$scope.showPopover = function (mouseOver) {

				$timeout.cancel(timeout);

				if (!mouseOver && $scope.popover) {
					$scope.popover = false;
					return;
				}
				if (!$scope.popover) {
					$scope.urlLoading = true;
				}
				$scope.popover = true;

				$timeout(function () {
					$('body').on('click.share', function (e) {
						if (!$(e.target).closest('.share-popover').length) {
							$scope.hidePopoverSlow();
						}
					});
				}, 10);


				// ?o=LAX&d=HNL&dt=0&date=2014-12-31
				var baseUrl = [location.protocol, '//', location.host, location.pathname].join('');
				var urlString = ['?o=', $scope.leg.origin.Code, '&d=', $scope.leg.destination.Code, '&dt=', ($scope.dateType.type === 'd') ? 0 : 1, '&date=', $filter('date')(($scope.dateType.type === 'd') ? $scope.departureDay : $scope.monthChoice.date, 'yyyy-MM-dd')].join('');
				var payload = {
					LongUrl: baseUrl + urlString
				};

				if (tinyUrls[urlString]) {
					$scope.url = tinyUrls[urlString];
					$scope.urlLoading = false;
					return;
				}

				haHttpService.POST(endpoints.getTinyUrl, JSON.stringify(payload)).then(
					function (response) {
						if (response.data && response.data.Success && response.data.TinyUrl) {
							$scope.url = response.data.TinyUrl.TinyUrl;
							$scope.urlLoading = false;
							tinyUrls[urlString] = response.data.TinyUrl.TinyUrl;
						} else {
							// THROW THE ERROR STATE
							$scope.urlLoading = false;
						}
					},
					function () {
						// THROW THE ERROR STATE
						$scope.urlLoading = false;
					}
				);
			};

			$scope.hidePopoverSlow = function () {
				timeout = $timeout(function () {
					$scope.popover = false;
					$('body').off('click.share');
				}, 1000);
			};

			$scope.getFlightLegs = function (flight) {
				var legs = [];
				for (var i = 0; i < flight.FlightSegments.length; i++) {
					legs = legs.concat(flight.FlightSegments[i].Legs);
				}
				return legs;
			};

			$scope.getOriginDestination = function () {
				return ($scope.leg.origin && $scope.leg.destination) ? [$scope.leg.origin.DisplayName, $scs('FlightScheduleSearch.to'), $scope.leg.destination.DisplayName].join(' ') : ' ';
			};

			$scope.switchOriginDestination = function () {
				if ($scope.modalData.leg.origin && $scope.modalData.leg.destination) {
					var tempOrigin = $scope.modalData.leg.origin;
					$scope.modalData.leg.origin = $scope.modalData.leg.destination;
					$scope.modalData.leg.destination = null;
					$scope.modalData.leg.destination = tempOrigin;
				}
			};

			$scope.getDisplayDate = function () {
				return ($scope.dateType.type === 'd') ? $filter('date')($scope.departureDay, 'longDate') : $filter('date')($scope.monthChoice.date, 'MMMM yyyy');
			};

			$scope.getDate = function (flight, type) {

				// First leg of first segment or last leg of last segment
				var segment = (type === 'o') ? flight.FlightSegments[0] : flight.FlightSegments[flight.FlightSegments.length - 1];
				var leg = (type === 'o') ? segment.Legs[0] : segment.Legs[segment.Legs.length - 1];

				return moment((type === 'o') ? leg.DepartureDateTime : leg.ArrivalDateTime).format('YYYY-MM-DDTHH:mm:ssZZ');
			};

			$scope.isNextDay = function (depart, arrive) {
				var departDt = haDateUtils.msToDate(depart);
				var arriveDt = haDateUtils.msToDate(arrive);

				return !haDateUtils.isSameDay(departDt, arriveDt);
			};

			$scope.isTripOvernight = function (flight) {
				var firstLeg = flight.FlightSegments[0].Legs[0].DepartureDateTime;
				var arrivalLeg = flight.FlightSegments[flight.FlightSegments.length - 1].Legs[flight.FlightSegments[flight.FlightSegments.length - 1].Legs.length - 1].ArrivalDateTime;

				return $scope.isNextDay(firstLeg, arrivalLeg);
			};

			$scope.numDaysDifference = function (date1, date2) {
				var departDt = haDateUtils.msToDate(date1);
				var arriveDt = haDateUtils.msToDate(date2);

				return haDateUtils.numDaysDifference(departDt, arriveDt);
			};

			$scope.getDurationDelta = function (date1, date2) {
				return moment(date2).diff(date1, 'minutes');
			};

			$scope.getDuration = function (minutes) {
				var hours = Math.floor(minutes / 60);
				var min = minutes % 60;

				return [hours, $scs('FlightScheduleResults.hr'), ' ', min, $scs('FlightScheduleResults.min')].join('');
			};

			$scope.getLayover = function (flight, idx, last) {
				var legs = $scope.getFlightLegs(flight);

				return (!last) ? $scope.getDuration($scope.getDurationDelta($scope.msToDate(legs[idx].ArrivalDateTime), $scope.msToDate(legs[idx + 1].DepartureDateTime))) : '';
			};

			function getStops(segments) {
				// Count the legs in all segments
				var stops = 0;
				for (var i = 0; i < segments.length; i++) {
					for (var j = 0; j < segments[i].Legs.length; j++) {
						stops++;
					}
				}

				return stops;
			}

			$scope.getStops = function (segments) {
				// Count the legs in all segments
				var stops = getStops(segments);

				return stops === 1 ? $scs('FlightScheduleResults.nonstop') : (stops === 2) ? 1 + ' ' + $scs('FlightScheduleResults.stop') : --stops + ' ' + $scs('FlightScheduleResults.stops');
			};

			$scope.getMonthAvailability = function (flight) {
				if (!flight.ValidDates || !flight.ValidDates.length) {
					return '';
				}

				// Assuming mm/dd/yyyy
				var first = moment(flight.ValidDates[0], 'M/D/YYYY');
				var last = moment(flight.ValidDates[flight.ValidDates.length - 1], 'M/D/YYYY');

				if (!first.isValid() || !last.isValid()) {
					return '';
				}

				return (flight.ValidDates.length > 1) ? $filter('date')(first.toDate(), 'MMM. d-') + last.date() : $filter('date')(first.toDate(), 'MMM. d');
			};

			$scope.getWeekdayAvailability = function (flight) {
				return (flight.DaysOfOperation) ? [
					(flight.DaysOfOperation.Sunday ? $scs('FlightScheduleResults.sunday') : '-'),
					(flight.DaysOfOperation.Monday ? $scs('FlightScheduleResults.monday') : '-'),
					(flight.DaysOfOperation.Tuesday ? $scs('FlightScheduleResults.tuesday') : '-'),
					(flight.DaysOfOperation.Wednesday ? $scs('FlightScheduleResults.wednesday') : '-'),
					(flight.DaysOfOperation.Thursday ? $scs('FlightScheduleResults.thursday') : '-'),
					(flight.DaysOfOperation.Friday ? $scs('FlightScheduleResults.friday') : '-'),
					(flight.DaysOfOperation.Saturday ? $scs('FlightScheduleResults.saturday') : '-')].join(' ') : '';
			};

			$scope.setFilter = function (val) {
				$scope.flightFilter = val;
			};

			$scope.getCurrentSort = function (flight) {
				switch ($scope.currentSort) {
					case 'depart':
						return $scope.getDate(flight, 'o');
					case 'stops':
						return getStops(flight.FlightSegments);
					case 'arrive':
						return $scope.getDate(flight, 'd');
					case 'duration':
						return parseInt(flight.JourneyTime, 10);
					default:
						var codeShare = (flight.FlightNumber.indexOf('*') === 0) ? '1' : '0';
						var departDate = moment($scope.getDate(flight, 'o')).format('HH:mm:ssZZ');
						var stops = getStops(flight.FlightSegments);

						return codeShare + stops + departDate;
				}
			};

			$scope.filterResults = function (flight) {
				return ($scope.flightFilter === 'ha') ? !flight.IsCodeShare : true;
			};

			$scope.getTerminal = function (term) {
				return (term) ? $scs('FlightScheduleResults.terminal') + ' ' + term : '';
			};

			$scope.msToDate = haDateUtils.msToDate;
			$scope.moment = moment;

			// Highlight text when the url changes
			$scope.$watch('url', function () {
				$timeout(function () {
					$('[ng-model="url"]').select();
				}, 100);
			});
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';
	var module;
	try {
		module = angular.module('haGiftCardModule');
	} catch (e) {
		module = angular.module('haGiftCardModule', []);
	}

	module.controller('giftCardCustomizeController', ['$scope', 'haGlobals', 'haModal', '$window', function ($scope, haGlobals, haModal, $window) {

		$scope.languageCode = $window.languageCode;
		$scope.Error = '';
		$scope.personalMessages = [];
		$scope.currencySymbols = {
			USD: '$',
			AUD: '$',
			NZD: '$',
			CNY: '¥',
			KRW: '₩',
			JPY: '¥',
			TWD: 'NT$'
		};

		// used to assign separate array of design indexes
		// so the correct radio button will be initially selected
		$scope.assignDesignIndexes = function (cardOrders) {
			$scope.designIndexes = [];
			for (var i = 0; i < cardOrders.length; i++) {
				$scope.designIndexes.push(cardOrders[i].DesignIndex);
			}
		};

		haGlobals('giftCardVM', function (giftCardVM) {
			// $log.debug('///////// giftCardVM');
			// $log.debug(giftCardVM);

			$scope.GiftCardVM = giftCardVM;
			$scope.GiftCardVM.CardOrders = giftCardVM.CardOrders || [{
				CardID: 1,
				Design: giftCardVM.Designs[0].Name,
				DesignIndex: 0,
				Amount: 50,
				To: '',
				From: '',
				RecipientEmail: '',
				PersonalMessage: ''
			}];

			$scope.assignDesignIndexes($scope.GiftCardVM.CardOrders);
			// scroll to card being edited
			if ($scope.GiftCardVM.SelectedCardID) {
				setTimeout(function () {
					angular.element('#GiftCard' + $scope.GiftCardVM.SelectedCardID).ready(function () {
						$('html,body').animate({
							scrollTop: $('#GiftCard' + $scope.GiftCardVM.SelectedCardID).offset().top
						}, 1000);
					});
				}, 1000);
			}

			$scope.currencyCode = $scope.GiftCardVM.CurrencyCode || 'USD';
		});

		$scope.addCard = function () {
			var cardId = $scope.GiftCardVM.CardOrders.length + 1;
			if ($scope.GiftCardVM.CardOrders.length < 10) {
				$scope.GiftCardVM.CardOrders.push({
					CardID: cardId,
					Design: $scope.GiftCardVM.Designs[0].Name,
					DesignIndex: 0,
					Amount: 50,
					To: '',
					From: '',
					RecipientEmail: '',
					PersonalMessage: ''
				});
			}
			$scope.assignDesignIndexes($scope.GiftCardVM.CardOrders);
		};
		$scope.removeCard = function (index) {
			$scope.GiftCardVM.CardOrders.splice(index, 1);
			$scope.assignDesignIndexes($scope.GiftCardVM.CardOrders);
		};

		$scope.openFAQModal = function () {
			console.log('open faq modal');
			haModal({
				id: 'FAQ',
				backdrop: 'true',
				template: angular.element('.showFAQ')
			});
		};

		$scope.openTermsModal = function () {
			haModal({
				id: 'Terms',
				backdrop: 'true',
				template: angular.element('.showTerms')
			});
		};

		$scope.getHelpContent = function () {
			haModal('', {
				id: 'gift-card-help',
				backdrop: 'true',
				template: angular.element('.getHelpContent')
			});
		};
	}
	]);

	module.controller('giftCardPaymentController', ['$scope', '$rootScope', 'haGlobals', 'haModal', '$window', '$timeout', 'haPaymentTypesService', function ($scope, $rootScope, haGlobals, haModal, $window, $timeout, haPaymentTypesSvc) {
		$scope.GiftCardPayment = {
			EmailAddress: '',
			IsGiftCardPayment: '',
			ProductDetails: ''
		};
		$scope.languageCode = $window.languageCode;
		$scope.submitting = false;

		haGlobals('giftCardPaymentJson', function (giftCardPaymentJson) {
			// $log.debug('///////// giftCardPaymentJson');
			// $log.debug(giftCardPaymentJson);

			$scope.GiftCardPayment = giftCardPaymentJson;
			// TEMPORARY
			//$scope.GiftCardPayment.Designs = Designs;
			$scope.currencyCode = $scope.GiftCardPayment.CurrencyCode || 'USD';

			// $scope.GiftCardPayment.EmailAddress = giftCardPaymentJson.EmailAddress;
			$scope.GiftCardPayment.IsGiftCardPayment = true;
			// $scope.GiftCardPayment.ProductDetails = giftCardPaymentJson.ProductDetails;

			$scope.haPaymentTypes = haPaymentTypesSvc;
			$scope.haPaymentTypes.setPaymentTypes({ data: giftCardPaymentJson.PaymentOptionVM });
			$scope.haPaymentTypes.paymentMethod = 'creditDebit';
		});


		$scope.getHelpContent = function () {
			haModal('', {
				id: 'gift-card-help',
				backdrop: 'true',
				template: angular.element('.getHelpContent')
			});
		};

		$scope.showPrivacyPolicy = function () {
			haModal('', {
				id: 'privacy-policy',
				backdrop: 'true',
				template: angular.element('.showPrivacyPolicy')
			});
		};

		$scope.showTerms = function () {
			haModal('', {
				id: 'terms-and-conditions',
				backdrop: 'true',
				template: angular.element('.showTerms')
			});
		};

		$scope.showGiftCardPreview = function (cardIndex) {
			$scope.orderPreview = $scope.GiftCardPayment.ProductDetails.CardDetails[cardIndex];
			haModal({
				id: 'giftCardPreview',
				backdrop: 'true',
				templateUrl: '/giftCardPreview.html',
				scope: $scope
			});
		};

		$scope.onSubmitForm = function () {
			// ... all other internal processing on submit
			/* global userdata */
			/* jshint -W106 */
			userdata.load_data('user_data');
			/* jshint +W106 */

			$scope.submitting = true;

			return true;
		};

		$scope.CDESubmit = function () {
			$rootScope.$broadcast('CDESubmit');
		};

		$scope.$on('CDEPaymentSubmitted', function() {
			$scope.submitting = true;
		});

		$scope.$on('CDEPaymentSubmitError', function() {
			$scope.submitting = false;
			$timeout(function() {
				$('html, body').animate({ scrollTop: $('#formIFrame').offset().top - 50 }, 'fast');
			}, 100);
		});
		// end PCI
	}
	]);

	module.controller('GiftCardConfirmationController', ['$scope', 'haGlobals', 'haModal', function ($scope, haGlobals, haModal) {

		$scope.Error = '';

		haGlobals('giftCardJson', function (giftCardJson) {
			// $log.debug('///////// giftCardJson');
			// $log.debug(giftCardJson);
			$scope.giftConfirmVM = giftCardJson;
		});

		haGlobals('errorMessage', function (errorMessage) {
			if (errorMessage !== '') {
				$scope.Error = errorMessage;
			}
		});

		$scope.getHelpContent = function () {
			haModal('', {
				id: 'gift-card-help',
				backdrop: 'true',
				template: angular.element('.getHelpContent')
			});
		};
	}
	]);
})(angular);
;
(function (angular) {
	'use strict';

	var haContactUsModule = angular.module('haContactUsTopicsModule', []);

	haContactUsModule.controller('ContactUsTopicsController', [
		'$scope', 'haGlobals', 'haUtils', function ($scope, haGlobals, haUtils) {
			var savedFaqs = [];
			var subTopicLoaded = false; // Flag to indicate whether or not the subtopic was loaded from json.
			$scope.isEN = haUtils.isEN();

			haGlobals(['topics', 'faqs', 'jsonTopicInfo'], function (topics, faqs, jsonTopicInfo) {
				$scope.topics = topics;
				$scope.QuestionsIntroText = faqs && faqs.length ? faqs[0].QuestionsIntroText : '';
				savedFaqs = faqs && faqs.length ? faqs[0].FaqsList : '';
				$scope.faqs = faqs && faqs.length ? faqs[0].FaqsList : '';

				if (!$scope.topics) {
					return;
				}

				var top;
				var sub;
				var i;
				for (i = 0; i < $scope.topics.length; i++) {
					if ($scope.topics[i].Title === jsonTopicInfo.topic) {
						top = $scope.topics[i];
						break;
					}
				}

				if (typeof top !== 'undefined') {
					for (i = 0; i < top.SubTopics.length; i++) {
						if (top.SubTopics[i].Title === jsonTopicInfo.subTopic) {
							sub = top.SubTopics[i];
							break;
						}
					}

					$scope.topic = top;
					if (typeof sub !== 'undefined') {
						$scope.subTopic = sub;
						subTopicLoaded = true;
					}
				}
			});

			// Create lists for the quantity dropdown for the awards
			$scope.awardChanged = function (awardType) {
				$scope.awardQuantityArray = [];
				if (awardType.Min !== undefined && awardType.Min !== awardType.Max && awardType.Increment !== 0) {
					// This award has an associated quantity range
					for (var i = awardType.Min; i < awardType.Max; i = i + awardType.Increment) {
						$scope.awardQuantityArray.push(i.toString());
					}
					$scope.awardQuantityArray.push(awardType.Max.toString());
				} else {
					// Fallbacks
					$scope.awardQuantityArray = ["1","2","3","4","5","6","7","8","9","10"];
				}
			};

			$scope.$watch('topic', function (newVal) {
				if (newVal) {
					if ($scope.topic.FaqsList && $scope.topic.FaqsList.length > 0) {
						$scope.faqs = $scope.topic.FaqsList;
					} else {
						$scope.faqs = savedFaqs;
					}
				}

				//  If the subtopic was loaded from json, hold off resetting it to an empty object just this once.
				if (subTopicLoaded) {
					subTopicLoaded = false;
				}
			});

			$scope.$watch('autoSelectFirstTopic', function (newVal) {
				if (newVal && $scope.topics) {
					$scope.topic = $scope.topics[0];
				}
			});
		}
	]);
})(angular);
;
'use strict';

var module = angular.module('haMyReceiptsModule', ['haUtilsModule', 'haGlobalsModule', 'haFeatureFlagsModule', 'haCurrencyModule']);

module.controller('MyReceiptsController', [
	'$scope', 'haGlobals', 'haModal', 'haHttpService', 'haLaunchDarklyAPI', function ($scope, haGlobals, haModal, haHttpService, halaunchDarklyAPI) {

		$scope.Error = '';
		$scope.activeTab = 'ticketsAndFees';

		// for inflight receipts date range inputs
		$scope.dateRangeConfig = {
			start: '[name=_startDate]',
			end: '[name=_endDate]',
			depart_cal_title: '',
			return_cal_title: '',
			namespace: 'inflight',
			viewing: moment().startOf('month').subtract(1, 'months'),
			range_start: moment().startOf('day').subtract(9, 'months'),
			range_end: moment().startOf('day'),
			onRender: function ($cal) {
				$cal.addClass('double-wide');
			}
		};
		$scope.tripType = 2;
		$scope.legs = [];
		$scope.model = {
			startDate: '',
			endDate: ''
		};

		var endpoints = {
			getReceipts: '/MyReceipts/GetRecieptList',
			getMyReceiptDetails: '/MyReceipts/GetReceiptDetails',
			GetMSRDetails: '/MyReceipts/GetMSRDetails'
		};

		$('.ha-page-help').hide();
		$scope.getRecieptList = function () {
			$scope.loading = true;
			haHttpService.POST(endpoints.getReceipts).then(
				function (response) {
					if (response.data && response.data.Success) {
						$scope.RecieptList = response.data.eReceiptsVM;
						$scope.error = false;
						$scope.loading = false;
					} else {
						$scope.error = true;
						$scope.loading = false;
					}
				},
				function () {
					$scope.error = true;
					$scope.loading = false;

				}
			);
		};

		$scope.ConvertToValidDate = function (dateString) {
			var re = /-?\d+/;
			var m = re.exec(dateString);
			return new Date(parseInt(m[0]));
		};

		$scope.moment = moment;
		$scope.SplitTicketNumber = function (ticketNumber) {
			if (ticketNumber.indexOf('-') >= 0) {
				return ticketNumber.split('-')[0];
			} else {
				return ticketNumber;
			}
		};

		var enable1AForLegacy = false;

		halaunchDarklyAPI.getFeatureFlag(HA.FLAGS.EnableSpaForLegacy, 'boolean', true, true).then(
                    function (response) {
                        if (response.data === "ENABLED") {
                            enable1AForLegacy = true;
                        }
                    });
		// form submit
		$scope.submitMyReceiptsForm = function (evt) {
		    if (enable1AForLegacy)
		    {
		        window.location.href = "/system-maintenance";
		    }
			evt.preventDefault();
			$scope.loadingFindReceipt = true;
			$scope.error = false;
			$("#lastName").val($("#lastName").val().trim());
			$("#ticketOrMSRNumber").val($("#ticketOrMSRNumber").val().trim());
			var payload = $("#MyReceiptsForm").serialize();
			var jsonPayload = {};
			var nullCheck = false;
	        var splitTktName = payload.split("&");

	        splitTktName.forEach(function (pair) {
				pair = pair.split("=");
				if (pair[1] === "") {
					nullCheck = true;
				}
				jsonPayload[pair[0]] = decodeURIComponent(pair[1] || "");
			});

			jsonPayload = JSON.parse(JSON.stringify(jsonPayload));
			jsonPayload.lastName = jsonPayload.lastName.replace('+', ' ');


	        if (nullCheck === true) {
	            $scope.loadingFindReceipt = false;
	            $("html, body").animate({ scrollTop: 0 }, "slow");
	        }
	        else {
				$scope.getReciept(jsonPayload, "", "", false);
	        }

	        return false;
	    };

	    var reqParams = "";

	    $scope.getReciept = function (payload, ticketNumber, lastName, isExchangedTicket) {
	        var requestUrl = endpoints.getMyReceiptDetails;
			if (payload === '') {
	            //Will be called from view link, present on Find Receipt Page.
				reqParams = {
					'ticketOrMSRNumber': ticketNumber,
					'lastName': lastName,
					'enc': 0,
					'isExchangedTicket': isExchangedTicket
				};
	        }
			else {
		        reqParams = payload;
	        }
			haHttpService.POST(requestUrl, reqParams).then(
				function (response) {
				    if (response.data && response.data.Success) {
				        
				        $scope.IsMSRdetails = false;
				        $scope.IsTicketDetailAvailable = false;
				        $scope.RecieptDetails = response.data.eReceiptsDetailViewModel;
						$scope.flightToBeExchangedEncoded = $scope.RecieptDetails.FlightToBeExchanged;
						$scope.IsEmdOrdersEnabled = $scope.RecieptDetails.IsEmdOrdersEnabled;
						$scope.IsFindMyCreditFeatureEnabled = $scope.RecieptDetails.IsFindMyCreditFeatureEnabled;
			
				        if (response.data.eReceiptsDetailViewModel.FlighDetailsList == null) {
				            $scope.IsMSRdetails = true;
				        }
				        else {
				            $scope.IsTicketDetailAvailable = true;
				        }

				        if (response.data.eReceiptsDetailViewModel.MSRlist == null) {
				            $scope.IsMSRdetails = false;
				        }
				        if ($scope.IsMSRdetails || $scope.IsTicketDetailAvailable) {
				            $scope.error = false;
				            window.location.href = response.data.RedirectURL;
				        }
				        else {
				            $scope.error = true;
				            $('html, body').animate({ scrollTop: 0 }, 'slow');
				        }
				        $scope.loadingFindReceipt = false;
				    } else {
				        $scope.error = true;
				        $scope.loadingFindReceipt = false;
				        $('html, body').animate({ scrollTop: 0 }, 'slow');
				    }
				},
				function () {
				    $scope.error = true;
				    $scope.loading = false;
				    $('html, body').animate({ scrollTop: 0 }, 'slow');
				});
	    };
	    $scope.manageQuantity = function (input) {
	        return parseInt(input, 10);
	    };
	    haGlobals(['HACombinedReceipts', 'HACombinedReceiptsList'], function (HACombinedReceipts, HACombinedReceiptsList) {
	        $scope.CombinedReceipts = HACombinedReceipts;
	        $scope.CombinedReceiptsList = HACombinedReceiptsList;
	    });
	    $scope.getRecieptList();

	    $scope.HasMainCabinBasicBooking = function () {
	    	if (!$scope.CombinedReceiptsList) {
		        return false;
	        }
	    	for (var i = 0; i < $scope.CombinedReceiptsList.length; i++) {
	    		if ($scope.CombinedReceiptsList[i].IsMainCabinBasicBooking) {
		            return true;
	            }
	    	}
	        return false;
	    };


	}
]);

module.controller('MyReceiptsDetailController', [
	'$scope', 'haGlobals', 'haModal', 'haHttpService', 'haDateUtils', 'haSitecoreStrings', '$window',
	function ($scope, haGlobals, haModal, haHttpService, haDateUtils, $scs, $window) {
	    $scope.Error = '';
	    $scope.ReceiptEncryptedTicketNumber = $window.EncrytpedTicketnumberJson;
	    $scope.ReceiptEncryptedLastName = $window.EncrytpedLastNameJson;
	    $scope.EncryptedFlag = $window.encryptedFlagJson;
	    $scope.IsTravelerMSRAvail = false;
	    var endpoints = {
	        getReceiptDetails: '/MyReceipts/GetReceipt',
	        getMSRDetails: '/MyReceipts/GetMSRDetails'
	    };
	    haGlobals('myReceiptsJson', function () {

	    });

	    $scope.ShowMSR = function (msrIndex) {
	        $scope.RecieptMSRDetails = $scope.RecieptDetails.MSRlist[msrIndex];
	        $scope.IsMSRdetails = false;
	        $scope.IsTicketDetailAvailable = false;

	        if ($scope.RecieptMSRDetails.MSRnumber !== '' && $scope.RecieptMSRDetails.LastName !== '') {

				var reqParams = {
					'ticketOrMSRNumber': $scope.RecieptMSRDetails.MSRnumber,
					'lastName': $scope.RecieptMSRDetails.LastName
				};
				
	            endpoints.GetMSRDetails = '/MyReceipts/GetMSRDetails';

	            $scope.loading = true;
	            $scope.IsTicketDetailAvailable = false;
				haHttpService.POST(endpoints.GetMSRDetails, reqParams).then(
					function (response) {
					    if (response.data && response.data.Success) {
					        $scope.loading = false;
					        $scope.IsMSRdetails = true;
							$scope.MSRDetails = response.data.eReceiptsDetailViewModel.MSRlist;
							$scope.IsEmdOrdersEnabled = response.data.eReceiptsDetailViewModel.IsEmdOrdersEnabled;
							$scope.IsFindMyCreditFeatureEnabled = response.data.eReceiptsDetailViewModel.IsFindMyCreditFeatureEnabled;
					    }
					    else {
					        $scope.error = true;
					        $scope.loading = false;
					    }
					}, function () {
					    $scope.error = true;
					    $scope.loading = false;
					}
				);

	        }
	    };

	    $scope.isContainChar = function (strValue, charValue) {
	        if (strValue.indexOf(charValue) > -1) {
	            return true;
	        }
	        else {
	            return false;
	        }
	    };
	    $scope.getConjTickets = function (tktNumber) {
	        var tktNumbers = tktNumber.split('-');
	        var appendedTickets = tktNumbers[0];
	        var newTkt = appendedTickets.substring(0, appendedTickets.length - 2);
	        for (var i = 1; i < tktNumbers.length; i++) {
	            appendedTickets += ', ' + newTkt + tktNumbers[i];
	        }
	        return appendedTickets;
	    };
	    $('.ha-page-help').hide();
	    $scope.IsNumber = function (input) {
	        return !isNaN(input) && input !== null && input !== 'undefined';
	    };


	    $scope.conjInfo = function (infoheading, infolabelm, ticketnumber) {
	        return infoheading + infolabelm + $scope.getConjTickets(ticketnumber);
	    };

	    $scope.manageQuantity = function (input) {
	        return parseInt(input, 10);
	    };


	    $scope.msToDate = haDateUtils.msToDate;
		$scope.moment = moment;
		var reqParams = "";
	    $scope.getRecieptDetails = function (ticketNumber, lastName, encryptedFlag, isExchangedTicket) {
	        if (ticketNumber !== '' && lastName !== '' && encryptedFlag !== '') {
		        reqParams = {
					'ticketOrMSRNumber': ticketNumber,
					'lastName': lastName,
					'enc': encryptedFlag,
					'isExchangedTicket': isExchangedTicket
				};
	            endpoints.getReceiptDetails = '/MyReceipts/GetReceiptDetails';
	        }
	        $scope.loading = true;
			haHttpService.POST(endpoints.getReceiptDetails, reqParams).then(
				function (response) {
				    if (response.data && response.data.Success) {
				        $scope.IsTravelerMSRAvail = false;
				        $scope.IsMSRdetails = false;
				        $scope.IsTicketDetailAvailable = false;
						$scope.RecieptDetails = response.data.eReceiptsDetailViewModel;
						$scope.flightToBeExchangedEncoded = $scope.RecieptDetails.FlightToBeExchanged;
						$scope.IsEmdOrdersEnabled = $scope.RecieptDetails.IsEmdOrdersEnabled;
						$scope.IsFindMyCreditFeatureEnabled = $scope.RecieptDetails.IsFindMyCreditFeatureEnabled;
				        if (response.data.eReceiptsDetailViewModel.FlighDetailsList == null) {
				            $scope.IsMSRdetails = true;
				            $scope.MSRDetails = response.data.eReceiptsDetailViewModel.MSRlist;
				        }
				        else {
				            $scope.IsTicketDetailAvailable = true;
				            $scope.conjInfo = '<b>' + $scs('MyReceiptsDetails.conjunctiveticketinfo') + '</b><br/>' + $scs('MyReceiptsDetails.eticketinfo') + $scope.getConjTickets($scope.RecieptDetails.ETicketNumber);

				            for (var i = 0; i < $scope.RecieptDetails.FlighDetailsList.length; i++) {
				                if ($scope.RecieptDetails.FlighDetailsList[i].DepartureDateTime) {
				                    var d = moment($scope.RecieptDetails.FlighDetailsList[i].DepartureDateTime).toDate();
				                    $scope.RecieptDetails.FlighDetailsList[i].DepartureDateObject = d;
				                }

				                if ($scope.RecieptDetails.FlighDetailsList[i].ArrivalDateTime) {
				                    var d = moment($scope.RecieptDetails.FlighDetailsList[i].ArrivalDateTime).toDate();
				                    $scope.RecieptDetails.FlighDetailsList[i].ArrivalDateObject = d;
				                }
				            }
				        }

				        if (response.data.eReceiptsDetailViewModel.MSRlist != null) {
				            $scope.RecieptMSRDetails = response.data.eReceiptsDetailViewModel.MSRlist;

				            for (var i = 0; i < $scope.RecieptDetails.MSRlist.length; i++) {
				                if ($scope.RecieptDetails.MSRlist[i].IsMsrAvailable) {
				                    $scope.IsTravelerMSRAvail = $scope.RecieptDetails.MSRlist[i].IsMsrAvailable;
				                }
				            }
				        }

				        $scope.error = false;
				        $scope.loading = false;
				    } else {
				        $scope.error = true;
				        $scope.loading = false;
				    }
				},
				function () {
				    $scope.error = true;
				    $scope.loading = false;
				}
			);

	    };

	    //Will be called on load.
	    if ($scope.ReceiptEncryptedTicketNumber != null && $scope.ReceiptEncryptedLastName != null && $scope.EncryptedFlag) {
	        $scope.getRecieptDetails($scope.ReceiptEncryptedTicketNumber, $scope.ReceiptEncryptedLastName, $scope.EncryptedFlag, false);
	    } else {
	        $scope.getRecieptDetails('', '', '', false);
	    }

	    $scope.SplitTicketNumber = function (ticketNumber) {
	        if (ticketNumber.indexOf('-') >= 0) {
	            return ticketNumber.split('-')[0];
	        } else {
	            return ticketNumber;
	        }
	    };
	}
]);
;
(function (ng) {

	// Trip Summary Controller
	// --------------------------------------------
	//
	// * **Class:** haTripSummaryCtrl
	// * **Author:** Michael Toymil
	//

	'use strict';

	var module;
	try {
		module = ng.module('haTripSummaryModule');
	} catch (e) {
		module = ng.module('haTripSummaryModule', []);
	}

	module.controller('haTripSummaryCtrl', [
		'$scope', 'haHttpService', '$window', 'haGlobals', '$filter', '$rootScope', 'haUplift',
		function ($scope, http, $window, haGlobals, $filter, $rootScope, haUplift) {

			//Ancillaries Tracking
			$scope.$on('ancillariesOffered', function (e, offers) {
				var events = [];
				var products = [];
				var url = location.href;
				if ((/book\/payment/i).test(url)) {
					if (offers.leiGreeting) {
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('LeiGreetingOffered'));
					}
					if (offers.tripInsurance) {
						// Custom Event for Analytics
						document.body.dispatchEvent(new CustomEvent('TripInsuranceOffered'));
					}
				}
			});

			// Check if child fare feature switch is enabled
			$scope.enableChildFare = $scope.$switch('FlightResult:enablechildfarelabels');

			$scope.UserInsuranceAmount = 0;
			$scope.TripSummary.TripInsurance = $scope.TripSummary.TripInsurance || {};
	
			if ($window.tripInsuranceAdded === 1) {
				$scope.TripSummary.TripInsurance.IsTripInsuranceSelected = true;
			} else {
				$scope.TripSummary.TripInsurance.IsTripInsuranceSelected = null;
			}

			$scope.$emit('ancillariesOffered', {
				leiGreeting: $scope.TripSummary.SelectedLeiGreeting === null,
				tripInsurance: !!$scope.TripSummary.TripInsurance && !$window.tripInsuranceAdded
			});


			// Logic for ETCO section
			haGlobals(['enableTCR', 'ETCOResponseModel'], function (enableTCR, ETCOResponseModel) {
				$scope.enableTCR = enableTCR;
				$scope.ETCOResponseModel = ETCOResponseModel;
			});

			$scope.$watch('PassengerTripSummary', function (data) {
				if (data) {
					parseData(data);
				}

				$(document).ready(function () {
					setTimeout(function () {
						haUplift.selectUplift($scope.summaryModel.grandTotal, data, false);
						haUplift.refreshUplift();
					}, $rootScope.UpTimeout); 
				});
			});

			$scope.$on('ancelaryStateChange', function () {
				if ($scope.PassengerTripSummary) {
					parseData($scope.PassengerTripSummary);
				}
			});

			$scope.getCabinString = function (code) {
				var lowerCase = code.toLowerCase();
				if ($scope.summaryModel.IsRefundableFare) {
					lowerCase = 'refundable' + lowerCase;
				}
				return 'PassengerTripSummary.' + lowerCase + 'text';
			};

			function parseData(data) {
				var url = location.href;
				$scope.summaryModel = data;
				$scope.summaryModel.ETCOResponseModel = $scope.ETCOResponseModel;
				$scope.summaryModel.allSegments = {};
				$scope.summaryModel.totalMiles = 0;
				$scope.summaryModel.grandTotal = 0;
				$scope.summaryModel.totalSaved = 0;
				// Flatten all segments
				for (var i = 0; i < $scope.summaryModel.AvailGridTrips.length; i++) {
					for (var j = 0; j < $scope.summaryModel.AvailGridTrips[i].Segments.length; j++) {
						$scope.summaryModel.allSegments[$scope.summaryModel.AvailGridTrips[i].Segments[j].SegmentID] = $scope.summaryModel.AvailGridTrips[i].Segments[j];
					}
				}

				// Associate passengers with correct fares and generate grand total
				for (var i = 0; i < $scope.summaryModel.Passengers.length; i++) {
					var pax = $scope.summaryModel.Passengers[i];
					var subtotal = 0; // holds value in cents to avoid JS arithmetic errors
					var totalSeatAmount = 0;

					for (var j2 = 0; j2 < $scope.summaryModel.FareDetails.length; j2++) {
						var fare = $scope.summaryModel.FareDetails[j2];
						if (pax.FareType === fare.FareType && pax.IsCompanion === fare.IsCompanion) {
							pax.fare = fare;
							if ($scope.summaryModel.SelectedHotel !== null) {
								subtotal = 0; // Subtotal for each pax will be zero if not upgrading and if package selected
							} else {
								subtotal = fare.TotalAmount * 100;
							}

							// Seat amounts are already added in Flight Results
							if (!(/book\/flightresults/i).test(url)) {
								angular.forEach(pax.Seats, function (seat) {
									subtotal += seat.SeatAmount * 100;
								});
							} else {
								//total preferred seat upgrade amount
								angular.forEach(pax.Seats, function (seat) {
                                    totalSeatAmount += seat.SeatAmount ;
                                })
								//Updating the Base Fare Amount to account for Prices set in flight Results
								pax.fare.BaseFareAmount = (pax.fare.TotalAmount * 100 - (pax.fare.TaxAmount * 100 + totalSeatAmount * 100)) / 100;
							}

							pax.Subtotal = subtotal / 100; // convert cents to dollars
							$scope.summaryModel.grandTotal += pax.Subtotal;
							$scope.summaryModel.totalMiles += pax.fare.BaseFareMiles;
							$scope.summaryModel.totalSaved += fare.DiscountedAmount;
						}
					}

					// in the Korean locale, show all taxes expanded by default (PBI 158611)
					if ($rootScope.$language === 'ko-kr') {
						pax.taxesOpen = true;
					}
				}

				// Add miles purchase amount to grand total
				if ($scope.summaryModel.MilesPurchaseDetails && $scope.summaryModel.MilesPurchaseDetails.PurchaseAmount) {
					$scope.summaryModel.grandTotal += $scope.summaryModel.MilesPurchaseDetails.PurchaseAmount;
				}

				// Add package total to grandtotal (EC cabin cost added to package total already from applogic)
				if ($scope.summaryModel.SelectedHotel !== null) {
					$scope.summaryModel.grandTotal = $scope.summaryModel.SelectedHotel.GrandTotalWithoutEC;
					$scope.summaryModel.totalSaved += $scope.summaryModel.SelectedHotel.PackageSavings;
					for (var i = 0; i < $scope.summaryModel.Passengers.length; i++) {
						var paxOWW = $scope.summaryModel.Passengers[i];
						angular.forEach(paxOWW.Seats, function (seat) {
							// add SeatAmount if it's EC upgrade or PS
							if (+seat.Type === 1 || +seat.Type === 2 || seat.IsUpgrade) {
								if (!$scope.IsSeatAssignmentError) {
									$scope.summaryModel.grandTotal += seat.SeatAmount;
								}
							}
						});
					}
				}

				// Subtract ETCO credit from grandtotal
				if ($scope.enableTCR && $scope.summaryModel.ETCOResponseModel && $scope.summaryModel.ETCOResponseModel.Amount) {
					$scope.summaryModel.grandTotal -= $scope.summaryModel.ETCOResponseModel.Amount;
					$scope.summaryModel.totalSaved += $scope.summaryModel.ETCOResponseModel.Amount;
				}
				
				if ($scope.TripSummary != null) {
					// adding the insurance amount to grand total if insurance is already added
					if ($scope.TripSummary.TripInsurance != null && ($scope.TripSummary.TripInsurance.IsTripInsuranceSelected || $scope.TripSummary.IsTripInsuranceConfirmed) && !$scope.IsTripInsurancePurchaseError) {
						$scope.summaryModel.grandTotal += parseFloat($scope.TripSummary.TripInsurance.InsuranceCost);					
					}

					if ($scope.TripSummary.SelectedLeiGreeting != null && $scope.TripSummary.SelectedLeiGreeting.isEligible) {
						$scope.summaryModel.grandTotal += parseFloat($scope.TripSummary.SelectedLeiGreeting.totalCost);
					}

					if ($scope.TripSummary.IsAirportShuttleSelected && $scope.TripSummary.SelectedShuttleDetails != null && $scope.TripSummary.SelectedShuttleDetails.isEligible) {
						$scope.summaryModel.grandTotal += parseFloat($scope.TripSummary.SelectedShuttleDetails.totalPrice);
					}

					if ($scope.IsAirportShuttlePurchaseError) {
						$scope.summaryModel.grandTotal -= parseFloat($scope.TripSummary.SelectedShuttleDetails.totalPrice);
					}

					if ($scope.IsLeigreetingsPurchaseError) {
						$scope.summaryModel.grandTotal -= parseFloat($scope.TripSummary.SelectedLeiGreeting.totalCost);
					}

				}

				// Prevent negative totals and correct saved amount
				if ($scope.summaryModel.grandTotal < 0) {
					var spillover = 0 - $scope.summaryModel.grandTotal;
					$scope.summaryModel.grandTotal = 0;
					$scope.summaryModel.totalSaved -= spillover;
				}

				
			}
		}
	]);

})(angular);
;
(function (ng) {

	// Promo Details Controller
	// --------------------------------------------
	//
	// * **Class:** promoDetailsCtrl
	// * **Author:** Michael Toymil
	//

	'use strict';

	var module;
	try {
		module = ng.module('promoDetailsModule');
	} catch (e) {
		module = ng.module('promoDetailsModule', []);
	}

	module.controller('promoDetailsCtrl', [
		'$scope', 'haHttpService', '$window', '$rootScope',
		function ($scope, http, $window, $rootScope) {
		    console.log('promo details ctrl loaded');
			http.POST('/MyAccount/MemberDiscounts/GetPromotionDetail', {promoCode: $scope.promoCode}).then(
				function (res) {
					$scope.promoData = res.data;
				},
				function (err) {
					console.log(err, 'err');
				}
			);


			$scope.applyPromo = function (promoIndex) {
				// tell the sticky progress bar to apply the promo
				var flightResultsStickProgressBarScope = angular.element('#flightResultsStickProgressBar').scope();
				if (flightResultsStickProgressBarScope != null) {
					flightResultsStickProgressBarScope.applyPromo(promoIndex);
				} else {
					$rootScope.$broadcast('ExpertBookingApplyPromo', promoIndex);
				}

				// close the modal
				$scope.$modalCancel();
			};

			$scope.RedirectToLoginPage = function (response) {
				if (response !== null && response.data !== null && response.data.RedirectURL !== null) {
					$window.location.href = response.data.RedirectURL;
				}
				else {
					$window.location.href = $window.location.href;
				}
			};

			$scope.$on('responseError:UNAUTHORIZED', function (event, response) {
				$scope.RedirectToLoginPage(response);
			});

			$scope.$on('responseError:FORBIDDEN', function (event, response) {
				$scope.RedirectToLoginPage(response);
			});
		}
	]);

})(angular);
;
(function (angular) {

	// Ha Hotels Add On
	// --------------------------------------------
	//
	// * **Class:** HaTrueUpCtrl
	// * **Author:** Cory Shaw
	//
	// This module handles the MVC View Model for the True Up Form

	'use strict';

	var module;
	module = angular.module('haTrueUpModule', ['haMemberDobModule']);
	module.controller('haTrueUpCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'haUtils',
		'haModal',
		'haGlobals',
		'haHttpService',
		'$window',
		'haGeoDataSvc',
		'haSitecoreStrings',
		'haFeatureFlags',
		function ($log, $scope, $rootScope, haUtils, haModal, haGlobals, haHttpService, $window, geo, $scs, flags) {

			function validateModel($VM) {
				//$log.debug($VM);
				// we need to validate all model data before attaching to partial
				// to prevent errors on seemingly empty inputs.

				// validator takes string and regex
				function validateRegex(string, regex, notFoundRes) {
					if (string) {
						if (string.match(regex)) {
							$log.debug('validating', string, 'TRUE');
							return string;
						} else {
							$log.debug('validating', string, 'FALSE');
							return notFoundRes;
						}
					} else {
						return notFoundRes;
					}
				}

				// validate email
				var $email = $VM.AccountDetail.EmailAddress;
				$VM.AccountDetail.EmailAddress = validateRegex($email, $rootScope.$regex.email, '');

				// validate username
				var $username = $VM.AccountDetail.UserName;
				$VM.AccountDetail.UserName = validateRegex($username, $rootScope.$regex.username, '');

				// validate addresses
				var $address = $VM.MemberAddress;
				$address.Address1 = validateRegex($address.Address1, $rootScope.$regex.addressLine, '');
				$address.Address2 = validateRegex($address.Address2, $rootScope.$regex.addressLine, '');
				$address.Address3 = validateRegex($address.Address3, $rootScope.$regex.addressLine, '');

				// validate zip
				var regex = geo.getPostalCodeRegex($scope.VM.MemberAddress.CountryKey);
				$scope.VM.MemberAddress.ZipCode = validateRegex($scope.VM.MemberAddress.ZipCode, regex, undefined);

			}


			haUtils.attachViewModelToScopeAsVM($scope, 'TrueUpViewModel')
			.then(function () {

				// Fetch list of Countries
				haGlobals('countryData', function (countryData) {
					$scope.countries = geo.setCountryData(countryData);
				});

				// set disable username flag if username exists
				$scope.VM.disableUsername = ($scope.VM.AccountDetail.UserName && $scope.VM.AccountDetail.UserName.length);

				// validating the model
				validateModel($scope.VM);


				// Is Username prefilled?
				$scope.usernamePrefilled = false;
				if ($scope.VM.AccountDetail.UserName != null &&
					$scope.VM.AccountDetail.UserName.length > 0) {
					$scope.usernamePrefilled = true;
				}

				// Until populated
				$scope.VM.MemberAddress = $scope.VM.MemberAddress || {};


				// Parse DOB
				$scope.MemberPersonalInfo = {};
				$scope.MemberPersonalInfo = $scope.VM.MemberPersonalInfo;
				$scope.MemberPersonalInfo.DOBMonthDropDown.shift();
				$scope.MemberPersonalInfo.DOBYearDropDown.shift();

				$scope.MemberPersonalInfo.GenderDropDown.shift();

				$scope.$broadcast('VMReady');

			});

			var error = function (err) {
				$log.error('haTrueUpCtrl', err);
				$scope.error = err.data;
				angular.element('html,body').animate({scrollTop: 0}, 'fast');
				$scope.submitting = false;
			};

			angular.extend($scope, {
				hasPostalCode: geo.hasPostalCode
			});


			$scope.showPrivacyPolicy = function () {
				haModal('/Shared/Modal/GetModalContent?fieldName=ViewPrivacyPolicy&itemId=HMRegistration', {
					id: 'registration-privacy',
					backdrop: 'true'
				});
			};

			$scope.showTerms = function () {
				haModal('/Shared/Modal/GetModalContent?fieldName=ViewTermsFullText&itemId=HMRegistration', {
					id: 'registration-privacy',
					backdrop: 'true'
				});
			};

			// For non-individual true up forms
			$scope.showIndividualFields = function () {
			    var nonIndividualTypes = flags.get('NonIndividualMemberTypes');
			    return $scope.VM
                    && ($scope.VM.MemberType == null
                        || nonIndividualTypes == null
                        || nonIndividualTypes.indexOf($scope.VM.MemberType) < 0);
			};

			$scope.submitForm = function () {

				$scope.submitting = true;

				var data = {
					AccountDetail: angular.copy($scope.VM.AccountDetail),
					MemberPersonalInfo: {
						DateOfBirth: $scope.MemberPersonalInfo.SelectedDOB,
						Gender: $scope.MemberPersonalInfo.Gender
					},
					GuardianInfo: angular.copy($scope.VM.GuardianInfo),
					MemberAddress: angular.copy($scope.VM.MemberAddress),
					PhoneDetailList: {
						PhoneDetails: angular.copy($scope.VM.PhoneDetailList.PhoneDetails)
					},
					SecurityQuestions: {
						SecurityQuestionAnswers: angular.copy($scope.VM.SecurityQuestions.SecurityQuestionAnswers)
					}
				};

				var MA = $scope.VM.MemberAddress;
				data.AccountDetail.ZipCode = MA.ZipCode;
				data.AccountDetail.PostalCode = MA.ZipCode;
				data.AccountDetail.PostalCodeKey = MA.PostalCodeKey;

				data.MemberAddress.IsoCode = (MA.Country && MA.Country.IsoCode) || null;
				data.MemberAddress.CountryKey = (MA.Country && MA.Country.Key) || 0;
				data.MemberAddress.Country = (MA.Country && MA.Country.DisplayName) || null;
				data.MemberAddress.StateKey = (MA.State && MA.State.Key) || 0;
				data.MemberAddress.State = (MA.State && MA.State.DisplayName) || null;
				data.MemberAddress.CityKey = (MA.City && MA.City.Key) || 0;
				data.MemberAddress.City = (MA.City && (MA.City.Name || MA.City.DisplayName)) || MA.CityName;

				haHttpService.POST('/MyAccount/Login/UserProfileTrueUp', data)
				.then(function (response) {
					$log.debug(response.data);
					if (response.data.RedirectURL != null) {
						$window.location = response.data.RedirectURL;
					}
					if (response.data.IsSuccess !== true) {
						return error(response);
					} else {
						$scope.error = false;
					}


				}, error);

			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haBarclaysModule', ['ngSanitize']);

	module.controller('barclaysController', ['$scope','$rootScope', 'haModal', 'haHttpService', 'haSitecoreStrings', function ($scope, $rootScope, haModal, haHttpService, $scs) {

		// Open the Barclays modal if the template rendered
        if ($('#barclaysModalTemplate').length > 0 && !$('#barclaysModal').length) {
			haModal('barclaysModalTemplate', {
				id: 'barclaysModal',
				backdrop: 'true',
				scope: $scope
			});
        }

        if ($('#preApprovedModalTemplate').length > 0) {
            haModal('preApprovedModalTemplate', {
                id: 'preApprovedModal',
                backdrop: 'true',
				modalLock: true,
                scope: $scope
            });
        }

		var barclaysPostComplete = false;
		var address1 = "PaymentInfo.BillingAddress.Address1";
		var country = "PaymentInfo.BillingAddress.Country";
		var city = "PaymentInfo.BillingAddress.City";
		var stateEn = "PaymentInfo.BillingAddress.State_EN";
		var stateEnText = "PaymentInfo.BillingAddress.State_EN_Text";
		var zip = "PaymentInfo.BillingAddress.ZipCode";
		var first = "PaymentInfo.CCInfo.FirstName";
		var last = "PaymentInfo.CCInfo.LastName";

		var bccPreAppNotSignedInUrl = "/book/creditcard/creditpreapprovalcheckfornotloggedinuser";
		var bccAcceptOfferUrl = "/book/creditcard/acceptoffer";
		var bccAcceptPreAppUrl = "/book/creditcard/acceptpreapprovaloffer";
		var bccDeclineOffer = "/book/creditcard/declineoffer";
		var closeModal = "/book/creditcard/closemodal";
		var ctaType = 0;


		$scs.get('ReviewAndPay').then(function (data) {
			$scope.scContent = data;
		});

		// for CC A/B test:
		$scope.$watch(function() {
			return window.showAlternateCCOffer;
		}, function (newVal) {
			if (newVal) {
				$scope.showAlternateCCOffer = newVal;
				$scope.$root.$broadcast('haStickyResize');
			}
		});

		$scope.isHMCardHolder = window.acctIsBCUSCardHolder || false;

		$scope.preApprovedForCC = false;
		$scope.CCApprovedWithFormData = false;
		$scope.CCItemEnum = $('#CCItemEnum').text();		

		if ($scope.CCItemEnum === '2' || $scope.CCItemEnum === '3') {
			$scope.preApprovedForCC = true;
		}

		// do the needful with Barclays response for pre-approval check
		function handlePreApprovalCheckResponse(response) {
			//update the barclays IPS rootscope flag
			$rootScope.bccIpsOffer = getBccIPSCookie() === "True";
			if (response.data) {
				var itemEnum = $(response.data).find('#CCItemEnum').text();
				if (itemEnum === '2' || itemEnum === '3') {
					$scope.preApprovedForCC = true;
					$scope.CCApprovedWithFormData = true;
				}
				$scope.CCItemEnum = itemEnum;
				var $replacement = $(response.data).children();
				$('#barclaysOfferContain').html($replacement);
				$scope.$root.$broadcast('haStickyResize');
			}
        }

		$scope.fieldBlurred = function () {
			if (!barclaysPostComplete && !$scope.$root.isMobile &&
				$scope.paymentform[first].$modelValue && $scope.paymentform[last].$modelValue &&
				$scope.paymentform[country].$modelValue && $scope.paymentform[address1].$modelValue && $scope.paymentform[city].$modelValue && $scope.paymentform[zip].$modelValue) {

				// State variations (State_EN or State_EN_Text)
				var state;
				if ($scope.paymentform[stateEn] && $scope.paymentform[stateEn].$modelValue) {
					state = $scope.paymentform[stateEn].$modelValue;
				} else if ($scope.paymentform[stateEnText] && $scope.paymentform[stateEnText].$modelValue) {
					state = $scope.paymentform[stateEnText].$modelValue;
				} else {
					return;
				}

				barclaysPostComplete = true;
				var paymentInfo = {
					Address: {
						Address: $scope.paymentform[address1].$modelValue,
						City: $scope.paymentform[city].$modelValue,
						State: state,
						Country: $scope.paymentform[country].$modelValue,
						ZipCode: $scope.paymentform[zip].$modelValue
					},
					CreditCard: {
						FirstName: $scope.paymentform[first].$modelValue,
						LastName: $scope.paymentform[last].$modelValue
					},
					IsBarclaysPreApprovalPostforNonLoggedInUserDone: true
				};

				haHttpService.POST(bccPreAppNotSignedInUrl, JSON.stringify(paymentInfo)).then(handlePreApprovalCheckResponse);
			}
		};

		// credit card approval check called by ha-payment-cde.js
		$scope.$on('BarclaysCreditpreApprovalCheck', function (event, data) {
			barclaysPostComplete = true;
			var paymentInfo = {
				Address: {
					Address: data.Address1,
					City: data.City,
					State: data.State,
					Country: data.Country,
					ZipCode: data.ZipCode
				},
				CreditCard: {
					FirstName: data.FirstName,
					LastName: data.LastName
				},
				IsBarclaysPreApprovalPostforNonLoggedInUserDone: true
			};

			haHttpService.POST(bccPreAppNotSignedInUrl, JSON.stringify(paymentInfo)).then(handlePreApprovalCheckResponse);
		});

		// request CDE iframe to pass necessary data
		$scope.requestData = function (event, itemEnum) {

			// for adding referrerid param to apply link, for tracking
			ctaType = $scope.preApprovedForCC ? 1 : 2;

			if (itemEnum === 'scope') {
				itemEnum = $scope.CCItemEnum;
			}

			if ($rootScope.isLoggedIn && (itemEnum === "2" || itemEnum === "3" || itemEnum === "7" || itemEnum === "8") && !$scope.CCApprovedWithFormData) { // 7 & 8 are new target cc types
				// if logged in and pre-approved but not CCApprovedWithFormData, call the applyNow method, same as overlay
				$scope.applyNow(false, event, itemEnum);
			} else {
				// save the itemEnum
				$scope.itemEnum = itemEnum;
				// request billing info from CDE payment form
			var iframe = document.getElementById('formIFrame');
			iframe.contentWindow.postMessage('barclaysRequest', iframeOrigin);
			}
		};

		// barclays apply now  called by ha-payment-cde.js
		$scope.$on('BarclaysApplyNow', function (event, data) {

			var requestUrl = bccAcceptOfferUrl + '?IsFullApp=true' +
				'&fn=' + data.FirstName + '&ln=' + data.LastName + '&co=' + data.Country + '&a1=' + data.Address1 +
				'&cy=' + data.City + '&st=' + data.State + '&zp=' + data.ZipCode + '&oe=' + $scope.itemEnum;
	
			$scope.error = false;

			haHttpService.GET(requestUrl).then(
				function (response) {
					if (response.data && response.data.Success) {

						var url = response.data.postURL;
						if (ctaType > 0) {
							var referrerId="";
							switch (ctaType) {
								case 1:
									referrerId = $scope.scContent.preapprovalbannerctalink;
									break;
								case 2:
									referrerId = $scope.scContent.fullapplicationbannerctalink;
									break;
								case 7:
								case 8:
									referrerId = sessionStorage.getItem("referrerId") + "";
									sessionStorage.removeItem("referrerId");
									break;
							}
							url += '&referrerid='+referrerId;
						}
						window.location.href = url;
					} else {
						$scope.error = true;
					}
				},
				function () {
					$scope.error = true;
				}
			);
		});

		$scope.applyNow = function (isFullApp, e, itemEnum) {
			var firstName = '';
			var lastName = '';
			var countryText = '';
			var address1Text = '';
			var cityText = '';
			var zipText = '';

			var requestUrl = bccAcceptOfferUrl + '?IsFullApp=' + isFullApp;
			if (isFullApp) {
				var state;
				if ($scope.paymentform[stateEn] && $scope.paymentform[stateEn].$modelValue) {
					state = $scope.paymentform[stateEn].$modelValue;
				} else if ($scope.paymentform[stateEnText] && $scope.paymentform[stateEnText].$modelValue) {
					state = $scope.paymentform[stateEnText].$modelValue;
				}
				if (typeof $scope.paymentform[first] !== 'undefined') {
					firstName = $scope.paymentform[first].$modelValue;
				}
				if (typeof $scope.paymentform[last] !== 'undefined') {
					lastName = $scope.paymentform[last].$modelValue;
				}
				if (typeof $scope.paymentform[country] !== 'undefined') {
					countryText = $scope.paymentform[country].$modelValue;
				}
				if (typeof $scope.paymentform[address1] !== 'undefined') {
					address1Text = $scope.paymentform[address1].$modelValue;
				}
				if (typeof $scope.paymentform[city] !== 'undefined') {
					cityText = $scope.paymentform[city].$modelValue;
				}
				if (typeof $scope.paymentform[zip] !== 'undefined') {
					zipText = $scope.paymentform[zip].$modelValue;
				}
				if (typeof state === 'undefined') {
					state = '';
				}
				requestUrl += '&fn=' + firstName + '&ln=' + lastName + '&co=' + countryText + '&a1=' + address1Text + '&cy=' + cityText + '&st=' + state + '&zp=' + zipText;
			}

			requestUrl += '&oe=' + itemEnum;

			$scope.error = false;
			haHttpService.GET(requestUrl).then(
				function (response) {
					if (response.data && response.data.Success) {
						window.location.href = response.data.postURL;
					} else {
						$scope.error = true;
					}
				},
				function () {
					$scope.error = true;
				}
			);
        };

        $scope.acceptOffer = function () {
			haHttpService.GET(bccAcceptPreAppUrl);
            $scope.$modalClose();
        }

		$scope.decline = function () {
			haHttpService.GET(bccDeclineOffer);
            $scope.$modalClose();
		};

		$scope.closeModal = function () {
			haHttpService.GET(closeModal);
            $scope.$modalClose();
		};

		function getBccIPSCookie() {
			var checkCookie = readCookie('IPS');
			if (checkCookie !== undefined) {
				return checkCookie;
			}
			return null;
		};
		function readCookie(name) {
			name += '=';
			for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--) {
				if (!ca[i].indexOf(name)) {
					return ca[i].replace(name, '');
				}
			}
		};

	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haNitpModule', []);

	//nitp Service Offer Controller
	module.controller('nitpServiceOfferController', [
		'$scope',
		'haGlobals',
		'$window',
		'$location',
		'haCitiesSvc',
		'haHttpService',
		'$timeout',
		'haModal',
		'$filter',
		function ($scope, haGlobals, $window, $location, haCitiesSvc, haHttpService, $timeout, haModal, $filter) {

			//var query_string = {};
			//var query = $location.search().plan;

			$scope.nitpFormData = {};
			$scope.nitpFormData.homeAirport = '';
			$scope.nitpFormData.destinationAirport = '';


			$scope.nitpVM = {
				OfferDetails: null,
				DefaultOfferDetails: null,
				AccountNumber: ''
			};

			$scope.errorMessage = '';
			$scope.hasErrorMessage = false;

			haCitiesSvc.getNitpOrigins().then(function (data) {
				$scope.originCities = data;
			});

			$scope.selectTab = function (offerID) {
				$scope.errorMessage = '';
				$scope.hasErrorMessage = false;
				angular.forEach($scope.nitpVM.OfferDetails, function (offer) {
					if (offer.NIPassID === offerID) {
						$scope.nitpVM.OfferDetails[$scope.nitpVM.OfferDetails.indexOf(offer)].IsSelected = true;
						$scope.nitpFormData.plan = offer;
					}
					else {
						$scope.nitpVM.OfferDetails[$scope.nitpVM.OfferDetails.indexOf(offer)].IsSelected = false;
					}
				});
			};
			$scope.getHelpContent = function () {
				haModal('', {
					id: 'nitp-selectoffer-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			haGlobals('jsonofferDetailsVM', function (jsonofferDetailsVM) {

				$scope.nitpVM.OfferDetails = jsonofferDetailsVM.AvailableOffers;
				$scope.nitpVM.DisplayOffers = jsonofferDetailsVM.DisplayOffers;
				$scope.nitpVM.AccountNumber = jsonofferDetailsVM.AccountNumber;
			});

			$scope.submit = function () {

				var selectedOffer;
				var keepGoing = true;
				var hasError = false;
				angular.forEach($scope.nitpVM.DisplayOffers, function (defaultOffer) {
					if (defaultOffer.IsSelected && keepGoing) {
						selectedOffer = defaultOffer;
						keepGoing = false;
					}
				});

				var offerAvail = false;
				if (selectedOffer != null && selectedOffer.NIPassID === '4' && $scope.nitpFormData.homeAirport && $scope.nitpFormData.destinationAirport) {

					angular.forEach($scope.nitpVM.OfferDetails, function (offer) {

						if (selectedOffer.NIPassID === offer.NIPassID && ((offer.Origin === $scope.nitpFormData.homeAirport && offer.Dest === $scope.nitpFormData.destinationAirport) || (offer.Origin === $scope.nitpFormData.destinationAirport && offer.Dest === $scope.nitpFormData.homeAirport))) {
							offerAvail = true;
							if (offer.IsEligible && !offer.IsAvailable) {
								$scope.errorMessage = $window.nitpErrorMsg;
								hasError = true;
							}
							else if (!offer.IsEligible) {
								if (offer.NextEligibleDate !== '01/01/0001') {
									var purchaseDate = $filter('date')(new Date(offer.NextEligibleDate), 'MMM dd yyyy');
									$scope.errorMessage = $window.nitpPurchasedMsg + '  <b>' + purchaseDate + '</b>.';
								}
								else {
									$scope.errorMessage = $window.nitpErrorMsg;
								}
								hasError = true;
							}
						}
					});
				}
				else if (selectedOffer != null && +selectedOffer.NIPassID !== 4) {
					offerAvail = true;
					if (!selectedOffer.IsEligible && !selectedOffer.IsAvailable) {
						$scope.errorMessage = $window.nitpErrorMsg;
						hasError = true;
					}
					else if (!selectedOffer.IsEligible) {
						if (window.offer.NextEligibleDate !== '01/01/0001') {
							var purchaseDate = $filter('date')(new Date(offer.NextEligibleDate), 'MMM dd yyyy');/* what is offer? supposed to iterate, like above? */ // jshint ignore:line
							$scope.errorMessage = $window.nitpPurchasedMsg + '  <b>' + purchaseDate + '</b>.';
						}
						else {
							$scope.errorMessage = $window.nitpErrorMsg;
						}
					}

					// if (selectedOffer.IsRenewalEligible) {
					//     console.log("Renewal");
					// }
				}
				else {
					$scope.errorMessage = $window.nitpErrorMsg;
					hasError = true;
				}
				if (!offerAvail) {
					$scope.errorMessage = $window.nitpErrorMsg;
					hasError = true;
				}
				if (!hasError) {
					haHttpService.POST('/Program/NITPPurchase/PurchaseSelectedNIOffer', {
						offerID: selectedOffer.NIPassID,
						orig: $scope.nitpFormData.homeAirport,
						dest: $scope.nitpFormData.destinationAirport
					})
					.then(function (response) {
						if (response.data.IsSuccess) {
							$window.location = response.data.RedirectURL;
						}
					});
				}
				else {
					$scope.hasErrorMessage = true;
					angular.element('html,body').animate({ scrollTop: 100 }, 'fast');
				}
			};


			$scope.$watch('nitpFormData.homeAirport', function (newVal) {
				if (!newVal) {
					return;
				}

				haCitiesSvc.getNitpDestinations(newVal).then(function (data) {
					$scope.destinationCities = data;
				});
			});


		}

	]);

	//nitp Landing Controller
	module.controller('nitpLandingController', [
		'$scope',
		'haGlobals',
		'$window',
		'$location',
		'haCitiesSvc',
		'haHttpService',
		function ($scope, haGlobals, $window, $location, haCitiesSvc, haHttpService) {

			var error = function (err) {
				console.error(err);
			};

			$scope.showExtraFeatures = false;
			$scope.IsEligible = false;

			haHttpService.POST('/Program/NITPPurchase/GetNIOfferDetails')
			.then(function (response) {
				if (response.data.IsSuccess) {
					$.extend($scope, response.data);
				} else {
					error(response);
				}
			}, error);

			$scope.selectNIOffer = function (selectedOfferID) {
				haHttpService.POST('/Program/NITPPurchase/SetSelectedNIOffer', { offerID: selectedOfferID })
				.then(function (response) {
					if (response.data.RedirectURL != null) {
						$window.location = response.data.RedirectURL;

					}
				});

			};
		}
	]);

	module.controller('nitpPaymentController', [
		'$scope',
		'$rootScope',
		'haGlobals',
		'$window',
		'$location',
		'haCitiesSvc',
		'haHttpService',
		'haModal',
		'$timeout',
        '$filter',
		function ($scope, $rootScope, haGlobals, $window, $location, haCitiesSvc, haHttpService, haModal, $timeout, $filter) {

			$scope.errorMessage = '';
			$scope.nitpVM = {
				OfferDetails: null
			};
			$scope.submitting = false;

			$scope.getHelpContent = function () {
				haModal('', {
					id: 'nitp-payment-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.submit = function ($event) {
				// prevent submit from happening multiple times
				if ($scope.IsPaymentProcessing) {
					return false;
				}

				$scope.IsPaymentProcessing = true;

				if ($('#PaymentInterstitial-modal').length === 0) {

					var data = $('#PaymentInterstitial').html();

					haModal({
						id: 'PaymentInterstitial-modal',
						backdrop: 'true',
						template: data,
						modalLock: true
					});

					$scope.interstitialDynamicResize();
				}

				$event.preventDefault();
				$scope.form = $('#nitppaymentform');
				$timeout(function() {
					$scope.processPayment();
				}, 0);
			};

			$scope.isNitpPurchaseValid = function (response) {
			    var hasError = false;
			    angular.forEach(response.data.AvailableOffers, function (offer) {
			        if (offer.NIPassID === $scope.nitpVM.OfferDetails.NIPassID) {
			            if (!offer.IsEligible) {
			                if (offer.NextEligibleDate !== '01/01/0001') {
			                    var purchaseDate = $filter('date')(new Date(offer.NextEligibleDate), 'MMM dd yyyy');
			                    $scope.errorMessage = $window.nitpPurchasedMsg + '  <b>' + purchaseDate + '</b>.';			                  
			                }
			                else
			                {
			                    $scope.errorMessage = $window.nitpErrorMsg;
			                }

			                hasError = true;
			                $scope.isDuplicatePurchase = true;
			            }
			        }
			    });
			    return hasError;
			};

			$scope.interstitialDynamicResize = function () {
				$('.ha-modal#PaymentInterstitial-modal .modalContainer').css({
					'width': $(window).width(),
					'height': $(window).height(),
					'padding-top': $(window).height() * 0.20
				});
			};

			$scope.showTerms = function () {
				haModal('', {
					id: 'terms-and-conditions',
					backdrop: 'true',
					template: angular.element('.showTerms')
				});
			};

			$scope.processPayment = function () {

				userdata.load_data('user_data');/* what is userdata? */ // jshint ignore:line

				//var usrdata = user_data.value;
				////Added null and count check
				//if (user_data != null && user_data.length != null && user_data.length > 0) {
				//	usrdata = user_data[0].value;
				//}
				var url = '/Program/NITPPurchase/Payment';
				var formData = $($scope.form).serialize();
				haHttpService.POST(url, formData, {
					headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
				}).success(function (response) {
					if (response != null && response.data != null && response.data.IsSuccess) {
						$window.location = response.data.RedirectURL;
						$timeout(function () {
							$scope.IsPaymentProcessing = false;
							$scope.$emit('closeModal');
							$('body, html').animate({
								scrollTop: 300
							}, 'slow');
						}, 20000);
					} else if (response != null && response.HasServiceError) {
						$scope.IsPaymentProcessing = false;
						$scope.errorMessage = response.TranslateAllServiceErrors[0] != null ? response.TranslateAllServiceErrors[0] : ServiceErrors[0];
						$scope.$emit('closeModal');
						$('body, html').animate({
							scrollTop: 300
						}, 'slow');
					}
					else {
						$scope.IsPaymentProcessing = false;
						if (response.data.ServiceErrors != null) {
							$scope.errorMessage = response.data.ServiceErrors[0];
						}
						if (response.data.RedirectURL != null && response.data.RedirectURL.indexOf('programs/nitp/confirmation') <= -1) {
							$window.location = response.data.RedirectURL;
						}

						$scope.$emit('closeModal');
						$('body, html').animate({
							scrollTop: 300
						}, 'slow');
					}

				}).error(function (response) {
					$scope.IsPaymentProcessing = false;
					$scope.$emit('closeModal');
					$('body, html').animate({
						scrollTop: 300
					}, 'slow');
				});
			};

			haGlobals('nitpPaymentJson', function (nitpPaymentJson) {

				$scope.nitpVM.OfferDetails = nitpPaymentJson.OfferDetail;

			});

			// for PCI Payment Authorization
			$scope.haPaymentTypes = {
				paymentMethod: 'creditDebit'
			};

			$scope.CDESubmit = function () {

			    $scope.form = $('#nitppaymentform');

			    var url = '/Program/NITPPurchase/GetNIOfferDetails';
			    haHttpService.POST(url).then(function (response) {
			        var hasError = $scope.isNitpPurchaseValid(response);
			        if (hasError === false) {
			            $rootScope.$broadcast('CDESubmit');
			        }
			        else {
			            $scope.$emit('closeModal');
			            $('body, html').animate({
			                scrollTop: 300
			            }, 'slow');
			        }
			    })
			};

			$scope.$on('CDEPaymentSubmitted', function() {

				var data = $('#PaymentInterstitial').html();

				haModal({
					id: 'PaymentInterstitial-modal',
					backdrop: 'true',
					template: data,
					modalLock: true
				});

				$scope.interstitialDynamicResize();
			});

			$scope.$on('CDEPaymentSubmitError', function() {
				$scope.$emit('closeModal');
				$timeout(function() {
					$('html, body').animate({ scrollTop: $('#formIFrame').offset().top - 50 }, 'fast');
				}, 100);
			});
			// end PCI

		}
	]);

	module.controller('nitpConfirmController', [
		'$scope',
		'haGlobals',
		'$window',
		'$location',
		'haCitiesSvc',
		'haHttpService',
		'haModal',
		function ($scope, haGlobals, $window, $location, haCitiesSvc, haHttpService, haModal) {

			$scope.nitpVM = {
				OfferDetails: null,
				OrderID: ''
			};
			haGlobals('nitpConfirmJson', function (nitpConfirmJson) {
				$scope.nitpVM.OfferDetails = nitpConfirmJson.OfferDetail;
				$scope.nitpVM.OrderID = nitpConfirmJson.OrderID;
				$scope.nitpVM.RequiredSavePayment = nitpConfirmJson.RequiredSavePayment;
				$scope.nitpVM.CCMessage = nitpConfirmJson.CCMessage;
				$scope.nitpVM.CCSuccess = nitpConfirmJson.CCSuccess;

			});

			$scope.getHelpContent = function () {
				haModal('', {
					id: 'nitp-Confirmation-help',
					backdrop: 'true',
					template: angular.element('.getHelpContent')
				});
			};

			$scope.showTerms = function () {
				haModal('', {
					id: 'terms-and-conditions',
					backdrop: 'true',
					template: angular.element('.showTerms')
				});
			};

			$scope.showFAQ = function () {
				haModal('', {
					id: 'nitp-faq',
					backdrop: 'true',
					template: angular.element('.showFAQ')
				});
			};
		}
	]);

})(angular);
;
(function (angular) {

    'use strict';

    var module = angular.module('haMyDashboardModule', ['ui.router']);

    if ((/^\/my-account\/?$/i).test(location.pathname)) {
        module.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {

            $stateProvider
			.state('changerequest', {
			    url: '/changerequest', templateProvider: ['haConfig', function (haConfig) {
			        return haConfig.getTemplateUrlWithInclude('ha-mydashboard-changerequest.html');
			    }]
			})
			.state('contactsalesrep', {
			    url: '/contactsalesrep', templateProvider: ['haConfig', function (haConfig) {
			        return haConfig.getTemplateUrlWithInclude('ha-mydashboard-salesrepcontact.html');
			    }]
			})
			.state('changeaccount', {
			    url: '/changeaccount', templateProvider: ['haConfig', function (haConfig) {
			        return haConfig.getTemplateUrlWithInclude('ha-mydashboard-changeaccount.html');
			    }]
			});

            $urlRouterProvider.otherwise('/dashboard');
        }])
		.run(['$rootScope', function ($rootScope) {
		    $rootScope.$on('$stateChangeSuccess', function () {
		        $('html, body').animate({ scrollTop: $('[ui-view]').offset().top }, 200);
		    });
		}]);
    }

    module.controller('haMyDashboardCtrl', ['$scope', '$http', '$timeout', '$q', '$state', '$filter', '$window', '$log', 'haMyAccountAPI', '$rootScope', function ($scope, $http, $timeout, $q, $state, $filter, $window, $log, haMyAccountAPI, $rootScope) {

        $scope.$on('$stateChangeSuccess', function (event, toState) {
            // Custom Event for Analytics
            setTimeout(function () {
                document.body.dispatchEvent(new CustomEvent('MyDashBoard'));
            });

            // prevent non-corporate users from visting corporate forms/routes
            var isCorporateUser = $rootScope.isLoggedIn && $rootScope.corpAccTypes.indexOf($rootScope.user.accountType) > -1;
            if (!isCorporateUser && (toState.name === 'changeaccount' || toState.name === 'contactsalesrep' || toState.name === 'changerequest')) {
                $window.location.href = "/my-account";
            }

            // get view model data from GET request ONLY when that state is requested
            if (toState.name === 'contactsalesrep' && !$scope.salesRepContact) {
                $scope.loading = true;
                $scope.showSalesRepForm = false;
                haMyAccountAPI.getcontactsalesrepform().then(function (response) {
                    $scope.salesRepContact = response.data;
                    $scope.loading = false;
                    $scope.showSalesRepForm = true;
                }, function () {
                    $log.error('There was an error while retreiving /myaccount/mydashboardform/getcontactsalesrepform');
                });
            } else if (toState.name === 'changerequest' && !$scope.changeRequest) {
                $scope.loading = true;
                haMyAccountAPI.getchangerequestform().then(function (response) {
                    $scope.changeRequest = response.data;
                    $scope.loading = false;
                }, function () {
                    $log.error('There was an error while retreiving /myaccount/mydashboardform/getchangerequestform');
                });
            } else if (toState.name === 'changeaccount' && !$scope.corpWebAccountUpdate) {
				$scope.loading = true;
				$scope.ready = false;
                haMyAccountAPI.getcorpwebaccountupdate().then(function (response) {
                    $scope.corpWebAccountUpdate = response.data;
                    $scope.changeAcctModel = {};
                    $scope.paymentModel = {};
                    $scope.changeAcctModel.DateOfChangeRequest = $filter('date')(new Date(), 'shortDate');
                    $scope.changeAcctModel.FromEmail = $scope.corpWebAccountUpdate.FromEmail;
                    $scope.changeAcctModel.CorporateAccountNumber = $scope.corpWebAccountUpdate.CorporateAccountNumber;
                    $scope.changeAcctModel.AccountName = $scope.corpWebAccountUpdate.AccountName;
					$scope.ready = true;
					$scope.loading = false;
                }, function () {
                    $log.error('There was an error while retreiving /myaccount/mydashboardform/getcorpwebaccountupdate');
                });
            }
        });

        $scope.AddHyphenString = function (strValue) {
            if (strValue.length == 7) {
                return strValue.replace(/(\d{3})(\d{4})/, "$1-$2");
            }
            else {
                return strValue;
            }
        };
        // Custom Event for Analytics
        setTimeout(function () {
            document.body.dispatchEvent(new CustomEvent('MyDashBoard'));
        });

        var oldUrl = window.location.href;
        var index = 0;
        var newUrl = oldUrl;
        index = oldUrl.indexOf('?');
        if (index === -1) {
            index = oldUrl.indexOf('#');
        }
        if (index !== -1) {
            newUrl = oldUrl.substring(0, index) + '/';
        }
        $scope.URLHome = newUrl;
        $scope.changeRequestSubmitted = $scope.changeRequestError = $scope.salesRepContactSubmitted = $scope.salesRepContactError = false;

        var scrollUp = function () {
            $('body, html').animate({ scrollTop: 500 }, 'slow');
        };

        $scope.changeRequestModel = {};
        $scope.$watch('changeRequestModel.DepartTravelDate', function (nv) {
            if (nv > $scope.changeRequestModel.ReturnTravelDate) {
                $scope.changeRequestModel.ReturnTravelDate = undefined;
            }
        });
        $scope.$watch('changeRequestModel.ReturnTravelDate', function (nv) {
            if (nv < $scope.changeRequestModel.DepartTravelDate) {
                $scope.changeRequestModel.DepartTravelDate = undefined;
            }
        });
		$scope.submitChangeRequest = function (changeRequestModel) {
			var fd = new FormData();
			var firstFileAttachment = document.querySelector('input[name="FirstFileAttachment"]').files[0];
			var secondFileAttachment = document.querySelector('input[name="SecondFileAttachment"]').files[0];
			var thirdFileAttachment = document.querySelector('input[name="ThirdFileAttachment"]').files[0];
			var fourthFileAttachment = document.querySelector('input[name="FourthFileAttachment"]').files[0];
			var fifthFileAttachment = document.querySelector('input[name="FifthFileAttachment"]').files[0];
			if (firstFileAttachment) {
				changeRequestModel.FirstFileAttachment = firstFileAttachment;
			}
			if (secondFileAttachment) {
				changeRequestModel.SecondFileAttachment = secondFileAttachment;
			}
			if (thirdFileAttachment) {
				changeRequestModel.ThirdFileAttachment = thirdFileAttachment;
			}
			if (fourthFileAttachment) {
				changeRequestModel.FourthFileAttachment = fourthFileAttachment;
			}
			if (fifthFileAttachment) {
				changeRequestModel.FifthFileAttachment = fifthFileAttachment;
			}
			for (var key in changeRequestModel) {
				fd.append(key, changeRequestModel[key]);
            }
			var config = { headers: { 'Content-Type': undefined } };

			console.log(fd);

			$scope.loading = true;

			$http.post('/myaccount/mydashboardform/UpdateChangeRequest', fd, config).then(
				function (response) {
					if (response.data.IsFileSizeError || response.data.IsFileTypeError) {
						$scope.fileAttachmentNum = response.data.fileAttachmentNum;
						$scope.isFileSizeError = response.data.IsFileSizeError;
						$scope.isFileTypeError = response.data.IsFileTypeError;
					} else {
						if (response && response.data.Success) {
							$scope.changeRequestSubmitted = true;
							$scope.changeRequestModel = {};
							scrollUp();
						} else {
							$scope.changeRequestError = true;
							scrollUp();
						}
					}
				},
				function () {
				    $scope.changeRequestError = true;
				    scrollUp();
				}
			)['finally'](function () {
			    $scope.loading = false;
			});
        };

        $scope.submitSalesRepContact = function (salesRepModel) {
            $scope.loading = true;

            $http.post('/myaccount/mydashboardform/sendcontactsalesrepformemail', JSON.stringify(salesRepModel)).then(
				function (response) {
				    if (response && response.data.Success) {
				        $scope.salesRepContactSubmitted = true;
				        $scope.salesRepModel = {};
				        scrollUp();
				    } else {
				        $scope.salesRepContactError = true;
				        scrollUp();
				    }
				},
				function () {
				    $scope.salesRepContactError = true;
				    scrollUp();
				}
			)['finally'](function () {
			    $scope.loading = false;
			});
        };


        $scope.submitChangeCorpAccountRequest = function (data) {
        	var fd = new FormData();
        	var firstFileAttachment = document.querySelector('input[name="FirstFileAttachment"]').files[0];
        	var secondFileAttachment = document.querySelector('input[name="SecondFileAttachment"]').files[0];
        	var thirdFileAttachment = document.querySelector('input[name="ThirdFileAttachment"]').files[0];
        	if (firstFileAttachment) {
        		data.FirstFileAttachment = firstFileAttachment;
        	}
        	if (secondFileAttachment) {
        		data.SecondFileAttachment = secondFileAttachment;
        	}
        	if (thirdFileAttachment) {
        		data.ThirdFileAttachment = thirdFileAttachment;
        	}
            if (Object.keys(data).length === 4) {
                //'Please enter at least one field to change'
                $window.alert($scope.corpWebAccountUpdate.corpWebAccountUpdateSitecoreVM.AlertNoFieldChanged);
                scrollUp();
                return;
            }
            if (Object.keys(data).length > 5) {
                //'You have modified more than one field.'
                if (!$window.confirm($scope.corpWebAccountUpdate.corpWebAccountUpdateSitecoreVM.AlertMoreThanOneFieldChanges)) {
                    return;
                }
            }
            if (Object.keys($scope.paymentModel).length > 0) {
                //'You have modified payment fields.'
                if (!$window.confirm($scope.corpWebAccountUpdate.corpWebAccountUpdateSitecoreVM.AlertFormOfPaymentFieldsChanged)) {
                    return;
                }
            }

            // Add all data values to the form data
            for (var key in data) {
            	fd.append(key,data[key]);
            }

            $scope.loading = true;

            var config = { headers: {'Content-Type': undefined} };

            $http.post('/myaccount/mydashboardform/updatecorpwebaccount', fd, config).then(
				function (response) {
				    if (response && response.data.Success) {
				        $scope.changeCorpAcctSubmitted = true;
				        $scope.changeAcctModel = {};
				    } else {
				        $scope.changeCorpAccountError = true;
				        scrollUp();
				    }
				},
				function () {
				    $scope.changeCorpAccountError = true;
				    scrollUp();
				}
			)['finally'](function () {
			    $scope.loading = false;
			});
        };

        $scope.selectedAvatar = {};

        $scope.$on('$closeCustomDropdown', function () {
            if ($scope.selectedAvatar) {
                $http.post('/myaccount/mydashboard/saveprofileavatar', JSON.stringify({
                    AvatarId: $scope.selectedAvatar.ID,
                    AvatarUrl: $scope.selectedAvatar.ProfileImageSrc
                }));

            }
        });


        $scope.ccYears = (function (a, b) {
            while (a--) {
                b[a] = a + (new Date()).getFullYear();
            }
            return b;
        })(11, []);

        $scope.$on('$selectedAvatarURL', function (event, avatar) {
            if (avatar) {
                $scope.selectedAvatar = avatar;
                //   $http.post('/myaccount/mydashboard/saveprofileavatar', JSON.stringify({ AvatarId: avatar.ID, AvatarUrl: avatar.ProfileImageSrc }));
            }
        });

        // display cc offer for new users as long as the preapproved offer isn't available
        var preApprovedOfferExists = angular.element('#barclaysModalTemplate').length > 0;
        var newuserOfferExists = angular.element('#newUserCCOfferTemplate').length > 0;

        if (!preApprovedOfferExists && newuserOfferExists && $rootScope.$language === 'en') {
            haModal('', {
                id: 'newuserccoffer',
                backdrop: 'true',
                modalLock: false,
                scope: $scope,
                template: angular.element('#newUserCCOfferTemplate')
            });
        }

    }]);

})(angular);
;
(function (angular) {

	// Ha Hotels Add On
	// --------------------------------------------
	//
	// * **Class:** HaAffiliateCtrl
	// * **Author:** Mano
	//
	// This module handles the MVC View Model for the Affiliate Form

	'use strict';

	var module;
	module = angular.module('haAffiliateModule', []);
	module.controller('haAffiliateCtrl', [
		'$log',
		'$scope',
		'$rootScope',
		'haHttpService',
		'$window',
		function ($log, $scope, $rootScope, haHttpService, $window) {

			$scope.VM = {};
			$scope.VM.affiliatePromoCode = '';
			$scope.VM.isErrorResponse = false;
			$scope.VM.errorMessage = '';

			var error = function (err) {
				$log.error('haAffiliateCtrl', err);
			};

			$scope.submitForm = function () {
				$scope.VM.isErrorResponse = false;
				$scope.VM.errorMessage = '';
				haHttpService.POST('/Program/Affiliate/ValidateAffiliate', {promoCode: $scope.VM.affiliatePromoCode})
				.then(function (response) {
					$log.debug(response.data);
					if (response.data.IsSuccess && response.data.RedirectURL != null) {
						response.data.RedirectURL = response.data.RedirectURL;
						$window.location = response.data.RedirectURL;
					} else {
						$scope.VM.isErrorResponse = true;

						if (response.data.ServiceErrors != null) {
							$scope.VM.errorMessage = response.data.ServiceErrors[0];
						}
						else {
							$scope.VM.errorMessage = 'Error While Processing Request';
						}
						error(response);
					}
				}, error);
			};
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haPaxModule', ['ui.router', 'duScroll', 'ngSanitize', 'haGeoDataModule']);

	if (location.pathname.toLowerCase().indexOf('/book/pax') > -1) {
		module.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
			$urlRouterProvider.otherwise(function ($injector) {
				var haGlobals = $injector.get('haGlobals');
				var rootScope = $injector.get('$rootScope');
				var state = $injector.get('$state');
				haGlobals(['PaxVM'], function (PaxVM) {
					if (!PaxVM.IsLoggedIn) {
						state.go('passenger.x', { id: 1 });
					} else {
						if (PaxVM.IsCorporate) {
							state.go('passenger.corporate.x', { id: 1, replace: true });
						} else {
							state.go('passenger.x', { id: 1 });
						}
					}
				});
			});

			var ctrl = ['$rootScope', '$scope', '$state', function ($rootScope, $scope, $state) {

				window.digitalDataLoaded.then(function () {
					setTimeout(function () {
						document.body.dispatchEvent(new CustomEvent('PassengerPage', { 'detail': { pageName: window.digitalData.page.pageInfo.name } }));
					});
				});

				// Forward and Backward events - $state.go (notify: false) called inside prevTraveler and nextTraveler
				$scope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {

					var from = parseInt(fromParams.id, 10);
					var to = parseInt(toParams.id, 10);

					// Backward
					if ((fromState.name === 'passenger.x' && from && to && from > to) || (fromState.name === 'passenger.contact')) {

						// Custom Event for Analytics
						setTimeout(function () {
							document.body.dispatchEvent(new CustomEvent('PassengerPage', { 'detail': { pageName: window.digitalData.page.pageInfo.name } }));
						});
						var custom = {
							pageName: window.digitalData.page.pageInfo.name + ':passenger:' + to,
							pageURL: [location.origin, location.pathname, location.search, '#/passenger/' + to].join('')
						};
						window.digitalDataLoaded.then(function () {
							setTimeout(function () {
								document.body.dispatchEvent(new CustomEvent('PassengerPage', { 'detail': custom }));
							});
						});

						$scope.prevTraveler();
					}

					// Forward
					if (fromState.name === 'passenger.x' && from && to && from < to) {
						$scope.nextTraveler();
					}

					// To contact or back from it
					if (toState.name !== 'passenger.contact' && fromState.name !== 'passenger.contact' && toState.name !== 'passenger.corporate.x') {
						event.preventDefault();
					}

					// Back from contact make sure all modals close
					if (toState.name === 'passenger.x' && fromState.name === 'passenger.contact') {
						$scope.resetErrors();
						$rootScope.$broadcast('$modalCancel');
					}
				});
			}];

			$stateProvider
				.state('passenger', {
					abstract: true,
					url: '/passenger',
					controller: ctrl,
					template: '<div ui-view></div>'
				})
				.state('passenger.x', {
					url: '/{id:int}',
					templateProvider: ['haConfig', function (haConfig) {
						return haConfig.getTemplateUrlWithInclude('Book/Pax/ha-pax.html');
					}]
				})
				.state('passenger.corporate', {
					url: '^/corporate',
					controller: 'PaxCtrl',
					template: '<div ui-view></div>'
				})
				.state('passenger.corporate.x', {
					url: '/{id:int}',
					controller: 'haCorporateTravelersCtrl',
					templateProvider: ['haConfig', function (haConfig) {
						return haConfig.getTemplateUrlWithInclude('my-account/ha-corporate-travelers.html');
					}]
				})
				.state('passenger.contact', {
					url: '^/contact',
					templateProvider: ['haConfig', function (haConfig) {
						return haConfig.getTemplateUrlWithInclude('Book/Pax/ha-pax-contact.html');
					}]
				});

		}])
			.run(['$rootScope', '$stateParams', '$state', function ($rootScope, $stateParams, $state) {
				$rootScope.$state = $state;
				$rootScope.$stateParams = $stateParams;
			}]);
	}

	module.controller('PaxCtrl', ['$scope', '$rootScope', '$stateParams', '$state', 'haHttpService', '$document', '$timeout', 'haGlobals', 'haModal', 'haUtils', 'haConfig', 'haPassengersService', 'haGeoDataSvc', 'haPaxService', '$window', 'haInterstitialAPI', function ($scope, $rootScope, $stateParams, $state, haHttpService, $document, $timeout, haGlobals, haModal, haUtils, haConfig, $pax, geo, haPaxService, $window, haInterstitialAPI) {

		if (haUtils.inIframe()) promptIframeBreakout();

		$scope.genders = [{
			name: $rootScope.$scs('PassengerInfo.malelabel'),
			value: 'M'
		}, { name: $rootScope.$scs('PassengerInfo.femalelabel'), value: 'F' }];
		$scope.phoneTypes = [{
			name: $rootScope.$scs('PassengerInfo.HomeText'),
			value: 1
		}, { name: $rootScope.$scs('PassengerInfo.WorkText'), value: 2 }, {
			name: $rootScope.$scs('PassengerInfo.MobileText'),
			value: 3
		}];
		$rootScope.isBooking = true;

		$scope.months = (window.PaxVM.DOBMonthDropDown) ? window.PaxVM.DOBMonthDropDown.map(function (m) {
			return { name: m.Name, value: m.Value };
		}) : [{ name: 'January', value: '1' }, { name: 'February', value: '2' }, { name: 'March', value: '3' }, {
			name: 'April',
			value: '4'
		}, { name: 'May', value: '5' }, { name: 'June', value: '6' }, { name: 'July', value: '7' }, {
			name: 'August',
			value: '8'
		}, { name: 'September', value: '9' }, { name: 'October', value: '10' }, {
			name: 'November',
			value: '11'
		}, { name: 'December', value: '12' }];
		if ($scope.months.length > 12) {
			$scope.months.shift();
		}

		$scope.days = (function (a, b) {
			while (a--) {
				b[a] = (a + 1).toString();
			}
			return b;
		})(31, []);
		$scope.years = (function (a, b) {
			while (a--) {
				b[a] = (a + (new Date()).getFullYear() - 100).toString();
			}
			return b;
		})(101, []).reverse();
		$scope.IsAffiliateBooking = window.PaxVM.IsAffiliateBooking;

		$scope.$pax = $pax;
		$scope.Passengers = $scope.$pax.passengers = [];
		$scope.selectedTravelers = haPaxService.selectedTravelers();
		$scope.isCountryCodeNotAvailable = false;
		$scope.countryCodeWarningText = "";
		$scope.tempTraveler = [];
		$scope.grandTotal = 0;
		$scope.isPageError = false;
		$scope.ContactInfo = '';
		$scope.paxRules = [];
		$scope.showInfantHelp = false;

		$scope.Country = [];
		$scope.CountryFlag = false;
		$scope.DupeEmailFlag = '';
		$scope.IsServiceErrors = false;
		$scope.IsInValidHMNumber = false;
		$scope.ServiceErrorMessage = '';
		$scope.errordescription = '';
		$scope.PhoneReqired = false;
		$scope.IsInfantMaxError = false;
		$scope.IsInfantAdultError = false;
		$scope.IsEditingError = false;
		$scope.isDisableName = false;
		$scope.isLoggedinEtco = false;
		$scope.taxDetails = false;
		$scope.isInfantEnabled = false;
		$scope.enablePassengerTripSummary = true;
		$scope.enableTCR = $window.enableTCR;
		$scope.travelCoordinator = false;

		// Get State
		haGlobals(['PaxVM', 'defaultPhoneCountryCode', 'ETCOResponseModel', 'countryData'], function (PaxVM, defaultPhoneCountryCode, ETCOResponseModel, countryData) {

			$.extend($scope, PaxVM);

			// Fetch list of Countries
			$scope.countries = geo.setCountryData(countryData);

			// Phone Defaults
			phonesInit($scope.PhoneDetails);


			$rootScope.isTargetBcusEligible(); // Checks eligibility and toggles BCUS Credit Card offer on/off


			$scope.countryCodeDropDown = $scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.CountryCodeDropDown;
			$rootScope.phoneCount = $scope.PaxContactInfo.PhoneDetailsVM.PhoneDetails.reduce(function (prev, curr) {
				return Number(!!curr.Number) + prev;
			}, 0) || 1;

			// Travel Coordinator Init
			$scope.tcInfo = $scope.PaxContactInfo.TravelCoordinatorInfo;
			if ($scope.tcInfo !== null && ($scope.tcInfo.TravelCoordinatorPhoneDetails === null || !$scope.tcInfo.TravelCoordinatorPhoneDetails.Number)) {
				$scope.tcInfo.TravelCoordinatorPhoneDetails = {
					CountryCode: defaultPhoneCountryCode,
					Number: '',
					Type: 1
				};
			} else {
				$scope.travelCoordinator = true;
				$scope.tcInfo.TravelCoordinatorPhoneDetails.Type = ['Home', 'Work', 'Mobile'].indexOf($scope.tcInfo.TravelCoordinatorPhoneDetails.Type);
			}

			// ETCO
			if (ETCOResponseModel && ETCOResponseModel.Passengers && ETCOResponseModel.Passengers.length === 1 && !ETCOResponseModel.IsGAFTravelCredit) {
				$scope.isDisableName = true;
				$scope.isLoggedinEtco = true;
				angular.extend($scope.Passengers[0], ETCOResponseModel.Passengers[0]);
			}

		});

		$scope.countryPhoneDropdown = geo.getPhoneCountryCodes();

		// get phoneRegex by CountryCode
		$scope.phoneRegexByCC = function (cc) {
			var country = geo.lookupCountryByCode(cc);
			if (country !== null) {
				return geo.getPhoneNumberRegex(country.Key);
			}
			return /.*/;
		};

		// Trip Summary
		$scope.TripSummary.SelectedShuttleDetails = null;
		$scope.TripSummary.IsAirportShuttleSelected = false;
		$scope.TripSummary.TotalPremiumSeatAmount = 0;
		$scope.TripSummary.UnselectedExtraComfortSeatsDowngradeAmount = 0;
		$scope.PricingType = $scope.TripSummary.PricingType;
		$scope.currency = $scope.TripSummary.currency;
		$scope.totalPaxCount = parseInt($scope.TripSummary.PaxCount, 10);
		$scope.IsAllPaxUpdated = true;


		//////////////////
		// INLINE STUFF //

		// These is ONLY for the sticky header
		var adultCount = parseInt($scope.TripSummary.AdultCount, 10) || 0;
		var childCount = parseInt($scope.TripSummary.ChildCount, 10) || 0;
		var infantCount = parseInt($scope.TripSummary.InfantCount, 10) || 0;
		var i;

		for (i = 0; i < adultCount; i++) {
			$scope.$pax.add({ type: 'Adult' });
		}
		for (i = 0; i < childCount; i++) {
			$scope.$pax.add({ type: 'Child' });
		}
		for (i = 0; i < infantCount; i++) {
			$scope.$pax.add({ type: 'Infant' });
		}


		//////////////////////////
		// NAVIGATION FUNCTIONS //
		$scope.setNextForm = function (id) {
			var frm = angular.element('ol.pax li.active form, #contactInfoForm');
			//var frm = angular.element('ol.pax li form[name="passenger' + id + 'EditForm"], #contactInfoForm');
			if (frm.length) {
				$scope.currentForm = frm.scope()[frm.attr('name')];
				$scope.currentForm.$setPristine();
				$timeout(function () {
					frm.find('input').first().focus().select();
				}, 1200);
			}
		};

		$scope.nextTraveler = function () {

			var index = $stateParams.id - 1;
			var traveler = $scope.Passengers[index];
			
			if (!validateKTNs()) {
				return;
			}

			if ($scope.currentForm.$invalid) {
				$timeout(function () {
					$scope.currentForm.$validate($scope.currentForm);
				}, 0);
				return;
			}

			$scope.IsServiceErrors = $scope.IsInValidHMNumber = $scope.DuplicateInfoError = $scope.DuplicateKTNError = false;
			$scope.ServiceErrorMessage = '';

			// Right now resave everytime TODO: sort out session to prevent an unnecessary save
			if (true || $scope.currentForm.$dirty) {

				resetMilesOrEmail(traveler);
				traveler.saving = true;

				$scope.$pax.addeditpax(traveler).success(function (result) {

					if (handleExceptions(result, 'haPassengersAPI-addeditpax')) {
						return;
					}

					$scope.showInfantHelp = (result.Passengers) ? result.Passengers.some(function (pax) {
						return pax.PassengerType === 'Infant';
					}) : false;

					if (result.ErrorMessage) {
						$scope.IsServiceErrors = true;
						$scope.ServiceErrorMessage = result.ErrorMessage;
						traveler.saving = false;
						$('body, html').animate({ scrollTop: 0 }, 'fast');
						return;

					} else if (result.DoBCheckMessagesList.length > 0) {
						$scope.paxRules = result.DoBCheckMessagesList;
						$scope.AllowBooking = result.AllowBooking;
						$scope.showInfantHelp = true;
						$('body, html').animate({ scrollTop: 0 }, 'fast');
					} else {
						$scope.paxRules = [];
						$scope.AllowBooking = true;
						$scope.showInfantHelp = false;
					}

					if (result.DuplicatFirstAndLastNameError) {
						$scope.DuplicateInfoError = true;
						traveler.saving = false;
						$('body, html').animate({ scrollTop: 0 }, 'fast');
						return;
					}
					else if (result.DuplicateKTNError) {
						$scope.DuplicateKTNError = true;
						traveler.saving = false;
						$('body, html').animate({ scrollTop: 0 }, 'fast');
						return;
					}
					else {
						traveler.DuplicateEnrollEmail = result.Passengers[index].DuplicateEnrollEmail;
						traveler.IsValidHMNumber = result.Passengers[index].IsValidHMNumber;

						if (!traveler.IsValidHMNumber) {
							$scope.IsInValidHMNumber = true;
							traveler.saving = false;
							$('body, html').animate({ scrollTop: 0 }, 'fast');
							return;
						}

						$scope.Country = result.InternationalCountryListForMessage;

						if ($scope.Country.length > 0) {
							$scope.CountryFlag = true;
						}

						checkDuplicateEmail(traveler, true);
						//if the check returned a duplicate, stop
						if ($scope.duplicateEmailError || $scope.ageLimitError) {
							traveler.saving = traveler.saveSuccessDelayActive = false;
							return;
						}
					}

					traveler.Type = result.Passengers[index].Type;
					// if you're trying to go back and edit a pax, replace the data, dont add another
					if ($scope.selectedTravelers.get().length === index) {
						$scope.selectedTravelers.get().push(traveler);
					} else {
						$scope.selectedTravelers.get()[index] = traveler;
					}

					if (result.PassengerTripSummary) {
						$scope.PassengerTripSummary = result.PassengerTripSummary;
					}

					// Save transition
					traveler.saveSuccessDelayActive = true;

					var nextId = $stateParams.id + 1;
					if ($stateParams.id < $scope.Passengers.length) {
						var custom = {
							pageName: window.digitalData.page.pageInfo.name + ':passenger:' + nextId,
							pageURL: [location.origin, location.pathname, location.search, '#/passenger/' + nextId].join('')
						};
						window.digitalDataLoaded.then(function () {
							setTimeout(function () {
								// Custom Event for Analytics
								document.body.dispatchEvent(new CustomEvent('PassengerPage', { 'detail': custom }));
							});
						});

						$state.go('passenger.x', { id: nextId }, { notify: false });
					} else {
						var custom = {
							pageName: window.digitalData.page.pageInfo.name + ':passenger:contact',
							pageURL: [location.origin, location.pathname, location.search, '#/passenger/contact'].join('')
						};
						window.digitalDataLoaded.then(function () {
							setTimeout(function () {
								// Custom Event for Analytics
								document.body.dispatchEvent(new CustomEvent('PassengerPage', { 'detail': custom }));
							});
						});

						$state.go('passenger.contact');
					}

					$timeout(function () {
						$('body, html').animate({ scrollTop: 0 }, 'fast').promise().done(function () {
							haUtils.safeApply($scope, function () {
								if ($stateParams.id <= $scope.Passengers.length) {
									$scope.setNextForm(nextId);
									$scope.currentPaxClass = 'pax' + $stateParams.id;
								}
							});
						});
					}, 500);
				});

			} else {
				var nextId = $stateParams.id + 1;
				if ($stateParams.id < $scope.Passengers.length) {
					$state.go('passenger.x', { id: nextId }, { notify: false });
				} else {
					$state.go('passenger.contact');
				}

				$('body, html').animate({ scrollTop: 0 }, 'fast').promise().done(function () {
					var nextId = $stateParams.id;
					haUtils.safeApply($scope, function () {
						if ($stateParams.id <= $scope.Passengers.length) {
							$scope.setNextForm(nextId);
							$scope.currentPaxClass = 'pax' + nextId;
						}
					});
				});
			}

		};

		$scope.prevTraveler = function () {
			var travelerIdx = ($stateParams.id) ? $stateParams.id - 2 : $scope.Passengers.length - 1;
			var traveler = $scope.Passengers[travelerIdx];

			traveler.saveSuccessDelayActive = traveler.saving = false;
			$state.go('passenger.x', { id: travelerIdx + 1 }, { notify: false });

			$('body, html').animate({ scrollTop: 0 }, 'fast').promise().done(function () {
				haUtils.safeApply($scope, function () {
					$scope.setNextForm(travelerIdx + 1);
				});
			});
		};

		$scope.resetErrors = function () {
			$timeout(function () {
				$scope.addEditState = '';
			}, 500);
			$scope.IsServiceErrors = $scope.showInfantHelp = $scope.IsInValidHMNumber = $scope.DuplicateInfoError = $scope.DuplicateKTNError = $scope.IsEditingError = $scope.duplicateEmailError = $scope.ageLimitError = false;
			$scope.ServiceErrorMessage = '';
		};

		$scope.formSubmitted = function () {

			if ($state.is('passenger.x')) {
				if ($scope.IsLoggedIn) {
					$scope.continueToContactInfo(true);
				} else {
					$scope.nextTraveler();
				}
			}
		};

		//check if MCB is selected
		$scope.isMainCabinBasicSelected = $scope.PassengerTripSummary.AvailGridTrips.filter(function (item) { return item.CabinName === 'MAINCABINBASIC' }).length > 0;

		$scope.continueToContactInfo = function () {

			// prevent submit from happening multiple times
			if ($scope.IsProcessing) {
				return false;
			}
			$scope.IsProcessing = true;

			if (!checkSelectedPax()) {
				$scope.IsProcessing = false;
				return;
			}

			$scope.$pax.addPaxToPNR($scope.selectedTravelers.get()).success(function (result) {
				if (handleExceptions(result, 'haPassengersAPI-addPaxToPNR')) {
					$scope.IsProcessing = false;
					return;
				}

				$scope.showInfantHelp = false;
				$scope.IsServiceErrors = false;
				$scope.DuplicateInfoError = false;
				$scope.DuplicateKTNError = false;

				if (result.ErrorMessage !== null) {
					$scope.IsServiceErrors = true;
					$scope.ServiceErrorMessage = result.ErrorMessage;
					$('body, html').animate({ scrollTop: 0 }, 'slow');
				}
				else if (result.DuplicatFirstAndLastNameError !== null && result.DuplicatFirstAndLastNameError !== undefined) {
					$scope.DuplicateInfoError = true;
					$('body, html').animate({ scrollTop: 0 }, 'slow');
				}
				else if (result.DuplicateKTNError !== null && result.DuplicateKTNError !== undefined) {
					$scope.DuplicateKTNError = true;
					$('#divDuplicateKTNError').removeClass('hidden')
					$('body, html').animate({ scrollTop: 0 }, 'slow');
				}
				else {
					$scope.showInfantHelp = ($scope.selectedTravelers.get()) ? $scope.selectedTravelers.get().some(function (pax) {
						return pax.PassengerType === 'Infant';
					}) : false;
					$('#divDuplicateKTNError').addClass('hidden')
					if (result.DoBCheckMessagesList.length > 0) {
						$scope.paxRules = result.DoBCheckMessagesList;
						$scope.AllowBooking = result.AllowBooking;
						$scope.showInfantHelp = true;
						$('body, html').animate({ scrollTop: 0 }, 'fast');
					}
					$scope.IsAllPaxUpdated = true;
					$scope.Passengers = result.Passengers;
					$scope.AllowBooking = result.AllowBooking;
					$scope.InternationalCountryListForMessage = result.InternationalCountryListForMessage;
					$scope.DoBCheckMessagesList = result.DoBCheckMessagesList;

					// Rebuild selected
					$scope.InitializeSelected();

					$state.go('passenger.contact');
				}
				if (result.PassengerTripSummary) {
					$scope.PassengerTripSummary = result.PassengerTripSummary;
				}
				$scope.IsProcessing = false;
			}).error(function (result) {
				$scope.IsProcessing = false;
				handleExceptions('jsError', 'haPassengersAPI-addPaxToPNR');
				$scope.IsServiceErrors = true;
				$scope.ServiceErrorMessage = result;
			});
		};

		$scope.fauxContinueToContact = function () {
			// workaround to use the existing button below the passengers list (to benefit from its scope)
			$timeout(function () {
				angular.element('button.done').trigger('click');
			});
		};

		$scope.InitializeSelected = function () {

			if (!$scope.IsAllPaxUpdated) {
				return;
			}
			// check pax info
			$scope.Passengers.forEach(function (pax) {
				// Normalize pax dates at empty if 01/01/1901
				if (!pax.DOBYear || pax.DOBYear === '0001') {
					pax.IsIncomplete = true;
					pax.DOBDay = '';
					pax.DOBMonth = '';
					pax.DOBYear = '';
				}
				// check for incomplete info
				if (!pax.DOBDay ||
					!pax.DOBMonth ||
					!pax.DOBYear ||
					!pax.FirstName ||
					!pax.LastName ||
					!pax.Gender) {
					pax.IsIncomplete = true;
				}
				// no info at all? new pax
				if (!pax.DOBDay &&
					!pax.DOBMonth &&
					!pax.DOBYear &&
					!pax.FirstName &&
					!pax.LastName &&
					!pax.Gender) {
					pax.IsIncomplete = false;
				}
			});
			var newSelectedPaxList = $scope.Passengers.filter(function (t) {
				return t.IsSelectedPax;
			});
			$scope.selectedTravelers.set(newSelectedPaxList);

			// if ForcePrimaryTraveller or PreselectPrimaryTraveller is true, pre-select the user
			var autoSelectPrimary = $scope.IsLoggedIn && ($scope.ForcePrimaryTraveller || $scope.PreselectPrimaryTraveller);
			if (($scope.selectedTravelers.get().length < $scope.totalPaxCount) && autoSelectPrimary) {
				angular.forEach($scope.Passengers, function (passenger) {
					if (passenger.isUser) {
						passenger.IsSelectedPax = true;
						$scope.selectedTravelers.get().push(passenger);
					}
				});
			}
			$scope.isCountryCodeNotAvailable = false;
			angular.forEach($scope.selectedTravelers.get(), function (traveler) {
				if ((traveler.Redressnumber != null && traveler.Redressnumber != "" && (traveler.RedressNoCountryCode == null || traveler.RedressNoCountryCode == "")) || (traveler.Knowntravelernumber != null && traveler.Knowntravelernumber != "" && (traveler.KnownTravelerNoCountryCode == null || traveler.KnownTravelerNoCountryCode == ""))) {
					$scope.isCountryCodeNotAvailable = true;
				}
			});
			$scs.get('CountryCodeWarningMessage.Content').then(function (string) {
				$scope.countryCodeWarningText = string;
			});

			$scope.paxRules = (!!$scope.DoBCheckMessagesList && $scope.DoBCheckMessagesList.length) ? $scope.DoBCheckMessagesList : undefined;
			$scope.showInfantHelp = !$scope.DoBCheckMessagesList || $scope.DoBCheckMessagesList.length;
			$scope.Country = $scope.InternationalCountryListForMessage;
			$scope.CountryFlag = !!$scope.Country.length;

			if ($scope.selectedTravelers.get().length === $scope.totalPaxCount) {
				$('.contact-info').addClass('current');
				$('body, html').animate({ scrollTop: 0 }, 'slow');

				//load phone number check
				if ($scope.PaxContactInfo !== null && $scope.PaxContactInfo.phone2number !== null) {
					$scope.showphone2 = true;
				}
			}

		};
		$scope.InitializeSelected();	// If jumped directly to contact

		$scope.contactInfoSubmit = function (e, continueWithUM) {

			// Validate the form if outer button clicked
			if ($(e.target).is('.continue')) {
				$scope.currentForm.$validate($scope.currentForm);
			}

			if (!$scope.currentForm.$valid) {
				return false;
			}
			// show modal if Unaccompanied Minor
			if ($scope.paxRules && $scope.paxRules.length > 0 && !continueWithUM) {
				$scope.showUMModal();
				return false;
			}

			//Checking for ages below 15 for national flights
			window.sessionStorage.removeItem('haMinor');
			if (!$pax.isAdultInFlight($scope.selectedTravelers.get(), $scope.TripSummary.DepartDate)) {
				window.sessionStorage.setItem('haMinor', true);
			}

			//ensure entered KTNs are unique
			if (!validateKTNs()) {
				return;
			}

			// ensure you've selected the same amount of pax and pax types that you searched for
			if (checkSelectedPax()) {

				var infants = numSelected(/Infant/);

				if (infants > 2) {
					$scope.IsInfantMaxError = true;
				} else if (infants > 0 && numSelected(/Adult/) === 0) {
					$scope.IsInfantAdultError = true;
				} else {
					$scope.isPageError = false;
					$scope.IsInfantMaxError = false;
					$scope.IsInfantAdultError = false;
				}
			}

			if (!$scope.isPageError) {

				$scope.ContactInfo = $scope.PaxContactInfo;

				$scope.$pax.updatecontactinfo($scope.ContactInfo).success(function (result) {
					if (handleExceptions(result, 'haPassengersAPI-updatecontactinfo') || !result.IsSuccess) {
						$scope.isPageError = true;
						$timeout(function () {
							populateStaticErrorContent(result.ErrorMessage);
						}, 800);
						return;
					}

					// PROCEED TO INFLIGHT / ITIN PAGE
					window.location.href = ($scope.IsHoldBooking || result.SkipItinPage) ?
						'/Book/inflightoptions/index' :
						'/Book/itinerary/index';

				}).error(function (result) {
					handleExceptions(result, 'haPassengersAPI-updatecontactinfo');
					$scope.IsServiceErrors = true;
					$scope.ServiceErrorMessage = result;
				});
			}
		};

		////////////////////////
		// ADD/EDIT FUNCTIONS //
		$scope.customSubmit = function () {
			return (!$scope.addEditState) ? $scope.formSubmitted() : ($scope.addEditState === 'edit') ? $scope.doneEditTraveler() : $scope.doneAddTraveler();
		};

		$scope.doneEditTraveler = function () {

			// Reset states
			$scope.IsServiceErrors = $scope.showInfantHelp = $scope.IsInValidHMNumber = $scope.DuplicateInfoError = $scope.DuplicateKTNError = $scope.IsEditingError = $scope.duplicateEmailError = $scope.ageLimitError = false;
			$scope.ServiceErrorMessage = '';

			resetMilesOrEmail($scope.modifyTraveler);

			if ($scope.IsLoggedIn) {
				// Added as a part of finding the page that requests Edit passenger
				$scope.modifyTraveler.MethodRequestor = $window.pageURL.indexOf('passenger') !== -1 ? 'passenger' : 'contactinfo';

				$scope.$pax.editLoggedInPax($scope.modifyTraveler).success(function (result) {

					// Generic errors
					if (handleExceptions(result, 'haPassengersAPI-editLoggedInPax')) {
						return;
					}

					// State errors
					if (result.ErrorMessage) {
						$scope.IsServiceErrors = true;
						$scope.ServiceErrorMessage = result.ErrorMessage;
						return;
					} else if (result.DoBCheckMessagesList.length > 0) {
						$scope.paxRules = result.DoBCheckMessagesList;
						$scope.AllowBooking = result.AllowBooking;
						$scope.showInfantHelp = true;
						$('body, html').animate({ scrollTop: 0 }, 'fast');
					} else {
						$scope.paxRules = [];
						$scope.AllowBooking = true;
					}

					// Is it a duplicate error
					if (result.DuplicatFirstAndLastNameError !== null && result.DuplicatFirstAndLastNameError !== undefined) {
						$scope.DuplicateInfoError = true;
						return;
					}

					if (result.DuplicateKTNError !== null && result.DuplicateKTNError !== undefined) {
						$scope.DuplicateKTNError = true;
						return;
					}


					// Is the ha miles invalid
					var index = result.Passengers.map(function (pass) {
						return pass.TravelerAssociationID === $scope.modifyTraveler.TravelerAssociationID;
					}).indexOf(true);

					var currentPassenger = result.Passengers[index];

					$scope.modifyTraveler.IsValidHMNumber = $scope.IsValidHMNumber = currentPassenger.IsValidHMNumber;
					$scope.IsInValidHMNumber = !$scope.modifyTraveler.IsValidHMNumber;
					$scope.modifyTraveler.DuplicateEnrollEmail = currentPassenger.DuplicateEnrollEmail;

					checkDuplicateEmail($scope.modifyTraveler, false);
					if (!$scope.modifyTraveler.IsValidHMNumber || $scope.duplicateEmailError || $scope.ageLimitError) {
						$scope.IsInValidHMNumber = true;
						return;
					}

					// Any other errors or close the modal
					if (($scope.modifyTraveler.DuplicateEnrollEmail.Status === 'SUCCESS' || $scope.modifyTraveler.DuplicateEnrollEmail.Status === '') && !$scope.IsInValidHMNumber) {
						$rootScope.$broadcast('closeModal');
						$timeout(function () {
							$scope.addEditState = '';
						}, 500);
					} else {
						$scope.IsEditingError = true;
					}

					$scope.modifyTraveler.Type = currentPassenger.Type;

					if ($scope.modifyTraveler.Type !== 'Infant') {
						$scope.modifyTraveler.GuardianID = null;
						$scope.modifyTraveler.GuardianName = null;
					}

					// Copy the changed pax back to passengers
					$scope.Passengers[index] = $scope.modifyTraveler;
					window.PaxVM.Passengers = $scope.Passengers;

					if ($scope.modifyTraveler.IsSelectedPax) {
						var selectedIdx = $scope.selectedTravelers.get().map(function (pass) {
							return pass.TravelerAssociationID === $scope.modifyTraveler.TravelerAssociationID;
						}).indexOf(true);
						$scope.selectedTravelers.get()[selectedIdx] = $scope.modifyTraveler;
					}

				}).error(function (result) {
					handleExceptions('jsError', 'haPassengersAPI-editLoggedInPax');
					$scope.IsServiceErrors = true;
					$scope.ServiceErrorMessage = result;
				});

			} else {

				$scope.$pax.addeditpax($scope.modifyTraveler).success(function (result) {

					// Generic errors
					if (handleExceptions(result, 'haPassengersAPI-editLoggedInPax')) {
						return;
					}

					$scope.showInfantHelp = (result.Passengers) ? result.Passengers.some(function (pax) {
						return pax.PassengerType === 'Infant';
					}) : false;

					// State errors
					if (result.ErrorMessage) {
						$scope.IsServiceErrors = true;
						$scope.ServiceErrorMessage = result.ErrorMessage;
						return;
					} else if (result.DoBCheckMessagesList.length > 0) {
						$scope.paxRules = result.DoBCheckMessagesList;
						$scope.AllowBooking = result.AllowBooking;
						$scope.showInfantHelp = true;
						$('body, html').animate({ scrollTop: 0 }, 'fast');
					} else {
						$scope.paxRules = [];
						$scope.AllowBooking = true;
					}


					if (result.DuplicatFirstAndLastNameError !== null && result.DuplicatFirstAndLastNameError !== undefined) {
						if ($scope.formValid === 'editpax') {
							$scope.DuplicateInfoError = true;
						}
						else {
							$scope.DuplicateInfoError = true;
							$scope.AllowBooking = false;
							$('body, html').animate({ scrollTop: 0 }, 'slow');
						}
						return;
					}

					if (result.DuplicateKTNError !== null && result.DuplicateKTNError !== undefined) {
						if ($scope.formValid === 'editpax') {
							$scope.DuplicateKTNError = true;
						}
						else {
							$scope.DuplicateKTNError = true;
							$scope.AllowBooking = false;
							$('body, html').animate({ scrollTop: 0 }, 'slow');
						}
						return;
					}
					

					// Is the ha miles invalid
					var index = result.Passengers.map(function (pass) {
						return pass.TravelerAssociationID === $scope.modifyTraveler.TravelerAssociationID;
					}).indexOf(true);

					var currentPassenger = result.Passengers[index];

					$scope.modifyTraveler.IsValidHMNumber = $scope.IsValidHMNumber = currentPassenger.IsValidHMNumber;
					$scope.IsInValidHMNumber = !$scope.modifyTraveler.IsValidHMNumber;
					$scope.modifyTraveler.DuplicateEnrollEmail = currentPassenger.DuplicateEnrollEmail;

					if (!$scope.modifyTraveler.IsValidHMNumber) {
						$scope.IsEditingError = true;
						return;
					}

					checkDuplicateEmail($scope.modifyTraveler, false);
					if (!$scope.modifyTraveler.IsValidHMNumber || $scope.duplicateEmailError || $scope.ageLimitError) {
						$scope.IsInValidHMNumber = true;
						return;
					}

					// Any other errors or close the modal
					$scope.modifyTraveler.DuplicateEnrollEmail = currentPassenger.DuplicateEnrollEmail;
					if (($scope.modifyTraveler.DuplicateEnrollEmail.Status === 'SUCCESS' || $scope.modifyTraveler.DuplicateEnrollEmail.Status === '') && !$scope.IsInValidHMNumber) {
						$rootScope.$broadcast('closeModal');
						$timeout(function () {
							$scope.addEditState = '';
						}, 500);
					} else {
						$scope.IsEditingError = true;
					}

					if (!$scope.IsEditingError) {
						$scope.modifyTraveler.Type = currentPassenger.Type;
						if ($scope.modifyTraveler.Type !== 'Infant') {
							$scope.modifyTraveler.GuardianID = null;
							$scope.modifyTraveler.GuardianName = null;
						}
						$scope.Passengers[index] = $scope.modifyTraveler;
						$scope.selectedTravelers.get()[index] = $scope.modifyTraveler;
					}

					if (result.PassengerTripSummary) {
						$scope.PassengerTripSummary = result.PassengerTripSummary;
					}

				})
					.error(function (result) {
						handleExceptions('jsError', 'haPassengersAPI-addeditpax');
						$scope.IsServiceErrors = true;
						$scope.ServiceErrorMessage = result;
					});

			}
		};

		$scope.doneAddTraveler = function () {

			// Reset states
			$scope.IsServiceErrors = $scope.showInfantHelp = $scope.IsInValidHMNumber = $scope.DuplicateInfoError = $scope.DuplicateKTNError = $scope.IsEditingError = $scope.duplicateEmailError = $scope.ageLimitError = false;
			$scope.ServiceErrorMessage = '';

			$scope.createSaveForFutureBookingCookie();

			resetMilesOrEmail($scope.modifyTraveler);

			$scope.$pax.addExtraLoggedTravellerInfo($scope.modifyTraveler).success(function (result) {
				if (handleExceptions(result, 'haPassengersAPI-addExtraLoggedTravellerInfo')) {
					return;
				}

				if (result.ErrorMessage) {
					$scope.IsServiceErrors = true;
					$scope.ServiceErrorMessage = result.ErrorMessage;
				} else if (result.DuplicatFirstAndLastNameError !== null && result.DuplicatFirstAndLastNameError !== undefined) {
					$scope.DuplicateInfoError = true; 
				}
				else if (result.DuplicateKTNError !== null && result.DuplicateKTNError !== undefined) {
					$scope.DuplicateKTNError = true; 
				}
				else {
					$scope.modifyTraveler = result;

					// Is the ha miles invalid
					$scope.modifyTraveler.IsValidHMNumber = $scope.IsValidHMNumber = result.IsValidHMNumber;
					$scope.IsInValidHMNumber = !$scope.modifyTraveler.IsValidHMNumber;
					$scope.modifyTraveler.DuplicateEnrollEmail = result.DuplicateEnrollEmail;

					checkDuplicateEmail($scope.modifyTraveler, false);
					if (!$scope.modifyTraveler.IsValidHMNumber || $scope.duplicateEmailError || $scope.ageLimitError) {
						$scope.IsInValidHMNumber = true;
						return;
					}


					if (($scope.modifyTraveler.DuplicateEnrollEmail.Status === 'SUCCESS' || !$scope.modifyTraveler.DuplicateEnrollEmail.Status) && $scope.modifyTraveler.IsValidHMNumber) {
						$scope.selectTraveler($scope.modifyTraveler);
						$scope.Passengers.push($scope.modifyTraveler);
						$rootScope.$broadcast('closeModal');

						$timeout(function () {
							$scope.addEditState = '';
						}, 500);
					}

					if (!result.IsValidHMNumber) {
						$scope.IsInValidHMNumber = true;
					}
				}
			}).error(function (result) {
				handleExceptions('jsError', 'haPassengersAPI-addExtraLoggedTravellerInfo');
				$scope.IsServiceErrors = true;
				$scope.ServiceErrorMessage = result;
			});
		};

		$scope.editPax = function (traveler, index) {
			$scope.formValid = '';
			$scope.ServiceErrorMessage = '';
			$scope.isPageError = $scope.DuplicateInfoError = $scope.DuplicateKTNError = $scope.IsInValidHMNumber = $scope.IsServiceErrors = false;
			$scope.addEditState = 'edit';

			$scope.modifyTraveler = angular.copy(traveler);
			$scope.modifyTraveler.DuplicateEnrollEmail = '';
			$scope.modifyTraveler.indexpos = index;
			$scope.modifyTraveler.isHAMember = ($scope.modifyTraveler.HMNumber) ? '1' : '0';

			// Model for the edit modal
			$scope.traveler = $scope.modifyTraveler;
			$scope.traveler.saveSuccessDelayActive = $scope.traveler.saving = false;
			$scope.traveler.IsInvalidRedress = false;
			$scope.traveler.IsInvalidKTN = false;

			haModal(haConfig.getTemplateUrl('book/pax/ha-pax-edit.html'), {
				id: 'addedit-pax',
				scope: $scope,
				backdrop: 'false',
				extendScope: { enableTCR: $scope.enableTCR, addEditState: $scope.addEditState }
			});
			return true;
		};

		$scope.addPax = function ($event) {
			if ($event) {
				$event.preventDefault();
			}
			$scope.formValid = '';
			$scope.ServiceErrorMessage = '';
			$scope.isPageError = $scope.DuplicateInfoError = $scope.DuplicateKTNError = $scope.IsInValidHMNumber = $scope.IsServiceErrors = false;
			$scope.addEditState = 'add';



			// Model for the add modal
			$scope.traveler = {
				HMNumber: '',
				isHAMember: '0',
				AvatarImage: $scope.DefaultAvatarUrl,
				SaveForFutureBooking: $scope.getSaveForFutureBookingValueFromCookie(),    //Read from cookie and check the state
				SeatPreference: '1'
			};
			$scope.modifyTraveler = $scope.traveler;

			haModal(haConfig.getTemplateUrl('book/pax/ha-pax-edit.html'), {
				id: 'addedit-pax',
				scope: $scope,
				backdrop: 'false',
				extendScope: { addEditState: $scope.addEditState }
			});
		};

		$scope.selectTraveler = function (traveler) {
			if ($scope.ForcePrimaryTraveller && traveler.isUser) {
				return false;
			}

			traveler.IsSelectedPax = !traveler.IsSelectedPax;
			if (traveler.IsSelectedPax) {
				$scope.selectedTravelers.get().push(traveler);
			} else {
				var index = $scope.selectedTravelers.get().indexOf(traveler);
				$scope.selectedTravelers.get().splice(index, 1);
			}
			// isProcessing was added for bug 179922, change set 30352 to avoid multiple clicks but it remains true in case
			// anyerror comes like on pax validation. Making it false whenever a traveler is selected/unselected.
			$scope.IsProcessing = false;
		};

		$scope.addPhone = function () {
			$scope.phoneCount++;
			$timeout(function () {
				$('select[name="PhoneType' + ($scope.phoneCount - 1) + '"]').focus();
			});
		};

		$scope.deletePhone = function () {
			$scope.phoneCount--;
			$timeout(function () {
				$('select[name="PhoneType' + ($scope.phoneCount - 1) + '"]').focus();
			});
		};

		////////////////////////
		// MISC SCOPE/HELPERS //
		$scope.showPaxHelp = function () {
			haModal(haConfig.getTemplateUrl('ha-pax-info-help-modal.html'), {
				id: 'pax-help'
			});
		};

		$scope.showUMModal = function () {
			haModal(haConfig.getTemplateUrl('book/pax/ha-pax-um-modal.html'), {
				id: 'pax-um-modal',
				scope: $scope,
				backdrop: 'false'
			});
		};

		$scope.getGuardian = function (traveler) {
			return $scope.selectedTravelers.get().filter(function (t) {
				return t.TravelerAssociationID === traveler.GuardianID;
			}).map(function (t) {
				return t.FirstName + ' ' + t.LastName;
			});
		};

		$scope.toFullDate = function (traveler) {
			var year = parseInt(traveler.DOBYear, 10);
			var month = parseInt(traveler.DOBMonth, 10);
			var day = parseInt(traveler.DOBDay, 10);

			return (isNaN(year) || isNaN(month) || isNaN(day) || year < 1900) ? '' : new Date(year, month - 1, day);
		};

		$scope.ToggleTaxDetails = function () {
			$scope.taxDetails = !$scope.taxDetails;
		};

		$scope.travelerSort = function (traveler) {
			return (traveler.isUser) ? '-1' : '1' + traveler.LastName + traveler.FirstName;
		};

		$scope.createSaveForFutureBookingCookie = function () {

			// Save the "SaveForFutureBooking" checkbox value to the user's cookies to persist in the future
			var currentDate = new Date();
			currentDate.setTime(currentDate.getTime() + (365 * 24 * 60 * 60 * 1000));
			var expires = "expires=" + currentDate.toUTCString();

			if (HA.CookiesRequireSsl) {
				document.cookie = ['PermanentSaveCookie', '=', $scope.modifyTraveler.SaveForFutureBooking, '; secure; ', expires, '; path=/'].join('');
			}
			else {
				document.cookie = ['PermanentSaveCookie', '=', $scope.modifyTraveler.SaveForFutureBooking, '; ', expires, '; path=/'].join('');
			}
		};

		$scope.getSaveForFutureBookingValueFromCookie = function () {

			var saveForFutureBookingValue = true;

			var checkCookie = haUtils.readCookie('PermanentSaveCookie');
			if (checkCookie !== undefined) {
				checkCookie = decodeURIComponent(checkCookie);
				saveForFutureBookingValue = JSON.parse(checkCookie);
			}

			return saveForFutureBookingValue;
		};


		////////////////////
		// INFANT HELPERS //
		$scope.showInfantTermsState = function () {
			$timeout(function () {
				if ($scope.formValid === 'assignguardians') {
					$scope.showInfantTerms = true;
					$timeout(function () {
						$scope.$apply();
					});
				}
			}, 0);
		};

		$scope.doneAcceptInfantTerms = function () {
			$timeout(function () {
				if ($scope.formValid === 'infantterms') {
					$scope.$pax.associateinfants($scope.infantTravelers).success(function (result) {
						if (handleExceptions(result, 'haPassengersAPI-associateinfants')) {
							return;
						}
						if (result === 'true') {
							$scope.$emit('paxInfoDone');  // TBD INFANTS
						} else {
							$rootScope.$broadcast('closeModal');
							$scope.isPageError = true;
							$scope.errortype = 'error';
							$scope.errordescription = 'There was error assigning the infants. Please try again.';
						}
					}).error(function (result) {
						handleExceptions('jsError', 'haPassengersAPI-associateinfants');
						$scope.IsServiceErrors = true;
						$scope.ServiceErrorMessage = result;
					});
				}
			}, 0);
		};

		$scope.doneAssignInfants = function () {
			$timeout(function () {
				if ($scope.formValid === 'assignInfants') {
					$rootScope.$broadcast('closeModal');
					$scope.showInfantTermsModal();
				}
			}, 0);
		};

		$scope.showAssignInfantsModal = function () {
			$scope.showInfantTerms = false;

			haModal(haConfig.getTemplateUrl('ha-pax-info-infant-assignment-modal.html'), {
				id: 'pax-assign-infants'
			});
		};


		///////////////
		// LISTENERS //
		$scope.$on('IncludeReady', function () {
			var frm = angular.element('ol.pax li.active form, #contactInfoForm');
			if (frm.length) {
				$scope.currentForm = frm.scope()[frm.attr('name')];
				if ($scope.currentForm) {
					$scope.currentForm.$setPristine();
				}
			}
		});

		$scope.$on('$selectedAvatarURL', function (event, avatar) {
			var idx = $scope.traveler ? $scope.traveler.indexpos : -1;
			var idxPax = (idx > -1) ? idx : $stateParams.id - 1;
			var trav;

			if ($scope.traveler) {
				trav = $scope.traveler;
			} else if ($scope.Passengers[idxPax]) {
				trav = $scope.Passengers[idxPax];
			}

			if (trav) {
				trav.AvatarImage = avatar.ProfileImageSrc;

				if (!$scope.currentForm) {
					var $modalFrm = $('form[name="passenger1form"]');

					if ($modalFrm.length && $modalFrm.scope()) {
						$scope.currentForm = $modalFrm.scope().passenger1form;
					}
				}

				// Set the view value to make the form dirty
				if ($scope.currentForm && $scope.currentForm.avatarUrl) {
					$scope.currentForm.avatarUrl.$setViewValue(trav.AvatarImage);
				}
			}

		});


		/////////////////////
		// PRIVATE HELPERS //

		function validateKTNs() {

			var duplicates = _.chain($scope.Passengers).filter(function (p) { return (!$scope.IsLoggedIn && p.Knowntravelernumber != null && p.Knowntravelernumber != "") || (p.IsSelectedPax && p.Knowntravelernumber != "" && p.Knowntravelernumber != null); }).groupBy('Knowntravelernumber').filter(function (v) { return v.length > 1 }).flatten().value();

			if (duplicates.length > 1) {
				var index = $stateParams.id - 1;
				var traveler = $scope.Passengers[index];

				$scope.DuplicateKTNError = true;
				traveler.saving = false;
				$('body, html').animate({ scrollTop: 0 }, 'fast');
				return false;
			}
			$scope.DuplicateKTNError = false;
			return true;
		}
		function populateStaticErrorContent(content) {
			$('.dynamicAlertContainer').find('.alert-content-secondary').html(content);
			$('body, html').animate({ scrollTop: 0 }, 'slow');
		}

		function numSelected(paxType) {
			return $scope.selectedTravelers.get().filter(function (traveler) {
				return paxType.test(traveler.Type);
			}).length;
		}

		function handleExceptions(result) {
			if (result !== 'jsError') {
				switch (result.ErrorCodeHandle) {
					case 'SessionTimeOut':
						window.location.href = '/book/error?ErrorType=SessionTimeOut';
						break;
					default:
						if (result.ErrorCodeHandle !== undefined) {
							window.location.href = result.RedirectURL;
						} else {
							return false;
						}
						break;
				}
			} else {
				window.location.href = '/book/error?ErrorType=Technical&ErrorCode=js%20xhr';
			}
		}

		function checkSelectedPax() {
			$scope.errordescription = '';
			$scope.errorCategory = '';


			var invalidPaxArr = $scope.Passengers.filter(function (p) {
				return p.IsSelectedPax && (!p.DOBYear || !p.DOBMonth || !p.DOBDay);
			});

			if (invalidPaxArr.length > 0) {
				$scope.isPageError = true;
				$scope.errortype = 'error';
				$scope.errorCategory = 'missinginfo';
				$scope.errordescription = $scs('PassengerInfo.CompanionMissingFieldErrorMessageText');

				$('body, html').animate({ scrollTop: 0 }, 'slow');
				return false;
			}

			// Types of pax ok (too many adults or chose children but didn't pick enough)
			var adults = numSelected(/Adult/);
			var adultsOrig = parseInt($scope.TripSummary.AdultCount, 10);
			var children = numSelected(/Child|Infant/);
			var childrenOrig = parseInt($scope.TripSummary.ChildCount, 10);

			// Same # of travelers or adults chosen and all kids/infants
			if ($scope.selectedTravelers.get().length !== $scope.totalPaxCount) { //|| (adultsOrig > 0 && adults === 0)) {
				$scope.isPageError = true;
				$scope.errortype = 'error';
				$scope.errorCategory = 'travelermismatch';
				$scope.errordescription = $scs('PassengerInfo.travelerinformationmismatchmessage');
			} else if (adults > adultsOrig || (childrenOrig > 0 && childrenOrig > children)) {
				$scope.isPageError = true;
				$scope.errortype = 'error';
				$scope.errorCategory = 'travelermismatch';
				$scope.errordescription = $scs('PassengerInfo.selectchildpassengertext');
			} else {
				$scope.isPageError = false;
			}

			if ($scope.isPageError) {
				$('body, html').animate({ scrollTop: 0 }, 'slow');
				return false;
			}

			$scope.isPageError = false;
			return true;
		}

		function checkDuplicateEmail(traveler, animate) {

			if ((traveler.DuplicateEnrollEmail === null || traveler.DuplicateEnrollEmail.Status === 'SUCCESS' || traveler.DuplicateEnrollEmail.Status === '') && traveler.IsValidHMNumber) {
				$scope.ServiceErrorMessage = '';
				$scope.IsServiceErrors = false;
				$scope.duplicateEmailError = $scope.ageLimitError = false;
			}
			else if (traveler.DuplicateEnrollEmail.Status === 'AGELIMIT') {
				//suppress the generic page messages, we are using input based messaging for these situations
				$scope.ServiceErrorMessage = $scs('PassengerInfo.agelimitemailstatuserror');
				$scope.IsServiceErrors = true;
				$scope.ageLimitError = true;
				if (animate) {
					$('body, html').animate({ scrollTop: 0 }, 'slow');
				}
			}
			else if (traveler.DuplicateEnrollEmail.Status === 'DUPLICATE') {
				//suppress the generic page messages, we are using input based messaging for these situations
				$scope.ServiceErrorMessage = $scs('PassengerInfo.duplicateemailstatuserror');
				$scope.IsServiceErrors = true;
				$scope.duplicateEmailError = true;
				if (animate) {
					$('body, html').animate({ scrollTop: 0 }, 'slow');
				}
			}

		}

		function interstitialDynamicResize() {
			if (!$rootScope.isMobile) {
				$('.ha-modal#InterstitialAirAvailability-modal .modalContainer').css({
					'width': $(window).width(),
					'height': $(window).height(),
					'padding-top': $(window).height() * 0.20
				});
			}
		}

		function resetMilesOrEmail(traveler) {
			// Remove any HA # or emailId they entered
			if (traveler.isHAMember === '0' || !traveler.HMNumber) {
				traveler.HMNumber = '';
			} else if (traveler.isHAMember === '1') {
				traveler.EmailID = '';
			}
		}

		function validCountrySelection(traveler) {
			if (traveler.Knowntravelernumber !== 'undefined' && traveler.Knowntravelernumber !== "") {
				if (traveler.KnownTravelerNoCountryCode == null || traveler.KnownTravelerNoCountryCode === '' || traveler.KnownTravelerNoCountryCode === 'undefined') {
					traveler.IsInvalidKTN = true;
				}
				else {
					traveler.IsInvalidKTN = false;
				}
			}
			else {
				traveler.IsInvalidKTN = false;
			}

			if (traveler.Redressnumber !== 'undefined' && traveler.Redressnumber !== "") {
				if (traveler.RedressNoCountryCode == null || traveler.RedressNoCountryCode === '' || traveler.RedressNoCountryCode === 'undefined') {
					traveler.IsInvalidRedress = true;
				}
				else {
					traveler.IsInvalidRedress = false;
				}
			}
			else {
				traveler.IsInvalidRedress = false;
			}

			if (traveler.IsInvalidKTN || traveler.IsInvalidRedress) {
				return false;
			}
			else {
				return true;
			}
		}
		// Enums are serializing to values!
		function phonesInit() {
			$scope.UpdatePhoneDetailsValidation = $scope.PaxContactInfo;
			$scope.PhoneDetails = $scope.UpdatePhoneDetailsValidation.PhoneDetailsVM.PhoneDetails;

			if ($scope.PhoneDetails.length) {
				$scope.PhoneDetails.forEach(function (p) {
					p.Type = (!isNaN(p.Type)) ? p.Type : ['Home', 'Work', 'Mobile'].indexOf(p.Type) + 1;
				});
			}

			$scope.PhoneDetails[0] = $scope.PhoneDetails[0] || {
				CountryCode: window.defaultPhoneCountryCode,
				Number: '',
				Type: $scope.phoneTypes.length > 2 ? $scope.phoneTypes[2].value : 1
			};
			$scope.PhoneDetails[1] = $scope.PhoneDetails[1] || {
				CountryCode: window.defaultPhoneCountryCode,
				Number: '',
				Type: $scope.phoneTypes.length > 2 ? $scope.phoneTypes[2].value : 1
			};
			$scope.PhoneDetails[2] = $scope.PhoneDetails[2] || {
				CountryCode: window.defaultPhoneCountryCode,
				Number: '',
				Type: $scope.phoneTypes.length > 2 ? $scope.phoneTypes[2].value : 1
			};
		}

		// If user searched through a third party Iframe, 
		// continue interstitial and prompt user to continue on HA.com
		function promptIframeBreakout() {
			haInterstitialAPI.getAirAvailability()
				.success(function (data) {
					haModal({
						id: 'InterstitialAirAvailability-modal',
						backdrop: 'true',
						template: data,
						modalLock: true
					});
					interstitialDynamicResize();
					showIframeBreakoutModal();
				});
		}

		// Force user to leave I-frame to continue booking
		function showIframeBreakoutModal() {
			haModal(haConfig.getTemplateUrl('ha-iframe-breakout-modal.html'), {
				modalClass: 'ha-content-modal',
				id: 'iframe-breakout-modal',
				scope: $scope,
				modalLock: true
			});
			$scope.breakOutUrl = $window.self.location.href;
		}

		/////////////
		// WATCHES //
		/////////////

		// Angular 1.2.x cannot revalidate form fields manually (reset phone numbers if the country is changed)
		$scope.$watch('PhoneDetails[0].CountryCode', function (n, o) {
			$scope.PhoneDetails[0].Number = (!o || n === o) ? $scope.PhoneDetails[0].Number : '';
		});
		$scope.$watch('PhoneDetails[1].CountryCode', function (n, o) {
			$scope.PhoneDetails[1].Number = (!o || n === o) ? $scope.PhoneDetails[1].Number : '';
		});
		$scope.$watch('PhoneDetails[2].CountryCode', function (n, o) {
			$scope.PhoneDetails[2].Number = (!o || n === o) ? $scope.PhoneDetails[2].Number : '';
		});
		$scope.$watch('PaxContactInfo.TravelCoordinatorInfo.TravelCoordinatorPhoneDetails.CountryCode', function (n, o) {
			$scope.tcInfo.TravelCoordinatorPhoneDetails.Number = (!o || n === o) ? $scope.tcInfo.TravelCoordinatorPhoneDetails.Number : '';
		});

		// Reset phones when removed
		$scope.$watch('phoneCount', function (n) {
			$scope.PhoneDetails.forEach(function (p, idx, arr) {
				arr[idx] = (n < idx + 1) ? { CountryCode: window.defaultPhoneCountryCode, Number: '', Type: 1 } : arr[idx];
			});
		});
	}]);

	module.directive('haIncludeNotify', [function () {

		return {
			restrict: 'A',
			link: function ($scope) {
				$scope.$emit('IncludeReady');
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Stories
	// ===============================
	//
	// * **Class:** haStories
	// * **Author:** Jamie Perkins
	//
	// Special Hawaiian blog functionalities

	var module = angular.module('haStoriesModule', []);

	/*
	 create a styled pull-quote from a p tag and place it in the right rail
	 the appropriate distance down
	 */
	module.directive('haPullQuote', function () {

		return {
			template: '<div class="pull-quote"><span class="top-quote">&ldquo;</span><p ng-transclude></p><span class="bottom-quote">&rdquo;</span></div>',
			transclude: true,
			restrict: 'A',
			scope: {},
			link: function ($scope, $el) {

				if (!$('.right-rail')) {
					return;
				}

				$scope.id = $($el).index() + 1;

				var PullQuote = function (el) {

					var idString = 'pull-quote' + $scope.id;
					var self = this;
					self.markerIdString = 'pull-quote-marker' + $scope.id;
					$(el).attr('id', idString).addClass('added-item');

					// calculate where in the right-rail to place the pull-quote
					var topDist = $(el).offset().top;
					var $lastRailItem = $('.right-rail').children().last();
					var firstAvailableSpace = $($lastRailItem).outerHeight() + $($lastRailItem).offset().top;
					//console.log('create pull quote '+$scope.id);

					// move DOM element to right-rail, leave a marker in case right-rail items shift
					var marker = '<span class="marker" id="' + self.markerIdString + '"></span>';
					$(marker).insertBefore(el);
					$(el).remove();
					$('.right-rail').append(el);
					if (topDist > firstAvailableSpace) {
						var diff = topDist - firstAvailableSpace;
						var topMargin = (diff < 20) ? 20 : diff;
						$(el).css({ marginTop: topMargin });
					}
				};


				if (!$scope.$root.isMobile) { // disable on mobile (pull quote keeps styles but remains in article body)

				$scope.pullQuote = new PullQuote($el);

				// listen for repositioning
				$scope.$on('repositionSidebarElements', function (event, data) {
					var markerTopDist = $('#' + $scope.pullQuote.markerIdString).offset().top;
					var $prev = $($el).prev();
					var prevBottomTopDist = $prev.offset().top + $($el).prev().outerHeight();
					if (markerTopDist > data.top) {
						if ($prev.hasClass('blocker')) {
							var diff = markerTopDist - (data.top + data.height);
							var topMargin = (diff < 20) ? 20 : diff;
							$($el).css({ marginTop: topMargin });
							//console.log('- position pq below blocker with top margin: '+topMargin);
						}
						// pq is below another sidebar item which is below the page-width image
						else if (prevBottomTopDist > data.top + data.height) {
							if (prevBottomTopDist > markerTopDist) {
								$($el).css({ marginTop: 20 });// default top margin
								//console.log('- position pq just below prev sidebar item');
							}
							else {
								var diff = markerTopDist - prevBottomTopDist;
								var topMargin = (diff < 20) ? 20 : diff;
								$($el).css({ marginTop: topMargin });
								//console.log('- position pq '+topMargin+' below prev');
							}
						}
						// pq is the first sidebar item below the page-width image
						else {
							var diff = markerTopDist - (data.top + data.height);
							var topMargin = (diff < 20) ? 20 : diff;
							$($el).css({ marginTop: topMargin });
							//console.log('- position pq '+topMargin+' below blocker');
						}
					}
				});
			}
			}
		};

	});

	/*
	 make an image fill up the container width despite being placed in a column
	 */
	module.directive('haPageWidthImage', ['$rootScope', function ($rootScope) {

		return {
			restrict: 'A',
			link: function ($scope, $el) {

				if (!$('.article-body')) {
					return;
				}

				var PageWidthImage = function (el) {

					var self = this;
					self.id = $(el).index() + 1;
					self.blockerIdString = 'blocker' + self.id;
					self.firstOverlappedIndex = -1;
					self.firstOverlappedIndexSet = false;

					this.enlarge = function () {

						// enlarge image and absolutely position to fill page width
						var pageWidth = $('.container').outerWidth();
						var containerOffset = $('.container').offset();
						var columnOffset = $('.article-body').children('p:first-child').offset();
						var elemOffset = $(el).offset();
						var $lastRailItem = $('.right-rail').children().last();
						var rightRailEnd = $($lastRailItem).outerHeight(true) + $($lastRailItem).offset().top;

						$(el).children('img:first-child').css({
							left: -(columnOffset.left - containerOffset.left),
							width: pageWidth,
							display: 'block' // display the img only after it's been moved
						});
						var newHeight = $(el).children('img:first-child').height();
						$(el).height(newHeight);

						//console.log('enlarge image '+self.id);

						// find overlapped items in right rail and push out of the way
						var extraMargin = 0;
						if (!self.firstOverlappedIndexSet) {
							self.firstOverlappedIndexSet = true;
							if (rightRailEnd > elemOffset.top) {
								$('.right-rail').children().each(function (i) {
									var offsetTop = $(this).offset().top;
									var height = $(this).outerHeight(true);
									if (offsetTop + height > elemOffset.top) {
										self.firstOverlappedIndex = i;
										if (i === 0) {
											extraMargin = (offsetTop + height) - elemOffset.top;
										}
										return false;
									}
								});
							}
						}
						// page-width image overlaps first right rail item. push it down so it doesn't overlap
						if (extraMargin > 0) {
							$(el).css({ marginTop: extraMargin });
						}

						// add a right rail blocker div
						var blocker = '<div id="' + self.blockerIdString + '" class="blocker">&nbsp;</div>';

						if (self.firstOverlappedIndex > -1) {
							// insert before overlapped items
							if (self.firstOverlappedIndex === 0) {
								if (!$('#' + self.blockerIdString).is('*')) {
									$('.right-rail').children().eq(0).after(blocker);
								}
								$('#' + self.blockerIdString).css({ height: newHeight });
							} else {
								var $lastVisibleRailItem = $('.right-rail').children().eq(self.firstOverlappedIndex - 1);
								var rightRailVisibleEnd = $($lastVisibleRailItem).outerHeight() + $($lastVisibleRailItem).offset().top;
								var topMargin = elemOffset.top - rightRailVisibleEnd;
								//console.log('first overlapped index: '+self.firstOverlappedIndex+', rightRailVisibleEnd: '+rightRailVisibleEnd+', elemOffset.top: '+elemOffset.top);
								if (!$('#' + self.blockerIdString).is('*')) {
									$('.right-rail').children().eq(self.firstOverlappedIndex - 1).after(blocker);
								}
								//console.log('div with height: '+newHeight+' and top margin: '+topMargin);
								if (topMargin > 0) {
									$('#' + self.blockerIdString).css({ height: newHeight, marginTop: topMargin });
								}
							}
						} else {
							// no overlapped items, append to right rail
							var $lastRailItem = $('.right-rail').children().last();
							var rightRailEnd = $($lastRailItem).outerHeight() + $($lastRailItem).offset().top;
							var topMargin = elemOffset.top - rightRailEnd;
							if (!$('#' + self.blockerIdString).is('*')) {
								$('.right-rail').append(blocker);
							}
							if (topMargin > 0) {
								$('#' + self.blockerIdString).css({ height: newHeight, marginTop: topMargin });
							}
						}
						// let other sidebar elements know to recalculate positioning
						$rootScope.$broadcast('repositionSidebarElements', { top: elemOffset.top, height: newHeight });
					};
				};

				if (!$scope.$root.isMobile) { // disable on mobile

				$scope.pageWidthImage = new PageWidthImage($el);
				$scope.pageWidthImage.enlarge();

				$scope.enlargeTimeout = 0;
				var sizedForWidth = $(window).width();

				// reposition when window size changes
				$(window).on('resize orientationchange', function () {
					clearTimeout($scope.enlargeTimeout);
					$scope.enlargeTimeout = setTimeout(function () {
						if ($(window).width() !== sizedForWidth) {
							sizedForWidth = $(window).width();
							$scope.pageWidthImage.enlarge();
						}
					}, 50);
				});
			}
			}
		};
	}]);

	/*
	 makes an aside - small box with text snippet, inset in article - whose 'read more'
	 link opens a modal with all the content.
	 */
	module.directive('haAside', ['$compile', 'haModal', function ($compile, haModal) {

		return {
			restrict: 'A',
			scope: {
				readMoreText: '@',
				heading: '@'
			},
			link: function ($scope, $el) {

				var id = $($el).index() + 1;
				var content = $($el).html();
				var bodyText = $($el).children('p').text();
				var asideText = bodyText.substring(0, Math.min(bodyText.length, 240));
				var readMoreDom = '<a class="bodycopy-sans-5 textlink" ng-click="openModal()">' + $scope.readMoreText + '</a>';
				var readMore = $compile(readMoreDom)($scope);
				var asideContent = '<h5>' + $scope.heading + '</h5>' + '<p>' + asideText + '... </p>';
				$($el).html(asideContent);
				$($el).children('p').append(readMore);
				$($el).show();

				var modalContent = '<div class="modal-template"><div class="modal-header containerFullBleed"><div class="container"><div class="row"><div class="col"><h2>' + $scope.heading + '</h2></div></div></div></div><div class="modal-main containerFullBleed padded-main"><div class="container"><div class="row"><div class="col">' + content + '<br></div></div></div></div></div>';

				$scope.openModal = function () {
					haModal('', {
						id: 'ha-aside-' + id,
						backdrop: 'true',
						template: modalContent
					});
				};
			}
		};
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Events Calendar
	// ===============================
	//
	// * **Class:** haEventsCalendar
	// * **Author:** Jamie Perkins
	//
	// Searching events functionality

	var module = angular.module('haEventsCalendarModule', []);

	module.controller('EventsCalendarController', [
		'$scope', '$document', '$timeout', 'haSitecoreStrings', 'haDateUtils', '$filter', 'haGlobals', '$log', '$location', '$locale',
		function ($scope, $document, $timeout, $scs, haDateUtils, $filter, haGlobals, $log, $location, $locale) {

			$scope.range_datepicker_config = {
				calendars: 2,
				start: '[name="FromDate"]',
				end: '[name="ToDate"]',
				range_start: moment().startOf('day'),
				range_end: moment().startOf('day').add(330, 'days'),
                double_wide: true
			};

			$scope.msToDate = haDateUtils.msToDate;
			// event search
			$scope.model = {
				departDate: '',
				returnDate: '',
				calendarOpen: [false],
				currentDateChoice: '',
				islands: [],
				islandDisplayNames: {},
				islandIds: [],
				allIslands: true
			};
			$scope.strings = {};
			$scs.get('CALENDAR_EVENTS_LABELS').then(function (labels) {
				$scope.strings = labels;
			});
            $scope.tripType = 2; // spoof a round trip to get the date range styling to work (since ha-datepicker was designed for the booking widget)

			// Islands
			haGlobals('HACalendarIslands', function (HACalendarIslands) {
				if (HACalendarIslands && HACalendarIslands.Islands) {
					for (var i = 0; i < HACalendarIslands.Islands.length; i++) {
						$scope.model.islands.push({
							id: HACalendarIslands.Islands[i].Id,
							displayName: HACalendarIslands.Islands[i].DisplayName,
							checked: true
						});
						$scope.model.islandDisplayNames[HACalendarIslands.Islands[i].Id] = HACalendarIslands.Islands[i].DisplayName;
						$scope.model.islandIds.push(HACalendarIslands.Islands[i].Id);
					}
				}
			});
			// Events
			haGlobals('HACalendarEvents', function (HACalendarEvents) {
				$scope.model.HAEvents = HACalendarEvents;
				// $log.debug('/////// HACalendarEvents');
				// $log.debug($scope.model.HAEvents);
				// create date range string for each event
				for (var i = 0; i < $scope.model.HAEvents.length; i++) {
					if ($scope.model.HAEvents[i]) {
						var eventObj = $scope.model.HAEvents[i];
						// index for filtering
						eventObj.IslandIndex = $scope.model.islandIds.indexOf(eventObj.Island);
						// display name
						if (!eventObj.IslandDisplayName) {
							eventObj.IslandDisplayName = $scope.model.islandDisplayNames[eventObj.Island];
						}
						// dates
						var startDate = dateStringToDate(eventObj.EventStartDate);
						var endDate = dateStringToDate(eventObj.EventEndDate);
						var eventDateRangeString = '';
						if (!startDate) {
							startDate = endDate;
						}
						if (!endDate) {
							endDate = startDate;
						}
						if (!isNaN(startDate.getTime()) && !isNaN(endDate.getTime())) {
							if (startDate.getDate() === endDate.getDate()) {
								eventDateRangeString = $filter('localDate')(startDate, $language);
							} else {
								eventDateRangeString = $filter('localDate')(startDate, $language) + ' - ' + $filter('localDate')(endDate, $language);
							}
							eventObj.EventStartDateObject = startDate;
							eventObj.EventEndDateObject = endDate;
							eventObj.EventDateRangeString = eventDateRangeString;
						}
					}
				}
				// sort events by date
				$scope.model.HAEvents.sort(function (a, b) {
					var aDate = new Date(a.EventEndDateObject);
					var bDate = new Date(b.EventEndDateObject);
					if (aDate < bDate) {
						return -1;
					}
					if (bDate < aDate) {
						return 1;
					}
					return 0;
				});
			});

			$scope.searchIsActive = false;

			// pagination (not yet used)
			$scope.pgInfo = {
				currentPage: 1,
				maxSize: 10,
				pageNumber: 1
			};

			// parse url on load
			var queryParams = $location.search();

			$scope.searchIsActive = false;
			if (queryParams.islands || queryParams.startDate || queryParams.endDate) {
				$scope.searchIsActive = true;
				// make form reflect params
				// islands
				if (queryParams.islands && queryParams.islands !== 'all') {
					checkAllIslands(false);
					var islandsArray = queryParams.islands.split(',');
					for (var i = 0; i < islandsArray.length; i++) {
						$scope.model.islands[Number(islandsArray[i])].checked = true;
					}
					$scope.model.allIslands = false;
				}
				else if (queryParams.islands && queryParams.islands === 'all') {
					checkAllIslands(true);
				}

				// start and end dates
				if (queryParams.startDate) {
					var sDate = moment(queryParams.startDate).toDate();
					if (!isNaN(sDate.getTime())) {
						$scope.model.departDate = sDate;
					}
				}
				if (queryParams.endDate) {
					var eDate = moment(queryParams.endDate).toDate();
					if (!isNaN(eDate.getTime())) {
						$scope.model.returnDate = eDate;
					}
				}
			}

			// watch island checkboxes to auto-check all islands box
			$scope.$watch(getIslandCodes, function () {
				$scope.model.allIslands = allIslandsChecked();
				if ($scope.searchIsActive) {
					$location.search('islands', getIslandCodes());
				}
			});

			// handles all islands checkbox
			$scope.$watch(function (scope) {
				return scope.model.allIslands;
			}, function (newValue) {
				if (newValue) {
					checkAllIslands(true);
					if ($scope.searchIsActive) {
						$location.search('islands', 'all');
					}
				} else {
					if (allIslandsChecked()) {
						checkAllIslands(false);
						if ($scope.searchIsActive) {
							$location.search('islands', '');
						}
					}
				}
			});

            $scope.togglesearchform = function(e) {
                e.preventDefault();
                $scope.searchformvisible = !$scope.searchformvisible;
            };

			$scope.searchEvents = function (e) {
				if (e) {
					e.preventDefault();
				}
				$scope.searchIsActive = true;
				var params = {
					islands: getIslandCodes()
				};
				if ($scope.model.departDate) {
					params.startDate = formatEncodedISODate($scope.model.departDate);
				}
				if ($scope.model.returnDate) {
					params.endDate = formatEncodedISODate($scope.model.returnDate);
				}
				$('#eventSearchSubmit').blur();
				$location.search(params);
                $scope.searchformvisible = false;
			};

			$scope.searchIsland = function (code) {
				var params = {};
				$scope.model.departDate = '';
				$scope.model.returnDate = '';

				$scope.searchIsActive = true;
				if (typeof code === 'string') {
					if (code === 'all') {
						checkAllIslands(true);
						params.islands = 'all';
					} else {
						for (var i = 0; i < $scope.model.islands.length; i++) {
							if (code === $scope.model.islands[i].id) {
								$scope.model.islands[i].checked = true;
							} else {
								$scope.model.islands[i].checked = false;
							}
						}
						params.islands = getIslandCodes();
					}
				} else {
					for (var i = 0; i < $scope.model.islands.length; i++) {
						if (Number(code) === i) {
							$scope.model.islands[i].checked = true;
						} else {
							$scope.model.islands[i].checked = false;
						}
					}
					params.islands = code;
				}
				$location.search(params);
			};

			$scope.showHighlightedEvents = function () {

				$scope.searchIsActive = false;
			};

			$scope.eventSearchRangeString = function () {
				var islands = getIslandsString();
				if (islands.length === 0) {
					return $scope.strings['please check at least one island'];
				} else {

					if ($scope.model.departDate && $scope.model.returnDate) {
						var departDate = new Date($scope.model.departDate);
						var returnDate = new Date($scope.model.returnDate);
						var departDateString = $filter('localDate')(departDate, $language);
						var returnDateString = $filter('localDate')(returnDate, $language);
						return $scope.strings.events + ': ' + departDateString + ' - ' + returnDateString;
					}
					else if ($scope.model.departDate) {
						var departDate = new Date($scope.model.departDate);
						var departDateString = $filter('localDate')(departDate, $language);
						return $scope.strings['events after'] + ' ' + departDateString;
					}
					else if ($scope.model.returnDate) {
						var returnDate = new Date($scope.model.returnDate);
						var returnDateString = $filter('localDate')(returnDate, $language);
						return $scope.strings['events before'] + ' ' + returnDateString;
					}
					else {
						if (allIslandsChecked()) {
							return $scope.strings['all events'];
						} else {
							return $scope.strings['events on'] + ': ' + islands;
						}
					}
				}
			};

			// predicate function to filter events
			$scope.eventSearchPredicate = function (val) {
				return ($scope.model.islands[val.IslandIndex].checked && testForDate(val.EventStartDateObject, val.EventEndDateObject));
			};

			// takes datestring, returns 'SEP', but in locale specific string
			$scope.dateStringToMonthAbbrev = function (datestring) {
				var d = moment(datestring).toDate();
				var m = d.getMonth();
				return $locale.DATETIME_FORMATS.SHORTMONTH[m];
			};

			// private fns

			// for islands param in query string
			function getIslandsString() {
				var islands = [];
				for (var i = 0; i < $scope.model.islands.length; i++) {
					if ($scope.model.islands[i].checked) {
						islands.push($scope.model.islands[i].displayName);
					}
				}
				return islands.join(',');
			}

			function getIslandCodes() {
				if (allIslandsChecked()) {
					return 'all';
				} else {
					var codes = [];
					for (var i = 0; i < $scope.model.islands.length; i++) {
						if ($scope.model.islands[i].checked) {
							codes.push(i);
						}
					}
					return codes.join(',');
				}
			}

			function allIslandsChecked() {
				for (var i = 0; i < $scope.model.islands.length; i++) {
					if (!$scope.model.islands[i].checked) {
						return false;
					}
				}
				return true;
			}

			function checkAllIslands(boolValue) {
				for (var i = 0; i < $scope.model.islands.length; i++) {
					$scope.model.islands[i].checked = boolValue;
				}
				$scope.model.allIslands = boolValue;
			}

			function formatEncodedISODate(dateString) {
				if (dateString) {
					var d = new Date(dateString);
					var dString = $filter('date')(d, 'yyyy-MM-dd');
					return encodeURIComponent(dString);
				}
			}

			function testForDate(startDateObject, endDateObject) {
				if ($scope.model.departDate && $scope.model.returnDate) {
					var departDate = moment($scope.model.departDate).toDate();
					var returnDate = moment($scope.model.returnDate).toDate();
					// because moment zeroes out the time of the date
					returnDate.setDate(returnDate.getDate() + 1);
					return (haDateUtils.isBetween(departDate, startDateObject, endDateObject) ||
					haDateUtils.isBetween(returnDate, startDateObject, endDateObject) ||
					(haDateUtils.isBefore(departDate, startDateObject) && haDateUtils.isAfter(returnDate, endDateObject)));
				}
				else if ($scope.model.departDate) {
					var departDate = moment($scope.model.departDate).toDate();
					return (haDateUtils.isAfter(departDate, startDateObject) ||
					haDateUtils.isAfter(endDateObject, departDate) ||
					haDateUtils.isSameDay(departDate, startDateObject) ||
					haDateUtils.isSameDay(departDate, endDateObject));
				}
				else if ($scope.model.returnDate) {
					var returnDate = moment($scope.model.returnDate).toDate();
					// because moment zeroes out the time of the date
					returnDate.setDate(returnDate.getDate() + 1);
					return (haDateUtils.isAfter(returnDate, startDateObject) ||
					haDateUtils.isSameDay(returnDate, startDateObject) ||
					haDateUtils.isSameDay(returnDate, endDateObject));
				}
				else {
					return true;
				}
			}


			/* 	tries to parse string into date,
			 if fails, takes "/Date(1420009200000)/"
			 returns date object
			 */
			function dateStringToDate(datestring) {
				//$log.debug('parse date string: '+datestring);
				var d = moment(datestring).toDate();
				if (!isNaN(d.getFullYear())) {
					return d;
				} else {
					var time = datestring.match(/[\d]+/);
					time = Number(time[0]);
					return new Date(time);
				}
			}


			/*  ////////////////////////////////////////////////////////
			 CALENDAR - fns pulled and adapted from ha-booking-form.js
			 ////////////////////////////////////////////////////////  */

			// listen for date input focused
			$scope.$on('dateInputFocused', function (e, currentDateChoice) {
				$scope.model.currentDateChoice = currentDateChoice;
				// $log.debug('current date choice: '+currentDateChoice+' (open calendar)');
				$scope.model.calendarOpen = [true];
				wireCalendarCloseEvent();
			});


			// private fn - delay cal close so user can see selection
			function delayCalendarClose() {
				$timeout(function () {
					$scope.model.calendarOpen = [false];
				}, 500);
			}

			function wireCalendarCloseEvent() {
				$document.off('click.closeCalendar');
				$document.on('click.closeCalendar', function (e) {
					var $targ = $(e.target);

					if ($targ.closest('.originDestinationWrap0').length) {
						return;
					}
					$document.off('click.closeCalendar');
					$scope.model.currentDateChoice = '';
					delayCalendarClose();
					$scope.$digest();
				});
			}

			// Calendar clicked
			$scope.$on('setDate', function (e, date) {
				// $log.debug('set '+$scope.model.currentDateChoice+' to '+date);
				// Did they click the already set depart or return
				$scope.model[$scope.model.currentDateChoice] = date || '';
				// $log.debug('	result: '+$scope.model[$scope.model.currentDateChoice]);

				// reset the other date if needed
				if ($scope.model.currentDateChoice === 'departDate' && $scope.model.returnDate && date > $scope.model.returnDate) {
					$scope.model.returnDate = undefined;
				}
				else if ($scope.model.currentDateChoice === 'returnDate' && $scope.model.departDate && date < $scope.model.departDate) {
					$scope.model.departDate = undefined;
				}

				//updateQuery();
				$scope.$digest();
			});

			// Watch depart date
			$scope.$watch(function (scope) {
				if (scope.model) {
					return scope.model.departDate;
				}
			}, function () {
				//$log.debug('watch departDate');
				if ($scope.model.departDate && !$scope.model.returnDate) {
					$scope.model.currentDateChoice = 'returnDate';
					// $log.debug('set current date choice to returnDate');
				} else if ($scope.model.returnDate && $scope.model.departDate) {
					delayCalendarClose();
				}
				if ($scope.searchIsActive) {
					$location.search('startDate', formatEncodedISODate($scope.model.departDate));
				}
			});

			// Watch return date
			$scope.$watch(function (scope) {
				if (scope.model) {
					return scope.model.returnDate;
				}
			}, function () {
				//$log.debug('watch returnDate');
				if ($scope.model.returnDate && !$scope.model.departDate) {
					$scope.model.currentDateChoice = 'departDate';
				} else if ($scope.model.returnDate && $scope.model.departDate) {
					delayCalendarClose();
				}
				if ($scope.searchIsActive) {
					$location.search('endDate', formatEncodedISODate($scope.model.returnDate));
				}
			});

			// Get calendar heading
			$scope.getCalendarHeading = function (legType) {
				return (legType !== 'returnDate') ? $scope.strings['start date'] : $scope.strings['end date'];
			};

			// Check unavailable
			var today = new Date();
			$scope.getIsUnavailable = function (date) {

				// Before today
				if (haDateUtils.isBefore(date, today) && !haDateUtils.isSameDay(date, today) || haDateUtils.isAfter331(date, today)) {
					return true;
				}
			};

			/*  ////////////////////////////////////////////////////////
			 END CALENDAR
			 ////////////////////////////////////////////////////////  */


		}
	]);

})(angular);
;
(function (ng) {

	// Traveler Controller
	// --------------------------------------------
	//
	// * **Class:** TravelerCtrl
	// * **Author:** Michael Toymil
	//
	// Controller for individual travelers on the pax page.

	'use strict';

	var module;
	try {
		module = ng.module('haPaxModule');
	} catch (e) {
		module = ng.module('haPaxModule', ['ui.router', 'duScroll', 'ngSanitize', 'haGeoDataModule']);
	}

	module.controller('TravelerCtrl', ['$scope', '$interval', 'haSitecoreStrings', function ($scope, $interval, $scs) {
		// Clear one field when the other is selected.
		$scope.traveler.SaveForFutureBooking = true;
		$scope.seatPreferences = [];
		$scs.get('SEATPREFERENCE_DROPDOWN').then(function(data) {
			$scope.seatPreferences = data;
		});
		$scope.$watch('traveler.isHAMember', function (nv) {
			if (nv === '0') {
				$scope.traveler.HMNumber = null;
			} else if (nv === '1') {
				$scope.traveler.EmailID = null;
			}
		});
		$scope.$watch('traveler.SaveForFutureBooking', function (bl) {
			$scope.traveler.SaveForFutureBooking = bl;
		});

		$scope.$on('$defaultAvatarURL', function (event, avatar) {
			var traveler = $scope.traveler;
			traveler.AvatarImage = avatar.ProfileImageSrc;
		});

		// indicate missing fields for incomplete traveler
		var formEl;
		if ($scope.traveler.IsIncomplete) {
			formEl = $interval(function() {
				if ($('[name=passengerEditForm]').length) {
					$('[name=passengerEditForm]').addClass('submitted');
					$interval.cancel(formEl);
				}
			}, 100);
		}
	}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haCorporateTravelersModule', ['ui.router', 'haTravelersAPI']);
	// if we're on the page run this code otherwise not
	if ((/my-account\/guests/i).test(location.pathname)) {

		module.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {

			$urlRouterProvider.otherwise('/corporate/1');

			// Controller to handle state changes only
			var ctrl = [function () { }];

			$stateProvider
			.state('passenger', { abstract: true, url: '', controller: ctrl, template: '<div ui-view class="row"></div>' })
			.state('passenger.corporate', { url: '^/corporate', template: '<div ui-view  class="col"></div>' })
			.state('passenger.corporate.x', {
				url: '/{id:int}', templateProvider: ['haConfig', function (haConfig) {
					return haConfig.getTemplateUrlWithInclude('my-account/ha-corporate-travelers.html');
				}]
			});

		}
		])
		.run(['$rootScope', '$state', function ($rootScope, $state) {
			$rootScope.$state = $state;
		}]);
	}

	// Shared controller logic
	module
	.controller('haCorporateTravelersCtrl', ['$scope', '$rootScope', '$stateParams', '$state', '$filter', 'filterFilter', 'SavedTravelersService', 'haModal', 'haTravelersAPI', 'haGlobals', 'haPaxService', function ($scope, $rootScope, $stateParams, $state, $filter, filterFilter, SavedTravelersService, haModal, haTravelersAPI, haGlobals, haPaxService) {

		$scope.selectedTravelers = haPaxService.selectedTravelers();
		$scope.passengerLimit = 7;
		$scope.pax = {};
		$scope.search = { query: '' };
		$scope.booking = $('.my-account.ha-manage-travelers').length === 0;
		$rootScope.isBooking = $scope.booking;
		$scope.pgInfo = {
			currentPage: 1,
			pageNumber: 20,
			maxSize: 10
		};

        // increase page size to a huge number to effectively kill pagination on mobile
        if ( $rootScope.isMobile ) {
            $scope.pgInfo.pageNumber = 1000;
        }

		$scope.changePageCount = function () {
			var filterdList = filterFilter($scope.travelers, $scope.search.query);
			var highLighted = $filter('slice')(filterdList);
			$scope.totalrecords = highLighted.length;
		};

		haGlobals(['PaxVM'], function (pax) {
			if (pax) {
				$scope.pax = pax;
				$scope.passengerLimit = $scope.pax.PassengerTripSummary.Passengers.length;
			}
		});

		$rootScope.ShowProfileImageDropdown = window.jsonTravelerListsModel ? window.jsonTravelerListsModel.ShowProfileImageDropdown : false;
		$scope.travelers = $scope.booking ? $scope.pax.Passengers : window.jsonTravelerListsModel.TravelersList;
		//Fixing model inconsistency
		angular.forEach($scope.travelers, function (t) {
			t.AvatarID = t.AvatarID || t.AvatarId;
		});
		$scope.totalrecords = $scope.travelers.length;
		$scope.limit = 20;


		/////////////////////
		// PRIVATE METHODS //
		function addEdit(traveler) {

			var avatar = traveler ? traveler.AvatarID || traveler.AvatarId || 1 : 1;
			var hasHmNum = traveler && (traveler.HMAccountNo || traveler.HMNumber) ? 'Yes' : 'No';

			traveler = $.extend({}, traveler, { HasHMNumber: hasHmNum });
			$rootScope.UpdateTraveler = angular.copy(traveler);
			$scope.currentTraveler = traveler;

			haModal('/MyAccount/TravelersList/TravelerAddEdit?AvatarID=' + avatar, {
				id: 'add-edit-traveler',
				backdrop: 'false'
			});
		}

		function mapToFullViewModel(travelerFull, travelerPartial) {
			if (travelerFull && travelerPartial) {

				var selectedBeforeExtend = travelerFull.IsSelectedPax;
				if (travelerPartial.DateOfBirth) {
					var bday = travelerPartial.DateOfBirth.split('/');
					if (bday.length === 3) {
						travelerPartial.DOBMonth = bday[0];
						travelerPartial.DOBDay = bday[1];
						travelerPartial.DOBYear = bday[2];
					}
				}

				$.extend(travelerFull, travelerPartial);
				travelerFull.IsSelectedPax = selectedBeforeExtend;
			}
			return travelerFull;
		}

		////////////////////
		// SCOPE METHODS //
		$scope.toggleStyle = function () {
			$scope.booking = !$scope.booking;
		};

		$scope.addNewTraveler = function ($event) {

			$event.preventDefault();
			addEdit();
		};

		$scope.removeSavedTraveler = function (traveler) {

			$scope.currentTraveler = traveler;

			haTravelersAPI.TravelerDelete(traveler.TravelerID).success(function (response) {
				if (response.IsSuccess) {
					$scope.$emit('closeModal');
					$rootScope.$broadcast('TravelerDeleted', {
						'response': response,
						'travelerID': traveler.TravelerID
					});
				} else {
					$scope.errorMessage = response.Message;
				}
			}).error(function (xhr, httpStatusCode) {
				if (httpStatusCode === 403) {
					$rootScope.$broadcast('SessionError');
				}
			});

		};

		$scope.removeTravelerFromAside = function (traveler) {
			var index = $scope.selectedTravelers.get().indexOf(traveler);
			$scope.selectedTravelers.get().splice(index, 1);
			traveler.IsSelectedPax = false;
		};

		$scope.generatePagination = function () {
			return [];
		};

		$scope.toggleTraveler = function (traveler) {
			if (traveler.IsSelectedPax) {
				$scope.selectedTravelers.get().push(traveler);
			} else {
				var index = $scope.selectedTravelers.get().indexOf(traveler);
				$scope.selectedTravelers.get().splice(index, 1);
			}
		};

		$scope.editTraveler = function (traveler) {
			addEdit(traveler);
		};

        $scope.viewTravelerDetails = function (traveler) {
            traveler.detailsvisible = !traveler.detailsvisible;
        };

        $scope.viewTravelersList = function (traveler) {
            $scope.passengersvisible = !$scope.passengersvisible;
        };


		////////////
		// EVENTS //
		$scope.$on('TravelerModified', function (event, data) {
			var trav = $scope.travelers.filter(function (t) {
				return t.$$hashKey === $scope.currentTraveler.$$hashKey;
			})[0];
			trav = mapToFullViewModel(trav, data.response.Data);
		});

		$scope.$on('TravelerDeleted', function () {
			$scope.travelers.splice($scope.travelers.indexOf($scope.currentTraveler), 1);
			$scope.changePageCount();
		});

		$scope.$on('TravelerAdded', function (event, data) {
			if (data.response.IsSuccess) {
				data.response.Data.IsSelectedPax = true;
				data.response.Data.AvatarID = data.response.Data.AvatarID || data.response.Data.AvatarId;
				$scope.toggleTraveler(data.response.Data);
				$scope.travelers.push(data.response.Data);
				$scope.changePageCount();
				$scope.successmessage = data.response.Message;
				$scope.shownewtraveleralert = true;
				if (data.response.IsWarnAndContinue) {
				    $scope.alertType = "warning";
				} else {
				    $scope.alertType = "success";
				}
			}
		});
	}])
	.factory('SavedTravelersService', ['$http', function ($http) {
		return {
			getPassengers: function (account) {
				return $http.get('/my-account/travelers/' + account);
			}
		};
	}])
	.filter('slice', [function () {

		return function (arr, query, start, end) {
			return query ? arr || [] : (arr || []).slice(start, end);
		};
	}])
	.directive('highlight', [function () {
		return {
			restrict: 'A',
			scope: {
				'highlight': '='
			},
			link: function (scope, elem) {

				function highlight(criteria) {
					if (criteria) {
						var r = RegExp('(' + criteria + ')', 'gi');
						var match = elem.text().match(r);

						if (match) {
							var node = elem.text().replace(r, '<span class="highlight">$1</span>');
							elem.html(node);
						} else {
							elem.html('<span>' + elem.text() + '</span>');
						}
					} else {
						elem.html('<span>' + elem.text() + '</span>');
					}
				}

				scope.$watch('highlight', function (newVal) {
					highlight(newVal);
				});
			}
		};
	}]);
})(angular);
;
(function (angular) {

	'use strict';

	// Hawaiian Homepage Stories and Events
	// ===============================
	//
	// * **Class:** haHomeStoriesAndEvents
	// * **Author:** Jamie Perkins
	//
	// Miscellaneous functions needed for stories and events on homepage

	var module = angular.module('haHomepageStoriesAndEventsModule', []);

	module.controller('HomepageStoriesAndEventsController', [
		'$scope',
		'$locale',
		'haDateUtils',
		function ($scope, $locale, haDateUtils) {

			// takes datestring, returns 'SEP', but in locale specific string
			$scope.dateStringToMonthAbbrev = function (datestring) {
				var d = moment(datestring).toDate();
				var m = d.getMonth();
				return $locale.DATETIME_FORMATS.SHORTMONTH[m];
			};

			$scope.msToDate = haDateUtils.msToDate;

		}]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haVerticalSeatmapPreviewModule', []);

	// Author: Jamie Perkins
	module.controller('verticalSeatmapPreviewCtrl', ['$scope', 'haVerticalSeatmapService', '$timeout', '$log', '$rootScope', function ($scope, haVerticalSeatmapSvc, $timeout, $log, $rootScope) {

		$scope.svc = haVerticalSeatmapSvc;
		$scope.vm = {
			showDetails: false
		};

		if ($scope.previewSegments && $scope.SignifiedMarket && $scope.disableSeatUpgrades !== undefined) {
			$scope.svc.initPreviewSegments($scope.previewSegments, $scope.SignifiedMarket, $scope.disableSeatUpgrades);
		} else {
			$scope.svc.handleError('"previewSegments", "disableSeatUpgrades" & "SignifiedMarket" expected to be in scope but are missing.');
			return;
		}

		$scope.$watch('svc.pageLoading', function () {

			if ($scope.svc.pageLoading === false) {
				$scope.svc.activeLegIndex = 0;
				if ($scope.svc.legs && $scope.svc.legs.length && $scope.svc.legs[$scope.svc.activeLegIndex]) {

					renderSeatmapForLegIndex($scope.svc.activeLegIndex);
					// if initial leg is codeshare, trigger bkgd load of other leg
					if ($scope.svc.legs[$scope.svc.activeLegIndex].IsCodeShare) {
						$scope.svc.getNextLeg();
					}
				}
				else {
					$scope.svc.handleError('Missing leg data for flight '+$stateParams.flightId);
				}
			}
		});

		$scope.$watch('svc.activeLegIndex', function (newVal, oldVal) {
			renderSeatmapForLegIndex(newVal);
		});


		function renderSeatmapForLegIndex(legIndex) {
			if (haVerticalSeatmapSvc.disallowAdvanceSeatSelection($scope.svc.legs[legIndex], $scope.$switch('InFlightOptionsInfo:EnableFAARestriction'), 'ppg')) {
				return haVerticalSeatmapSvc.setFAARestriction($scope.svc.legs[$scope.svc.activeLegIndex]);
			}
			if (!$scope.svc.legs[legIndex].IsCodeShare) {

				$scope.svc.legs[legIndex].error = null;

				if (!$scope.svc.legs[legIndex].promise) {
					$scope.svc.legs[legIndex].promise = $scope.svc.getAvailableSeatsForLeg(legIndex);
				}

				$scope.svc.legs[legIndex].promise.then(function() {
					$scope.svc.loadSvgSeatmapForLeg(legIndex).then(function() {

						// apply cross-browser scaling based on equip type

						var $svg = $('#svgSeatmap'+legIndex).find('svg');
						if (!$svg.attr('scaled')) {
							var scaleFactor = 1;
							switch ($scope.svc.legs[legIndex].EquipmentName) {
								case 'Airbus 330':
									scaleFactor = .65;
									break;
								case 'Boeing 767':
									scaleFactor = .7;
									break;
								case 'Boeing 717':
									scaleFactor = .8;
									break;
								case 'ATR 42 Turboprop':
									scaleFactor = .9;
									break;
							}
							$svg.attr({
								height: $svg.attr('height') * scaleFactor,
								scaled: true
							});
						}

						// check for ETCO or refundable fares
						if ($scope.enableTCR || $scope.isRefundable) {
							$scope.svc.disableSeatUpgrades = true;
						}

						$scope.svc.legs[legIndex].resolved = true;

						$scope.$broadcast('renderSeatmapAtIndex', legIndex);
						$scope.$emit('haStickyResize');


					}, handlePromiseError);

				}, handlePromiseError);
			}
		}

		function handlePromiseError(error) {
			$scope.svc.legs[$scope.svc.activeLegIndex].resolved = true;
			$scope.svc.legs[$scope.svc.activeLegIndex].error = error;
			$log.error(error);
		}

	}]);

})(angular);



;
(function (angular) {

	'use strict';

	// Author: Jamie Perkins

	var module = angular.module('haInFlightReceiptsModule', ['ui.router']);

	if ((location.pathname.toLowerCase().indexOf('manage/my-receipts/in-flight') > -1)) {

		module.config(['$stateProvider', function ($stateProvider) {
			$stateProvider.state('index', {
				url: '/:transactionId'
			});
		}]);
	}

	module.controller('InFlightReceiptsController', ['$scope', 'haGlobals', '$timeout', '$state', '$stateParams', function ($scope, haGlobals, $timeout, $state, $stateParams) {

		$scope.loading = true;
		var print = false;
		$('.ha-page-help').hide();

		haGlobals('results', function(results) {
			$scope.flights = results.Flights || [];
			angular.forEach($scope.flights, function (flight) {
				flight.FlightDateObj = new Date(flight.FlightDate);
				flight.transId = flight.Transactions[0].TransactionNumber
			});
			$scope.loading = false;
		});

		function getReceiptFromTransactionId (xactionId) {
			for (var i = 0, len = $scope.flights.length; i < len; i++) {
				if ($scope.flights[i].transId === xactionId) {
					return $scope.flights[i];
				}
			}
		}

		$scope.$on('$stateChangeSuccess', function () {
			$scope.loading = false;
			$scope.transactionId = $stateParams.transactionId;
			if ($stateParams.transactionId) {
				$scope.flightReceipt = getReceiptFromTransactionId($stateParams.transactionId);
			}
			if (print) {
				print = false;
				$timeout(function() {
					window.print();
				}, 100);
			}
		});

		$scope.viewReceiptThenPrint = function(xactionId) {
			print = true;
			$state.go('index', { transactionId: xactionId });
		};

	}]);

})(angular);
;
(function(ng) {
    'use strict';

    var module;
    try {
        module = ng.module('haUpgradeModalModule');
    } catch (e) {
        module = ng.module('haUpgradeModalModule', []);
    }

    module.controller('haUpgradeModalController', ['$scope', 'haCitiesSvc', 'haUpgradeService', '$rootScope', function($scope, haCitiesSvc, haUpgradeService, $rootScope) {
        $scope.cities = haCitiesSvc;

		// get the platinum member pax and the other companions from upcoming trip VM
		if ($scope.parent_view === "dashboard") {
			// platinum member from the passengers
			$scope.platinum_pax = $scope.trip.PAXList.filter(function (item) {
				return item.IsLoggedinPassenger;
			})[0];

			// other passengers
			$scope.companions = $scope.trip.PAXList.filter(function (item) {
				return !item.IsLoggedinPassenger;
			});

			// create an alias so that we can refer to list in both places.
			$scope.trip.Travellers = $scope.trip.PAXList;

		// get the platinum member pax and the other companions from itinerary info VM
		} else if ($scope.parent_view === "itinerary") {
			$scope.platinum_pax = $scope.VM.Travellers.filter(function (item) {
				return item.IsLoggedinPassenger;
			})[0];

			// other passengers
			$scope.companions = $scope.VM.Travellers.filter(function (item) {
				return !item.IsLoggedinPassenger;
			});
		} else {
			// close modal?
		}

        $scope.toDate = function(dateString) {
            return new Date(dateString);
        };

		$scope.isUpgradeRqReady = function() {
			// Check if any leg selected
			var selectedLegs = $scope.trip.allLegs
				.filter(function (leg) {
					return leg.IsOnlineUpgradeEligible;
				})
				.filter(function (leg) {
					return leg.selected;
				});
			if (selectedLegs.length < 1) {
				return false;
			}

			// check if no pax is selected in any legs
			var isNoPaxSelectedInAnyLegs = false;
			angular.forEach(selectedLegs, function (leg) {
				if (!isNoPaxSelectedInAnyLegs) {
					if (!leg.platinum_pax) {
						isNoPaxSelectedInAnyLegs = true;
					} else { // check if the platinum_pax is already requested one and no further companion newly selected
						if ($scope.isPlatinumUpgraded(leg)) {
							if (leg.optional_pax === undefined || leg.optional_pax === "") {
								isNoPaxSelectedInAnyLegs = true;
							}
						}

					}
				}
			});

			if (isNoPaxSelectedInAnyLegs) {
				return false;
			}

			return true;
		}

		$scope.resetPlatinum_pax = function (leg) {
			if (!leg.selected) {
				leg.platinum_pax = false;
				leg.optional_pax = "";
			}
			else {
				if ($scope.platinum_pax.isUpgraded) {
					leg.platinum_pax = $scope.isPlatinumUpgraded(leg);
				}
			}
		}

		$scope.resetOptional_pax = function (leg) {
			if (!leg.platinum_pax) {
				leg.optional_pax = "";
			}
		}

		$scope.resetAll = function () {
			angular.forEach($scope.trip.allLegs, function (leg) {
				leg.selected = false;
				$scope.resetPlatinum_pax(leg);
			});
		}

		function buildFlightString(leg) {
			// format flight number
			var flightNumber = leg.FlightNumber.split(' ');
			flightNumber = flightNumber[flightNumber.length - 1];

			// format date
			var dateString = moment(leg.DepartureDate).format('DDMMM').toUpperCase();

			return '' + flightNumber + '/' + dateString + '/' + leg.DepartureCityCode + '/' +  leg.ArrivalCityCode;
		}

		function buildRequestData() {
			var data = {
				Pnr: $scope.trip.ReservationCode
			};

			var platinumPax = $scope.platinum_pax;

			//segments
			var SegmentUpgradeDetails = [];
			angular.forEach($scope.trip.allLegs, function(leg) {
				if (leg.selected && leg.platinum_pax) {
					var legData = {
						UniqueFlightId: buildFlightString(leg),
						SequenceId: leg.SegmentNumber
					};

					// in case of adding additional companion i.e. PlatinumPax already requested, skip adding PlatinumPax
					var upgradePassengers = [];

					if (!$scope.isPlatinumUpgraded(leg)) {
						upgradePassengers.push({
							PaxNameNumber: platinumPax.TravellerNameNumber
						})
					}

					if (leg.optional_pax) {
						upgradePassengers.push({
							PaxNameNumber: leg.optional_pax
						})
					}

					legData.PaxUpgradeCostDetails = upgradePassengers;
					SegmentUpgradeDetails.push(legData);
				}
			});
			data.SegmentUpgradeDetails = SegmentUpgradeDetails;

			//passengers
			var Passengers = $scope.trip.Travellers.map(function(trav) {
				return {
					FirstName: trav.FirstName,
					LastName: trav.LastName,
					NameNumberId: trav.TravellerNameNumber
				}
			});

			if (Passengers[0] && $scope.trip.Travellers[0]) {
				Passengers[0].HmNumber = $scope.trip.Travellers[0].HMNumber || $scope.trip.Travellers[0].HmNumber;
			}

			data.Passengers = Passengers;

			return data;
		}

		$scope.isPlatinumUpgraded = function (leg) {
			if (leg.OnlineUpgradePassengers.length > 0) {
				$scope.platinum_pax.isUpgraded = true;
				return leg.OnlineUpgradePassengers[0].UpgradedPassengers.indexOf($scope.platinum_pax.FirstName + " " + (!!$scope.platinum_pax.MiddleName ? $scope.platinum_pax.MiddleName.trim() + " " : "") + $scope.platinum_pax.LastName) > -1;
			}
			else {
				return false;
			}
		};

		$scope.getUpgradedCompanions = function (leg) {
			var legUpgradedCompanions = [];

			if (leg.OnlineUpgradePassengers.length > 0) {
				angular.forEach($scope.companions, function (companion) {
					if (leg.OnlineUpgradePassengers[0].UpgradedPassengers.indexOf(companion.FirstName + " " + (!!companion.MiddleName ? companion.MiddleName.trim() + " " : "") + companion.LastName) > -1) {
						legUpgradedCompanions.push(companion);
					}
				});
			}

			leg.upgradedCompanions = legUpgradedCompanions;
			return leg.upgradedCompanions;
		};

		$scope.getNonUpgradedCompanions = function (leg) {
			var legNonUpgradedCompanions = [];

			angular.forEach($scope.companions, function (companion) {
				if (leg.OnlineUpgradePassengers.length === 0 ||
					leg.OnlineUpgradePassengers[0].UpgradedPassengers.indexOf(companion.FirstName + " " + (!!companion.MiddleName ? companion.MiddleName.trim() + " " : "") + companion.LastName) < 0) {
					legNonUpgradedCompanions.push(companion);
				}
			});

			leg.nonUpgradedCompanions = legNonUpgradedCompanions;
			return leg.nonUpgradedCompanions;
		};

		$scope.isNoFurtherUpgradable = function (leg) {
			if ($scope.isPlatinumUpgraded(leg)) {
				if ($scope.companions.length === 0) {
					return true;
				} else {
					if ($scope.getUpgradedCompanions(leg).length > 0) {
						return true;
					}
				}
			}

			return false;
		};

		$scope.submitRequest = function () {
			if (!$scope.isUpgradeRqReady()) {
				return false;
			}

			$scope.submitting = true;
			var data = buildRequestData();

			haUpgradeService.submitUpgradeRequest(data).then(function (res) {
				if (res.data.IsSuccess) {
					$scope.upgradeSuccess = true;
					$rootScope.$broadcast('OnlineUpgradeRqSuccess', $scope.trip);
				} else {
					$scope.upgradeError = true;
				}
				$scope.submitting = false;
			}, function (err) {
				$scope.upgradeError = true;
				$scope.submitting = false;
			});
		};
    }]);
})(angular);
;
(function (angular) {
	'use strict';

	var COOKIE_NAME = 'FLIGHT_STATUS_NATIVE_APP_HIDE_BANNER';
	var DEFAULT_DAYS = 60;
	var module;
	module = angular.module('haNativeUpsellBannerModule', ['ngCookies']);
	module.controller('haFlightStatusNativeUpsellController', [
		'$scope',
		'$cookies',
		'haGlobals',
		function ($scope, $cookies, haGlobals) {
			var cookieDays;

			function init() {
				// Check the hide banner cookie
				haGlobals('flightStatusCookieExpirationDays', function(flightStatusCookieExpirationDays) {
					if (!flightStatusCookieExpirationDays || isNaN(flightStatusCookieExpirationDays)) {
						cookieDays = DEFAULT_DAYS; // Fall back if we can't access sitecore value
					} else {
						cookieDays = flightStatusCookieExpirationDays;
					}
				});
				$scope.bannerHidden = !!$cookies.get(COOKIE_NAME);
			}

			$scope.hideBanner = function() {
				// Hide the banner first
				$scope.bannerHidden = true;

				// Set the hide banner cookie
				var expiration = new Date();
				expiration.setDate(expiration.getDate() + cookieDays);
				$cookies.put(COOKIE_NAME, true, {expires: expiration});
			}

			init();
		}
	]);

})(angular);
;
(function (angular) {

	'use strict';

	var module = angular.module('haVariableSeatmapPreviewModule', []);

	// Author: Jamie Perkins
	module.controller('variableSeatmapPreviewCtrl', ['$scope', 'haVariableSeatmapService', '$timeout', '$log', '$rootScope', function ($scope, haVariableSeatmapSvc, $timeout, $log, $rootScope) {

		$scope.svc = haVariableSeatmapSvc;
		$scope.vm = {
			showDetails: false
		};

		if ($scope.previewSegments && $scope.SignifiedMarket && $scope.disableSeatUpgrades !== undefined) {
			$scope.svc.initPreviewSegments($scope.previewSegments, $scope.SignifiedMarket, $scope.disableSeatUpgrades);
		} else {
			$scope.svc.handleError('"previewSegments", "disableSeatUpgrades" & "SignifiedMarket" expected to be in scope but are missing.');
			return;
		}

		$scope.$watch('svc.pageLoading', function () {

			if ($scope.svc.pageLoading === false) {
				$scope.svc.activeLegIndex = 0;
				if ($scope.svc.legs && $scope.svc.legs.length && $scope.svc.legs[$scope.svc.activeLegIndex]) {

					renderSeatmapForLegIndex($scope.svc.activeLegIndex);
					// if initial leg is codeshare, trigger bkgd load of other leg
					if ($scope.svc.legs[$scope.svc.activeLegIndex].IsCodeShare) {
						$scope.svc.getNextLeg();
					}
				}
				else {
					$scope.svc.handleError('Missing leg data for flight '+$stateParams.flightId);
				}
			}
		});

		$scope.$watch('svc.activeLegIndex', function (newVal, oldVal) {
			renderSeatmapForLegIndex(newVal);
		});


		function renderSeatmapForLegIndex(legIndex) {
			if (haVariableSeatmapSvc.disallowAdvanceSeatSelection($scope.svc.legs[legIndex], $scope.$switch('InFlightOptionsInfo:EnableFAARestriction'), 'ppg')) {
				return haVariableSeatmapSvc.setFAARestriction($scope.svc.legs[$scope.svc.activeLegIndex]);
			}
			if (!$scope.svc.legs[legIndex].IsCodeShare) {

				$scope.svc.legs[legIndex].error = null;

				if (!$scope.svc.legs[legIndex].promise) {
					$scope.svc.legs[legIndex].promise = $scope.svc.getAvailableSeatsForLeg(legIndex);
				}

				$scope.svc.legs[legIndex].promise.then(function() {
					$scope.svc.loadSvgSeatmapForLeg(legIndex).then(function() {

						// apply cross-browser scaling based on equip type

						// var $svg = $('#svgSeatmap'+legIndex).find('svg');
						// if (!$svg.attr('scaled')) {
						// 	var scaleFactor = 1;
						// 	switch ($scope.svc.legs[legIndex].EquipmentName) {
						// 		case 'Airbus 330':
						// 			scaleFactor = .65;
						// 			break;
						// 		case 'Boeing 767':
						// 			scaleFactor = .7;
						// 			break;
						// 		case 'Boeing 717':
						// 			scaleFactor = .8;
						// 			break;
						// 		case 'ATR 42 Turboprop':
						// 			scaleFactor = .9;
						// 			break;
						// 	}
						// 	$svg.attr({
						// 		height: $svg.attr('height') * scaleFactor,
						// 		scaled: true
						// 	});
						// }

						// check for ETCO or refundable fares
						if ($scope.enableTCR || $scope.isRefundable) {
							$scope.svc.disableSeatUpgrades = true;
						}

						$scope.svc.legs[legIndex].resolved = true;
						$scope.svc.legs[legIndex].disableFirstClass = $scope.disableFirstClass;
						$scope.$broadcast('renderSeatmapAtIndex', legIndex);
						$scope.$emit('haStickyResize');


					}, handlePromiseError);

				}, handlePromiseError);
			}
		}

		function handlePromiseError(error) {
			$scope.svc.legs[$scope.svc.activeLegIndex].resolved = true;
			$scope.svc.legs[$scope.svc.activeLegIndex].error = error;
			$log.error(error);
		}

		var $body = $('body');
		$scope.upgradeTracking = function (type, leg) {
			var seatUpgradeOffer = '';
			if (type == "EXTRACOMFORT" && (!$body.data('UpgradeSeatOffered') || $body.data('UpgradeSeatOffered') !== leg)) {
				seatUpgradeOffer = 'extracomfort';
			}

			if (type == "PREFERREDSEAT" && (!$body.data('UpgradeSeatOffered') || $body.data('UpgradeSeatOffered') !== leg)) {
				seatUpgradeOffer = 'preferred';
			}

			if (seatUpgradeOffer) {
				$body.data('UpgradeSeatOffered', leg);
				document.body.dispatchEvent(new CustomEvent('UpgradeSeatOffered', { 'detail': {
					'seatType': seatUpgradeOffer
				}}));
			}

			return true;
		}


	}]);

})(angular);
;
