This section is about using Scriptaculous for visual effects, and creating PHP web services to supply data to Ajax requests.

## 1. Prime Factors

Write a PHP web service, factors.php, which will return (in `text/plain`) a comma-separated list of the prime factors of a provided number. For example, the request:

``factors.php?n=264``

Would return:

``2 * 2 * 2 * 3 * 11``

You can make queries to the following working solution to test:

One simple algorithm for prime factorization of `num` is as follows:

```for fact from 2 to num:
while num % fact = 0:
num = num / fact
add fact to the list of prime factors of the original num
```

Make your web service return a `400 Bad Request` HTTP error code with an instructive error message if the parameter `n` is not provided.

problem idea by Eli White; revised by Morgan Doocy

### Solution

#### factors.php

``````<?php
if (!isset(\$_GET['n'])) {
echo "Please provide a parameter n.";
} else {
\$factors = factorize(\$_GET["n"]);
echo implode(" * ", \$factors);
}

function factorize(\$num) {
\$factors = array();
for (\$factor = 2; \$factor <= \$num; \$factor++) {
while (\$num % \$factor == 0) {
\$num /= \$factor;
array_push(\$factors, \$factor);
}
}
return \$factors;
}
?>``````

You are given the following files for an address book web application:

The javascript file sends requests to a web service that reads and saves address data. You can view the working application here. Write the PHP file, `addressbook.php`, that provides the following behavior:

If a `GET` request is sent to `addressbook.php`, then print out, in plain text, a comma separated list of the names of people in the address book currently, like so:

```Morgan,Alex,Marty,Tyler ```

If the `GET` request has a `name` parameter set, then print out the address associated with that name. For example, if `name=Alex`:

```1234 65th Ave ```

If a `POST` request is sent to `addressbook.php`, then you must add a name/address pair to the address book's data. You may assume that the user is passing a `name` and `address` parameter. You will need to save the data in the address book in some way. You may choose the specific method. It will most likely involve saving each name/address pair as a line in a plain text file.

Extra: Edit `addressbook.js` to add Scriptaculous effects to the page.

problem by Alex Miller

### Solution

``````<?php
if (\$_SERVER['REQUEST_METHOD'] == "GET") {
\$lines = explode("\n", \$file_text);
if (isset(\$_GET["name"])) {
\$name = \$_REQUEST["name"];
foreach (\$lines as \$line) {
\$info = explode(",", \$line);
if (\$info[0] == \$name) {
print \$info[1];
}
}
} else {
\$names = array();
foreach (\$lines as \$line) {
\$info = explode(",", \$line);
array_push(\$names, \$info[0]);
}
print(implode(\$names, ","));
}
} else {
\$name = \$_REQUEST["name"];
\$file_text .= "\n" . \$name . "," . \$address;
}
?>``````

## 3. Garbage Collector

Write the necessary JavaScript / Scriptaculous code to complete the following page, which removes each icon with an `Effect.Puff` when it is dragged and dropped onto the trash can as in this working solution. Start from these skeletons:

### trashcan.html (complete HTML/CSS code):

``````<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<title>Garbage Collector</title>

<script src="trashcan.js" type="text/javascript"></script>

<body>
<h1>CSE 190 M Garbage Collector</h1>

<p>Drag the IE6 browser icons to the trash to destroy them.</p>

<div id="browsers"></div>

<div id="trashcan"></div>
</body>
</html>
``````

### trashcan.js (skeleton of JS code):

``````document.observe('dom:loaded', function() {
createBrowsers();

// finish me!
});

function createBrowsers() {
// finish me!
}

function positionRandomly(elem) {
var x = parseInt(Math.random() * 472);
var y = parseInt(Math.random() * 172);
elem.style.left = x + "px";
elem.style.top = y + "px";
}``````

You will first need to inject 20 IE6 icons (imgs of class `.browser`, with a `src` of http://webster.cs.washington.edu/cse190m/sections/8/trashcan/ie6.png) into the #browsers div. Use the provided `positionRandomly` function to give each img that you inject a random location.

Then refer to the Scriptaculous documentation for Draggables, Droppables, and Core Effects to figure out how to configure your objects so that:

• All IE6 icons are draggable.
• The trash icon is a Droppable that will "accept" only elements of class `.browser`.
• Dragging and dropping a `.browser` icon to any part of the page except the trash can results in the icon returning to its original position.
• Hovering any `.browser` element over the trash icon will cause its class to change to `.full`.
• IE6 icons will disappear in an `Effect.Puff` when they are dropped onto the trash icon.
• After the Puff effect has finished, the puffed icon should remove itself from the page.

problem by Morgan Doocy

### Solution

#### trashcan.js

``````document.observe('dom:loaded', function() {
createBrowsers();

accept: 'browser',
hoverclass: 'full',
onDrop: removeBrowser
});
});

function createBrowsers() {
for (var i = 0; i < 10; i++) {
var img = \$(document.createElement('img'));
img.src = 'http://webster.cs.washington.edu/cse190m/sections/8/trashcan/ie6.png';

positionRandomly(img);

new Draggable(img, {
revert: 'failure'
});

\$('browsers').appendChild(img);
}
}

function positionRandomly(elem) {
var x = parseInt(Math.random() * 472);
var y = parseInt(Math.random() * 172);
elem.style.left = x + "px";
elem.style.top = y + "px";
}

function removeBrowser(elem) {
new Effect.Puff(elem, {
duration: .5,
afterFinish: function() {
elem.remove();
}
});
}``````

## 4. Photo Gallery (long)

Today we will write an animated photo gallery using Scriptaculous. Start with this skeleton:

### gallery.html (complete HTML/CSS code):

``````<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
CSE 190M, Spring 2009
Section 8 (Photo Gallery)
original code by TA Sylvia Tashev
-->

<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>Photo Gallery</title>

<script src="gallery.js" type="text/javascript"></script>

<body>
<h1>Picture-It Gallery</h1>
<div id="main">
<img id="mainimage" src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture1.jpg" alt="picture" />
</div>

<p>
<img id="left" src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/left.jpg" alt="Left" />
Double-click or drag a thumbnail into the viewing area or use the arrow keys to go in sequence.
<img id="right" src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/right.jpg" alt="Right" />
</p>

<!-- container for all the pictures -->
<div id="pictures">
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture1_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture2_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture3_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture4_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture5_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture6_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture7_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture8_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture9_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture10_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture11_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture12_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture13_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture14_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture15_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture16_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture17_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture18_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture19_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture20_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture21_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture22_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture23_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture24_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture25_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture26_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture27_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture28_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture29_thumb.jpg" alt="photo" />
<img src="http://webster.cs.washington.edu/cse190m/sections/8/photogallery/images/picture30_thumb.jpg" alt="photo" />
</div>

<div id="w3c">
<a href="http://validator.w3.org/check/referer">
<img class="w3c" src="http://www.cs.washington.edu/education/courses/cse190m/09sp/images/w3c-xhtml.png" alt="Valid XHTML 1.1" /></a>
<a href="http://jigsaw.w3.org/css-validator/check/referer">
<img class="w3c" src="http://www.cs.washington.edu/education/courses/cse190m/09sp/images/w3c-css.png" alt="Valid CSS" /></a>

<a href="https://webster.cs.washington.edu/jslint?referer">
<img src="http://www.cs.washington.edu/education/courses/cse190m/09sp/images/jslint.png" alt="JavaScript Lint" /></a>
</div>
</body>
</html>
``````

### gallery.js (skeleton of JS code):

``````// CSE 190M, Spring 2009
// Section 8: Photo Gallery
// original code by TA Sylvia Tashev

var pictures = \$\$("#pictures img");
for (var i = 0; i < pictures.length; i++) {
pictures[i].observe("dblclick", pictureClick);
}

\$("left").observe("click", goLeft);
\$("right").observe("click", goRight);

// finish me

});

// called when any picture is clicked
function pictureClick() {
\$("mainimage").src = this.src.replace("_thumb", "");
}

// called when Left arrow is clicked
function goLeft() {
// finish me

}

// called when Right arrow is clicked
function goRight() {
// finish me

}
``````

Click the following image to run our sample solution: (solution JS code `gallery.js`)

Right now, when you double-click an image, it shows in the large `mainimage` area. But it's boring! Add Scriptaculous effects such as the following:

1. When the page first loads up, the thumbnail of the current `mainimage` image (the first one) should scale up to 200% of its normal size, and the `mainimage` image should have a visual effect, such as shaking.
2. When the user double-clicks a thumbnail to show a new image, the same effects as just described should occur. The new thumbnail should grow to 200% size, and the `mainimage` image should shake as it changes. Also the old selected thumbnail should shrink back to its previous size. Note that if you double-click on the same thumbnail twice in a row, it shouldn't break.
3. When the previous/next, left/right arrows are clicked, the corresponding previous or next image should be placed into the large area. It should be as though the user double-clicked on the previous or next thumbnail, complete with the same effects.

Note that you can easily access neighbors of an element using Prototype's `previous` and `next` methods.

4. Make the list of images so that its order can be rearranged by dragging the images left and right. See the Scriptaculous wiki page for ideas and options such as `tag` and `constraint`.
5. Make it so that when the user drags a thumbnail image to the `mainimage` area, the large image will update. (This is the same behavior as when the thumbnail is double-clicked. See the Scriptaculous Wiki pages about dragging and dropping.
6. If you finish the previous effects, add other bling to the page. For example, when the images are rearranged by dragging them to sort them, make effects occur on the affected thumbnails. Or add sequences of effects that occur on the same element; for example, when the large `mainimage` is updated, make it have two effects in a row, such as highlighting and then shaking.

References: Scriptaculous wiki, JSLint

problem by Sylvia Tashev

### Solution

#### gallery.js

``````// CSE 190M, Spring 2009
// Section 8: Photo Gallery
// Sylvia Tashev

// set up basic event handlers
var pictures = \$\$("#pictures img");
for (var i = 0; i < pictures.length; i++) {
pictures[i].observe("dblclick", pictureClick);
}
\$("left").observe("click", goLeft);
\$("right").observe("click", goRight);

// set up initial pic and effects
setCurrentPic(pictures[0]);

// allows the user to rearrange the picture order
Sortable.create("pictures", {
tag: "img",
constraint: "horizontal"
});

// make all the thumbnails drag-and-droppable
for (var i = 0; i < pictures.length; i++) {
new Draggable(pictures[i], {revert: true});
}
});

// sets the current large picture to be the one from the given thumbnail
function setCurrentPic(pic) {
var oldCurrentPic = \$\$(".currentpic")[0];
if (oldCurrentPic != pic) {
// emphasize new current pic
new Effect.Scale(pic, 200);

\$("mainimage").src = pic.src.replace("_thumb", "");
\$("mainimage").shake();

if (oldCurrentPic) {
// de-emphasize old current pic
oldCurrentPic.removeClassName("currentpic");
new Effect.Scale(oldCurrentPic, 50);
}
}
}

// called when any picture is clicked  (or dragged into the main show area)
function pictureClick() {
setCurrentPic(this);
}

// called when Left arrow is clicked
function goLeft() {
var current = \$\$(".currentpic")[0];
var previous = current.previous();
if (previous) {
setCurrentPic(previous);
}
}

// called when Right arrow is clicked
function goRight() {
var current = \$\$(".currentpic")[0];
var next = current.next();
if (next) {
setCurrentPic(next);
}
}
``````

# Solutions

## 1. Prime Factors

### factors.php

``````<?php
if (!isset(\$_GET['n'])) {
echo "Please provide a parameter n.";
} else {
\$factors = factorize(\$_GET["n"]);
echo implode(" * ", \$factors);
}

function factorize(\$num) {
\$factors = array();
for (\$factor = 2; \$factor <= \$num; \$factor++) {
while (\$num % \$factor == 0) {
\$num /= \$factor;
array_push(\$factors, \$factor);
}
}
return \$factors;
}
?>``````

``````<?php
if (\$_SERVER['REQUEST_METHOD'] == "GET") {
\$lines = explode("\n", \$file_text);
if (isset(\$_GET["name"])) {
\$name = \$_REQUEST["name"];
foreach (\$lines as \$line) {
\$info = explode(",", \$line);
if (\$info[0] == \$name) {
print \$info[1];
}
}
} else {
\$names = array();
foreach (\$lines as \$line) {
\$info = explode(",", \$line);
array_push(\$names, \$info[0]);
}
print(implode(\$names, ","));
}
} else {
\$name = \$_REQUEST["name"];
\$file_text .= "\n" . \$name . "," . \$address;
}
?>``````

## 3. Garbage Collector

### trashcan.js

``````document.observe('dom:loaded', function() {
createBrowsers();

accept: 'browser',
hoverclass: 'full',
onDrop: removeBrowser
});
});

function createBrowsers() {
for (var i = 0; i < 10; i++) {
var img = \$(document.createElement('img'));
img.src = 'http://webster.cs.washington.edu/cse190m/sections/8/trashcan/ie6.png';

positionRandomly(img);

new Draggable(img, {
revert: 'failure'
});

\$('browsers').appendChild(img);
}
}

function positionRandomly(elem) {
var x = parseInt(Math.random() * 472);
var y = parseInt(Math.random() * 172);
elem.style.left = x + "px";
elem.style.top = y + "px";
}

function removeBrowser(elem) {
new Effect.Puff(elem, {
duration: .5,
afterFinish: function() {
elem.remove();
}
});
}``````

## 4. Photo Gallery

### gallery.js

``````// CSE 190M, Spring 2009
// Section 8: Photo Gallery
// Sylvia Tashev

// set up basic event handlers
var pictures = \$\$("#pictures img");
for (var i = 0; i < pictures.length; i++) {
pictures[i].observe("dblclick", pictureClick);
}
\$("left").observe("click", goLeft);
\$("right").observe("click", goRight);

// set up initial pic and effects
setCurrentPic(pictures[0]);

// allows the user to rearrange the picture order
Sortable.create("pictures", {
tag: "img",
constraint: "horizontal"
});

// make all the thumbnails drag-and-droppable
for (var i = 0; i < pictures.length; i++) {
new Draggable(pictures[i], {revert: true});
}
});

// sets the current large picture to be the one from the given thumbnail
function setCurrentPic(pic) {
var oldCurrentPic = \$\$(".currentpic")[0];
if (oldCurrentPic != pic) {
// emphasize new current pic
new Effect.Scale(pic, 200);

\$("mainimage").src = pic.src.replace("_thumb", "");
\$("mainimage").shake();

if (oldCurrentPic) {
// de-emphasize old current pic
oldCurrentPic.removeClassName("currentpic");
new Effect.Scale(oldCurrentPic, 50);
}
}
}

// called when any picture is clicked  (or dragged into the main show area)
function pictureClick() {
setCurrentPic(this);
}

// called when Left arrow is clicked
function goLeft() {
var current = \$\$(".currentpic")[0];
var previous = current.previous();
if (previous) {
setCurrentPic(previous);
}
}

// called when Right arrow is clicked
function goRight() {
var current = \$\$(".currentpic")[0];
var next = current.next();
if (next) {
setCurrentPic(next);
}
}
``````