diff --git a/Yo/ajax/setBroken.php b/Yo/ajax/setBroken.php new file mode 100644 index 0000000..90f1cf8 --- /dev/null +++ b/Yo/ajax/setBroken.php @@ -0,0 +1,12 @@ +prepare("UPDATE images SET broken = 1 WHERE imageUrl=:src"); + $query->bindParam(":src", $_POST["src"]); + + $query->execute(); +} else { + echo "No src passed to page"; +} +?> \ No newline at end of file diff --git a/Yo/ajax/updateImageCount.php b/Yo/ajax/updateImageCount.php new file mode 100644 index 0000000..8edef6a --- /dev/null +++ b/Yo/ajax/updateImageCount.php @@ -0,0 +1,12 @@ +prepare("UPDATE images SET clicks = clicks + 1 WHERE imageUrl=:imageUrl"); + $query->bindParam(":imageUrl", $_POST["imageUrl"]); + + $query->execute(); +} else { + echo "No image URL passed to page"; +} +?> \ No newline at end of file diff --git a/Yo/ajax/updateLinkCount.php b/Yo/ajax/updateLinkCount.php new file mode 100644 index 0000000..f6715f3 --- /dev/null +++ b/Yo/ajax/updateLinkCount.php @@ -0,0 +1,12 @@ +prepare("UPDATE sites SET clicks = clicks + 1 WHERE id=:id"); + $query->bindParam(":id", $_POST["linkId"]); + + $query->execute(); +} else { + echo "No link passed to page"; +} +?> \ No newline at end of file diff --git a/Yo/assets/css/style.css b/Yo/assets/css/style.css new file mode 100644 index 0000000..e0697a7 --- /dev/null +++ b/Yo/assets/css/style.css @@ -0,0 +1,270 @@ +* { + font-family: arial, sans-serif; + color: #545454; +} + +html, +body { + margin: 0; + height: 100%; +} + +.wrapper { + display: flex; + flex-direction: column; + min-height: 100%; +} + +.wrapper.indexPage { + justify-content: center; +} + +.mainSection { + display: flex; + flex-direction: column; + align-items: center; +} + +.mainSection .searchContainer { + margin-top: 20px; + width: 100%; +} + +.mainSection .searchContainer form { + display: flex; + flex-direction: column; + align-items: center; +} + +.searchContainer .searchButton { + color: #757575; + background-color: #f5f5f5; + border: none; + height: 36px; + width: 125px; + border-radius: 1px; + font-size: 13px; + font-weight: bold; + margin-top: 20px; + cursor: pointer; + outline: none; +} + +.mainSection .searchContainer .searchBox { + border: none; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08); + height: 44px; + border-radius: 2px; + outline: none; + padding: 10px; + box-sizing: border-box; + font-size: 16px; + width: 70%; + max-width: 630px; + color: #000; +} + +.mainSection .logoContainer { + width: 220px; + text-align: center; +} + +.logoContainer img { + width: 100%; +} + +.header { + background-color: #FAFAFA; + border-bottom: 1px solid #ebebeb; +} + +.wrapper .headerContent { + display: flex; + align-items: center; +} + +.headerContent .logoContainer { + width: 150px; + padding: 5px 20px; + box-sizing: border-box; +} + +.headerContent .searchContainer { + flex: 1; +} + +.headerContent .searchContainer form { + margin: 15px 0 28px 0; +} + +.headerContent .searchBarContainer { + height: 44px; + border-radius: 2px; + background-color: #fff; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08); + width: 70%; + max-width: 630px; + box-sizing: border-box; + display: flex; +} + +.headerContent .searchBarContainer .searchBox { + flex: 1; + border: none; + background-color: transparent; + outline-color: #D0021B; + padding: 12px; + font-size: 16px; + color: #000; +} + +.headerContent .searchBarContainer .searchButton { + background-color: #fff; + height: 44px; + margin-top: 0; + width: 44px; + padding-right: 20px; + display: flex; + justify-content: center; +} + +.headerContent .searchBarContainer .searchButton img { + width: 22px +} + +.tabsContainer { + margin-left: 150px; +} + +.tabsContainer .tabList { + padding: 0; + margin: 0; +} + +.tabsContainer .tabList li { + display: inline-block; + padding: 0 16px 12px 16px; + color: #777; + font-size: 13px; +} + +.tabsContainer .tabList li a { + text-decoration: none; +} + +.tabsContainer .tabList li.active { + border-bottom: 3px solid #D0021B; +} + +.tabsContainer .tabList li.active a { + font-weight: bold; + color: #D0021B; +} + +.mainResultsSection { + flex: 1; +} + +.mainResultsSection .resultsCount { + font-size: 13px; + color: #808080; + margin-left: 150px; +} + +.mainResultsSection .siteResults { + margin-left: 150px; +} + +.resultContainer { + display: flex; + flex-direction: column; + margin-bottom: 26px; +} + +.resultContainer .title { + margin: 0; +} + +.resultContainer .title a { + color: #1a0dab; + text-decoration: none; + font-weight: normal; + font-size: 18px; +} + +.resultContainer .title a:hover { + text-decoration: underline; +} + +.resultContainer .url { + color: #006621; + font-size: 14px; +} + +.resultContainer .description { + font-size: 12px; +} + +.paginationContainer { + display: flex; + justify-content: center; + margin-bottom: 25px; +} + +.pageButtons { + display: flex; +} + +.pageNumberContainer img { + height: 37px; +} + +.pageNumberContainer, +.pageNumberContainer a { + display: flex; + flex-direction: column; + align-items: center; + text-decoration: none; +} + +.pageNumber { + color: #000; + font-size: 13px; +} + +a .pageNumber { + color: #D0021B; +} + +.mainResultsSection .imageResults { + margin: 20px; +} + +.gridItem { + position: relative; +} + +.gridItem img { + max-width: 200px; + min-width: 50px; + visibility: hidden; +} + +.gridItem .details { + visibility: hidden; + position: absolute; + bottom: 0px; + left: 0px; + width: 100%; + overflow: hidden; + background-color: rgba(0,0,0,0.8); + color: #fff; + font-size: 11px; + padding: 3px; + box-sizing: border-box; + white-space: nowrap; +} + +.gridItem:hover .details { + visibility: visible; +} \ No newline at end of file diff --git a/Yo/assets/images/icons/search.png b/Yo/assets/images/icons/search.png new file mode 100644 index 0000000..e2fa9f4 Binary files /dev/null and b/Yo/assets/images/icons/search.png differ diff --git a/Yo/assets/images/logo.png b/Yo/assets/images/logo.png new file mode 100644 index 0000000..ff92924 Binary files /dev/null and b/Yo/assets/images/logo.png differ diff --git a/Yo/assets/images/page.png b/Yo/assets/images/page.png new file mode 100644 index 0000000..a3a18b8 Binary files /dev/null and b/Yo/assets/images/page.png differ diff --git a/Yo/assets/images/pageEnd.png b/Yo/assets/images/pageEnd.png new file mode 100644 index 0000000..a3a18b8 Binary files /dev/null and b/Yo/assets/images/pageEnd.png differ diff --git a/Yo/assets/images/pageSelected.png b/Yo/assets/images/pageSelected.png new file mode 100644 index 0000000..58a0d31 Binary files /dev/null and b/Yo/assets/images/pageSelected.png differ diff --git a/Yo/assets/images/pageStart.png b/Yo/assets/images/pageStart.png new file mode 100644 index 0000000..e686e57 Binary files /dev/null and b/Yo/assets/images/pageStart.png differ diff --git a/Yo/assets/js/script.js b/Yo/assets/js/script.js new file mode 100644 index 0000000..f14d842 --- /dev/null +++ b/Yo/assets/js/script.js @@ -0,0 +1,90 @@ +var timer; + +$(document).ready(function() { + $(".result").on("click", function() { + var id = $(this).attr("data-linkId"); + var url = $(this).attr("href"); + + if(!id) { + alert("data-linkId attribute not found"); + } + + increaseLinkClicks(id, url); + + return false; + }); + + var grid = $(".imageResults"); + + grid.on("layoutComplete", function() { + $(".gridItem img").css("visibility", "visible"); + }); + + grid.masonry({ + itemSelector: ".gridItem", + columnWidth: 200, + gutter: 5, + isInitLayout: false + }); + + $("[data-fancybox]").fancybox({ + caption : function( instance, item ) { + var caption = $(this).data('caption') || ''; + var siteUrl = $(this).data('siteurl') || ''; + + if ( item.type === 'image' ) { + caption = (caption.length ? caption + '
' : '') + + 'View image
' + + 'Visit page'; + } + + return caption; + }, + afterShow : function( instance, item ) { + increaseImageClicks(item.src); + } + }); +}); + +function loadImage(src, className) { + var image = $(""); + + image.on("load", function() { + $("." + className + " a").append(image); + + clearTimeout(timer); + + timer = setTimeout(function() { + $(".imageResults").masonry(); + }, 500); + }); + + image.on("error", function() { + $("." + className).remove(); + + $.post("ajax/setBroken.php", {src: src}); + }); + + image.attr("src", src); +} + +function increaseLinkClicks(linkId, url) { + $.post("ajax/updateLinkCount.php", {linkId: linkId}) + .done(function(result) { + if(result != "") { + alert(result); + return; + } + window.location.href = url; + }); +} + +function increaseImageClicks(imageUrl) { + $.post("ajax/updateImageCount.php", {imageUrl: imageUrl}) + .done(function(result) { + if(result != "") { + alert(result); + return; + } + }); +} \ No newline at end of file diff --git a/Yo/classes/DomDocumentParser.php b/Yo/classes/DomDocumentParser.php new file mode 100644 index 0000000..ce3bc70 --- /dev/null +++ b/Yo/classes/DomDocumentParser.php @@ -0,0 +1,31 @@ +array('method'=>"GET", 'header'=>"User-Agent: YoBot/0.1\n") + ); + $context = stream_context_create($options); + + $this->doc = new DomDocument(); + @$this->doc->loadHTML(file_get_contents($url, false, $context)); + } + + public function getlinks() { + return $this->doc->getElementsByTagName("a"); + } + + public function getTitleTags() { + return $this->doc->getElementsByTagName("title"); + } + + public function getMetaTags() { + return $this->doc->getElementsByTagName("meta"); + } + + public function getImages() { + return $this->doc->getElementsByTagName("img"); + } + } +?> \ No newline at end of file diff --git a/Yo/classes/ImageResultsProvider.php b/Yo/classes/ImageResultsProvider.php new file mode 100644 index 0000000..cd145f9 --- /dev/null +++ b/Yo/classes/ImageResultsProvider.php @@ -0,0 +1,79 @@ +con = $con; + } + + public function getNumResults($term) { + $query = $this->con->prepare("SELECT COUNT(*) as total + FROM images + WHERE (title LIKE :term + OR alt LIKE :term) + AND broken=0"); + + $searchTerm = "%". $term . "%"; + $query->bindParam(":term", $searchTerm); + $query->execute(); + + $row = $query->fetch(PDO::FETCH_ASSOC); + return $row["total"]; + } + + public function getResultsHtml($page, $pageSize, $term) { + + $fromLimit = ($page - 1) * $pageSize; + + $query = $this->con->prepare("SELECT * + FROM images + WHERE (title LIKE :term + OR alt LIKE :term) + AND broken=0 + ORDER BY clicks DESC + LIMIT :fromLimit, :pageSize"); + + $searchTerm = "%". $term . "%"; + $query->bindParam(":term", $searchTerm); + $query->bindParam(":fromLimit", $fromLimit, PDO::PARAM_INT); + $query->bindParam(":pageSize", $pageSize, PDO::PARAM_INT); + $query->execute(); + + $resultsHtml = "
"; + + $count = 0; + while($row = $query->fetch(PDO::FETCH_ASSOC)) { + $count++; + $id = $row["id"]; + $imageUrl = $row["imageUrl"]; + $siteUrl = $row["siteUrl"]; + $title = $row["title"]; + $alt = $row["alt"]; + + if($title) { + $displayText = $title; + } else if($alt) { + $displayText = $alt; + } else { + $displayText = $imageUrl; + } + + $resultsHtml .= "
+ + + + + $displayText + +
"; + } + $resultsHtml .= "
"; + + return $resultsHtml; + } +} +?> \ No newline at end of file diff --git a/Yo/classes/SiteResultsProvider.php b/Yo/classes/SiteResultsProvider.php new file mode 100644 index 0000000..e5543bf --- /dev/null +++ b/Yo/classes/SiteResultsProvider.php @@ -0,0 +1,73 @@ +con = $con; + } + + public function getNumResults($term) { + $query = $this->con->prepare("SELECT COUNT(*) as total + FROM sites WHERE title LIKE :term + OR url LIKE :term + OR keywords LIKE :term + OR description LIKE :term"); + + $searchTerm = "%". $term . "%"; + $query->bindParam(":term", $searchTerm); + $query->execute(); + + $row = $query->fetch(PDO::FETCH_ASSOC); + return $row["total"]; + } + + public function getResultsHtml($page, $pageSize, $term) { + + $fromLimit = ($page - 1) * $pageSize; + + $query = $this->con->prepare("SELECT * + FROM sites WHERE title LIKE :term + OR url LIKE :term + OR keywords LIKE :term + OR description LIKE :term + ORDER BY clicks DESC + LIMIT :fromLimit, :pageSize"); + + $searchTerm = "%". $term . "%"; + $query->bindParam(":term", $searchTerm); + $query->bindParam(":fromLimit", $fromLimit, PDO::PARAM_INT); + $query->bindParam(":pageSize", $pageSize, PDO::PARAM_INT); + $query->execute(); + + $resultsHtml = "
"; + + while($row = $query->fetch(PDO::FETCH_ASSOC)) { + $id = $row["id"]; + $url = $row["url"]; + $title = $row["title"]; + $description = $row["description"]; + $title = $this->trimField($title, 55); + $description = $this->trimField($description, 230); + $resultsHtml .= "
+ +

+ + $title + +

+ $url + $description + +
"; + } + $resultsHtml .= "
"; + + return $resultsHtml; + } + + private function trimField($string, $characterLimit) { + $dots = strlen($string) > $characterLimit ? "..." : ""; + return substr($string, 0, $characterLimit) . $dots; + } +} +?> \ No newline at end of file diff --git a/Yo/config.php b/Yo/config.php new file mode 100644 index 0000000..a4126c0 --- /dev/null +++ b/Yo/config.php @@ -0,0 +1,10 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); +} catch(PDOExeption $e) { + echo "Connection failed: " . $e->getMessage(); +} +?> \ No newline at end of file diff --git a/Yo/crawl.php b/Yo/crawl.php new file mode 100644 index 0000000..d2de6f1 --- /dev/null +++ b/Yo/crawl.php @@ -0,0 +1,162 @@ +prepare("SELECT * FROM sites WHERE url = :url"); + + $query->bindParam(":url", $url); + $query->execute(); + + return $query->rowCount() != 0; +} + +function insertLink($url, $title, $description, $keywords) { + global $con; + + $query = $con->prepare("INSERT INTO sites(url, title, description, keywords) + VALUES(:url, :title, :description, :keywords)"); + + $query->bindParam(":url", $url); + $query->bindParam(":title", $title); + $query->bindParam(":description", $description); + $query->bindParam(":keywords", $keywords); + + return $query->execute(); +} + +function insertImage($url, $src, $alt, $title) { + global $con; + + $query = $con->prepare("INSERT INTO images(siteUrl, imageUrl, alt, title) + VALUES(:siteUrl, :imageUrl, :alt, :title)"); + + $query->bindParam(":siteUrl", $url); + $query->bindParam(":imageUrl", $src); + $query->bindParam(":alt", $alt); + $query->bindParam(":title", $title); + + return $query->execute(); +} + +function createLink($src, $url) { + $scheme = parse_url($url)["scheme"]; // http + $host = parse_url($url)["host"]; // www.wsj.com + + if(substr($src, 0, 2) == "//") { + $src = $scheme . ":" . $src; + } else if(substr($src, 0, 1) == "/") { + $src = $scheme . "://" . $host . $src; + } else if(substr($src, 0, 2) == "./") { + $src = $scheme . "://" . $host . dirname(parse_url($url)["path"]) . substr($src, 1); + } else if(substr($src, 0, 3) == "../") { + $src = $scheme . "://" . $host . "/" . $src; + } else if(substr($src, 0, 5) != "https" && substr($src, 0, 4) != "http") { + $src = $scheme . "://" . $host . "/" . $src; + } + + return $src; +} + +function getDetails($url) { + global $alreadyFoundImages; + $parser = new DomDocumentParser($url); + $titleArray = $parser->getTitleTags(); + + if(sizeof($titleArray) == 0 || $titleArray->item(0) == NULL) { + return; + } + + $title = $titleArray->item(0)->nodeValue; + $title = str_replace("\n", "", $title); + + if($title == "") { + return; + } + + $description = ""; + $keywords = ""; + + $metasArray = $parser->getMetatags(); + + foreach($metasArray as $meta) { + if($meta->getAttribute("name") == "description") { + $description = $meta->getAttribute("content"); + } + if($meta->getAttribute("name") == "keywords") { + $keywords = $meta->getAttribute("content"); + } + } + + $description = str_replace("\n", "", $description); + $keywords = str_replace("\n", "", $keywords); + + if(linkExists($url)) { + echo "$url already exists
"; + } else if(insertLink($url, $title, $description, $keywords)) { + echo "SUCCESS: $url
"; + } else { + echo "ERROR: Failed to insert $url
"; + } + + $imageArray = $parser->getImages(); + foreach($imageArray as $image) { + $src = $image->getAttribute("src"); + $alt = $image->getAttribute("alt"); + $title = $image->getAttribute("title"); + + if(!$title && !$alt) { + continue; + } + + $src = createLink($src, $url); + + if(!in_array($src, $alreadyFoundImages)) { + $alreadyFoundImages[] = $src; + // insert the image + echo "INSERT: " . insertImage($url, $src, $alt, $title); + } + } +} + +function followLinks($url) { + global $alreadyCrawled; + global $crawling; + $parser = new DomDocumentParser($url); + $linkList = $parser->getLinks(); + + foreach($linkList as $link) { + $href = $link->getAttribute("href"); + + if(strpos($href, "#") !== false) { + continue; + } else if(substr($href, 0, 11) == "javascript:") { + continue; + } + + $href = createLink($href, $url); + + if(!in_array($href, $alreadyCrawled)) { + $alreadyCrawled[] = $href; + $crawling[] = $href; + + getDetails($href); + } + } + + array_shift($crawling); + + foreach($crawling as $site) { + followLinks($site); + } +} + +$startUrl = "https://www.wsj.com/"; +followLinks($startUrl); +?> \ No newline at end of file diff --git a/Yo/index.php b/Yo/index.php new file mode 100644 index 0000000..3c2f0b5 --- /dev/null +++ b/Yo/index.php @@ -0,0 +1,36 @@ + + + + Yo! + + + + + + + + + + + +
+ +
+
+ Site logo +
+ +
+
+ + + + +
+
+
+ +
+ + + \ No newline at end of file diff --git a/Yo/search.php b/Yo/search.php new file mode 100644 index 0000000..3a01de7 --- /dev/null +++ b/Yo/search.php @@ -0,0 +1,157 @@ + + + + + + Yo!What's up? + + + + + + + +
+ +
+ +
+ +
+ + + +
+ +
+ +
+ +
+ + + +
+ +
+ +
+ +
+ + +
+ + + + +
+
+ +
+ + getNumResults($term); + + echo "

$numResults results found

"; + echo $resultsProvider->getResultsHtml($page, $pageSize, $term); + ?> + +
+ +
+ +
+ +
+ +
+ + $numPages + 1) { + $currentPage = $numPages + 1 - $pagesLeft; + } + + while($pagesLeft != 0 && $currentPage <= $numPages) { + + if($currentPage == $page) { + echo "
+ + $currentPage +
"; + } + else { + echo ""; + } + + + $currentPage++; + $pagesLeft--; + + } + ?> + +
+ +
+ +
+
+ + + + + \ No newline at end of file