/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
* Copyright 2009 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms.
*
* This file is available and licensed under the following license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sun Microsystems nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package shoppingservice;
import java.lang.Exception;
import java.lang.Object;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.io.http.HttpHeader;
import javafx.io.http.HttpRequest;
import javafx.io.http.URLConverter;
import javafx.lang.FX;
import javafx.scene.control.TextBox;
import javafx.scene.Group;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import shoppingservice.model.ProductCatalog;
import shoppingservice.parser.XMLPullParser;
import shoppingservice.view.ImageButton;
import shoppingservice.view.ListItem;
import shoppingservice.view.MessageView;
import shoppingservice.view.ProductInfoView;
def appid = FX.getArgument("yahoo_appid");
def stageWidth = 240;
def stageHeight = 320;
var pageIndex:Integer = 0;
public var products: ProductCatalog[];
public var onExceptionHandler = function(exception: Exception) {
exception.printStackTrace();
var message = exception.getMessage();
if(message == null) { message = exception.toString(); }
alert("Error: {message.trim()}");
}
public var onResponseCodeHandler = function(responseCode:Integer) {
if (responseCode != 200) {
alert("Error fetching results: {responseCode}");
}
}
public var onInputHandler = function(input: java.io.InputStream) {
try {
XMLPullParser.processResults(input);
} finally {
input.close();
}
}
public var onDoneHandler = function() {
productInfoView.product = null;
if((sizeof products) == 0) {
alert("Your search - {searchText.text} - did not match any products.");
} else {
messageView.show = false;
showProductList(0, false);
}
}
function searchProducts(product:String) {
delete products;
productInfoView.show = false;
productListVisible = false;
if(product.trim().length() <= 0) {
alert("Please enter a product to search in above text box.");
return;
}
var location = "http:
"appid={appid}&query={URLConverter { }.encodeString(product.trim())}&show_numratings=0&results=12";
println("Loading xml data from {location}");
alert("Searching for product \"{product}\" \nPlease wait...");
try {
var request = HttpRequest {
location: location
method: HttpRequest.GET
onException: onExceptionHandler
onResponseCode: onResponseCodeHandler
onInput: onInputHandler
onDone: onDoneHandler
}
request.setHeader(HttpHeader.USER_AGENT, "Mozilla/4.0");
request.start();
} catch (e:Exception) {
println("WARNING: {e}");
alert("Error: Could not search... Please try again later.");
}
}
public function showProductList(pageIndex:Integer, scrollLeft:Boolean) {
productListVisible = true;
productInfoView.show = false;
if(products.size() < ((pageIndex-1) * 6)) { return ; }
if((sizeof productListItem) == 0) { initProductListItem(); }
var scrollXVal = 1;
if(scrollLeft) { scrollXVal = -1; }
productListX = 0;
var timeline:Timeline = Timeline {
keyFrames: [
KeyFrame {
time: 250ms
values: [ productListX => scrollXVal * stageWidth tween Interpolator.LINEAR ]
action: function() {
productListX = scrollXVal * -stageWidth;
for(i in [0..(productListItem.size() - 1)]) {
if((pageIndex + i) < products.size()) {
var product = products[(pageIndex * 6) + i];
productListItem[i].product = product;
} else {
productListItem[i].product = null;
}
}
}
},
KeyFrame {
time: 250ms
values: [ productListX => scrollXVal * -stageWidth tween Interpolator.DISCRETE ]
},
KeyFrame {
time: 500ms
values: [ productListX => 0 tween Interpolator.LINEAR ]
}
]
};
timeline.playFromStart();
}
public def defaultStarImage = Image { url: "{__DIR__}images/star0.png" };
var bgImage : ImageView = ImageView {
image: Image {
url: "{__DIR__}images/background.png"
}
onKeyPressed:function(e:KeyEvent):Void {
if(e.code == KeyCode.VK_LEFT) {
onBack();
} else if(e.code == KeyCode.VK_RIGHT) {
onNext();
} else if(e.code == KeyCode.VK_UP) {
searchText.requestFocus();
} else if(e.code == KeyCode.VK_ENTER) {
productInfoView.reorderNodes();
}
}
}
var messageView = MessageView { };
var backButton: ImageButton = ImageButton {
x: 25
y: stageHeight - 25
enabled: bind enableBackButton(
products, pageIndex, productInfoView.product.index, productInfoView.visible)
normalImage: Image { url: "{__DIR__}images/back_n.png" }
overImage: Image { url: "{__DIR__}images/back_h.png" }
disabledImage: Image { url: "{__DIR__}images/back_d.png" }
onMouseClicked: function(e) {
onBack();
}
}
function onBack() : Void {
if(not backButton.enabled) { return; }
if(productInfoView.visible) {
var prevIndex = productInfoView.product.index - 1;
if(prevIndex < 0) { return; }
productInfoView.product = products[prevIndex];
} else {
pageIndex--;
showProductList(pageIndex, false);
}
}
function enableBackButton(
pc:ProductCatalog[], page:Integer, productIndex:Integer,
productDetailsVisible:Boolean) : Boolean {
if(productDetailsVisible) {
return (productIndex > 0);
} else {
return (page > 0);
}
}
var nextButton: ImageButton = ImageButton {
x: 60
y: stageHeight - 25
enabled: bind enableNextButton(
products, pageIndex, productInfoView.product.index, productInfoView.visible)
normalImage: Image { url: "{__DIR__}images/next_n.png" }
overImage: Image { url: "{__DIR__}images/next_h.png" }
disabledImage: Image { url: "{__DIR__}images/next_d.png" }
onMouseClicked: function(e) {
onNext();
}
}
function onNext() : Void {
if(not nextButton.enabled) { return; }
if(productInfoView.visible) {
var nextIndex = productInfoView.product.index + 1;
if(nextIndex >= (sizeof products)) { return; }
productInfoView.product = products[nextIndex];
} else {
pageIndex++;
showProductList(pageIndex, true);
}
}
function enableNextButton(
pc:ProductCatalog[], page:Integer, productIndex:Integer,
productDetailsVisible:Boolean) : Boolean {
if(productDetailsVisible) {
return (productIndex < (pc.size() - 1));
} else {
return (pc.size() > ((page + 1) * 6));
}
}
var themeIndex = 1;
var themes: String[];
var themeImage = ImageView {
x: 2
y: 38
image: Image { url: "{__DIR__}images/theme_0.png" }
}
var themeBar : ImageView = ImageView {
x: 2
y: stageHeight - 5
image: Image { url: "{__DIR__}images/theme_n.png" }
opacity: 0.25
onMouseEntered: function(e) { themeBar.opacity = 0.5; }
onMouseExited: function(e) { themeBar.opacity = 0.25; }
onMousePressed: function(e) {
themeIndex++;
if(themeIndex >= themes.size()) { themeIndex = 0; }
themeImage.image = Image { url: themes[themeIndex] };
}
}
var listButton: ImageButton = ImageButton {
x: stageWidth - 37
y: stageHeight - 25
normalImage: Image { url: "{__DIR__}images/list_n.png" }
overImage: Image { url: "{__DIR__}images/list_h.png" }
onMousePressed: function(e) {
productInfoView.show = false;
productListVisible = true;
}
}
var maxMinButton = ImageButton {
x: 112
y: stageHeight - 25
normalImage: Image { url: "{__DIR__}images/maxmin_n.png" }
overImage: Image { url: "{__DIR__}images/maxmin_h.png" }
onMouseClicked: function(e) {
productInfoView.reorderNodes();
}
}
var closeButton = ImageButton {
x: stageWidth - 20
y: 13
normalImage: Image { url: "{__DIR__}images/x_normal.png" }
overImage: Image { url: "{__DIR__}images/x_over.png" }
visible: bind ("{__PROFILE__}" != "browser")
onMouseClicked: function(e) {
javafx.lang.FX.exit();
}
}
public var stageDragInitialX:Number;
public var stageDragInitialY:Number;
var dragBar = Rectangle {
width: stageWidth
height: stageHeight
fill: Color.TRANSPARENT
visible: bind ("{__PROFILE__}" != "browser")
onMousePressed: function(e) {
stageDragInitialX = e.screenX - stage.x;
stageDragInitialY = e.screenY - stage.y;
}
onMouseDragged: function(e) {
stage.x = e.screenX - stageDragInitialX;
stage.y = e.screenY - stageDragInitialY;
}
}
var productListItem: ListItem[];
function initProductListItem() {
for(i in [0..5]) {
insert ListItem { translateX: 0 translateY: 38.5 + (i * ListItem.height) } into productListItem;
};
}
var productListX: Number = 0;
var productListVisible = true;
var productList = Group {
visible: bind ((not productInfoView.visible) and productListVisible)
content: bind [ productListItem ]
translateX: bind productListX
clip: Rectangle {
x: 0
y: 32
width: stageWidth
height: stageHeight - 64
}
}
public var productInfoView = ProductInfoView {};
var textBounds: Rectangle = Rectangle {
x: 11 y: 8 width: 170 height: 23
};
var searchText: TextBox = TextBox {
translateX: textBounds.x
translateY: textBounds.y
width: textBounds.width
height: textBounds.height
columns: 13
selectOnFocus: false
text: ""
blocksMouse: true
action: function() {
searchProducts(searchText.text.trim());
}
onKeyPressed:function(e:KeyEvent):Void {
if(e.code == KeyCode.VK_DOWN) {
bgImage.requestFocus();
}
}
}
var searchButton = ImageButton {
x: stageWidth - 52
y: 7
normalImage: Image { url: "{__DIR__}images/search_n.png" };
overImage: Image { url: "{__DIR__}images/search_h.png" };
onMouseClicked: function(e) {
searchText.commit();
searchProducts(searchText.text.trim());
}
}
var innerBorderRect = Rectangle {
x: 1 y: 1 width: stageWidth - 2 height: stageHeight - 2
arcWidth: 8 arcHeight: 8
stroke: Color.rgb(30, 39, 47) fill: Color.TRANSPARENT
strokeWidth: 1.0
}
var outerBorderRect = Rectangle {
x: 0 y: 0 width: stageWidth - 1 height: stageHeight - 1
arcWidth: 8 arcHeight: 8
stroke: Color.rgb(26, 34, 41) fill: Color.TRANSPARENT
strokeWidth: 2.0
}
public def stage:Stage = Stage {
title: "Shopping Service"
resizable: false
visible: false
style: StageStyle.TRANSPARENT
scene: Scene {
content: Group {
content: [
bgImage, themeImage, dragBar, productList, backButton, nextButton, listButton,
maxMinButton, closeButton, searchText,
productInfoView, searchButton, outerBorderRect, innerBorderRect, messageView, themeBar
]
}
fill: Color.TRANSPARENT
}
}
public function alert(msg:String): Void {
println(msg);
messageView.text = msg;
messageView.show = true;
}
function run() {
if("{__PROFILE__}" == "mobile") {
stage.scene.fill = Color.BLACK;
}
for(i in [0..1]) {
insert "{__DIR__}images/theme_{i}.png" into themes;
}
if("{__PROFILE__}" == "browser") {
stage.scene.fill = Color.BLACK;
}
stage.visible = true;
searchText.requestFocus();
}