23 | ├── .gitignore ├── assets ├── images │ ├── page.png │ ├── pageEnd.png │ ├── doogleLogo.png │ ├── pageStart.png │ ├── icons │ │ └── search.png │ ├── pageSelected.png │ └── favicon │ │ ├── favicon.ico │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ └── android-chrome-512x512.png ├── js │ ├── script.js │ ├── masonry │ │ └── 4.2.2 │ │ │ └── masonry.pkgd.min.js │ └── fancybox │ │ └── 3.3.5 │ │ └── jquery.fancybox.min.js └── css │ ├── style.css │ └── fancybox │ └── 3.3.5 │ └── jquery.fancybox.min.css ├── ajax ├── setBroken.php ├── updateLinkCount.php └── updateImageCount.php ├── config.php ├── classes ├── DomDocumentParser.php ├── ImageResultsProvider.php ├── SiteResultsProvider.php └── Crawler.php ├── LICENSE ├── index.php ├── doogle-tables-no-data.sql ├── search.php ├── crawl-manual.php ├── crawl.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | config.php 3 | -------------------------------------------------------------------------------- /assets/images/page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/page.png -------------------------------------------------------------------------------- /assets/images/pageEnd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/pageEnd.png -------------------------------------------------------------------------------- /assets/images/doogleLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/doogleLogo.png -------------------------------------------------------------------------------- /assets/images/pageStart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/pageStart.png -------------------------------------------------------------------------------- /assets/images/icons/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/icons/search.png -------------------------------------------------------------------------------- /assets/images/pageSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/pageSelected.png -------------------------------------------------------------------------------- /assets/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/favicon/favicon.ico -------------------------------------------------------------------------------- /assets/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /assets/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /assets/images/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /assets/images/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safesploitOrg/doogle/HEAD/assets/images/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /ajax/setBroken.php: -------------------------------------------------------------------------------- 1 | prepare("UPDATE images SET broken = 1 WHERE imageUrl=:src"); 7 | $query->bindParam(":src", $_POST["src"]); 8 | 9 | $query->execute(); 10 | } 11 | else 12 | echo "No src passed to page"; //DEBUGGING 13 | ?> -------------------------------------------------------------------------------- /ajax/updateLinkCount.php: -------------------------------------------------------------------------------- 1 | prepare("UPDATE sites SET clicks = clicks + 1 WHERE id=:id"); 7 | $query->bindParam(":id", $_POST["linkId"]); 8 | 9 | $query->execute(); 10 | } 11 | else 12 | echo "No link passed to page"; //DEBUGGING 13 | ?> -------------------------------------------------------------------------------- /ajax/updateImageCount.php: -------------------------------------------------------------------------------- 1 | prepare("UPDATE images SET clicks = clicks + 1 WHERE imageUrl=:imageUrl"); 7 | $query->bindParam(":imageUrl", $_POST["imageUrl"]); 8 | 9 | $query->execute(); 10 | } 11 | else 12 | echo "No image URL passed to page"; //DEBUGGING 13 | ?> -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 13 | } 14 | catch(PDOExeption $e) 15 | { 16 | echo "Connection failed: " . $e->getMessage(); 17 | } 18 | ?> 19 | -------------------------------------------------------------------------------- /classes/DomDocumentParser.php: -------------------------------------------------------------------------------- 1 | '; 9 | 10 | $options = array( 11 | 'http'=>array('method'=>"GET", 'header'=>"User-Agent: doogleBot/0.1\n") 12 | ); 13 | $context = stream_context_create($options); 14 | $getConstants = file_get_contents($url, false, $context); 15 | 16 | $this->doc = new DomDocument('1.0', 'utf-8'); 17 | @$this->doc->loadHTML($html . $getConstants); 18 | //@ Error supression is unnecessary, PHP>7.0 supports HTML5 19 | } 20 | 21 | public function getlinks() 22 | { 23 | return $this->doc->getElementsByTagName("a"); 24 | } 25 | 26 | public function getTitleTags() 27 | { 28 | return $this->doc->getElementsByTagName("title"); 29 | } 30 | 31 | public function getMetaTags() 32 | { 33 | return $this->doc->getElementsByTagName("meta"); 34 | } 35 | 36 | public function getImages() 37 | { 38 | return $this->doc->getElementsByTagName("img"); 39 | } 40 | 41 | } 42 | ?> -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Zepher Ashe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
23 |
46 |
47 | $numResults results found
"; 94 | echo $resultsProvider->getResultsHtml($page, $pageSize, $term); 95 | ?> 96 |
102 |
123 | $currentPage
124 |
131 | $currentPage
132 |
133 |
144 |
7 |
8 |
65 |
66 |
67 |
68 | Doogle is now accessible via [localhost:8000](http://localhost:8000).
69 |
70 | For debugging phpMyAdmin has also been included on [localhost:8001](http://localhost:8001).
71 |
72 |
86 |
87 | ## PHP Dependencies
88 |
89 | mysql
90 | pdo_mysql
91 |
92 |
93 | ### SQL User Creation
94 |
95 | Amend the password _PASSWORD_HERE_ using a strong [random password](https://passwordsgenerator.net/).
96 |
97 | mysql> CREATE USER IF NOT EXISTS 'doogle'@'localhost' IDENTIFIED BY 'PASSWORD_HERE';
98 |
99 | ### SQL User Permissions
100 |
101 | The SQL user 'doogle' must have SELECT, INSERT and UPDATE privileges:
102 |
103 | mysql> GRANT SELECT, INSERT, UPDATE ON `doogle`.* TO 'doogle'@'localhost';
104 |
105 | - INSERT is used for crawling
106 | - SELECT is required for the search engine to return queries
107 | - UPDATE is required to amend the clicks and broken results (see ./ajax/)
108 |
109 | ## Connecting PHP to MySQL Server
110 |
111 | In the file config.php the following must be entered correctly for your database configuration:
112 |
113 | $dbname = "doogle";
114 | $dbhost = "localhost";
115 | $dbuser = "doogle";
116 | $dbpass = "";
117 |
118 | In the file 'doogle-tables-no-data.sql' the database will be created as 'doogle'.
119 |
120 | ## Crawling Websites to Populate Images and Sites tables
121 |
122 | ### Form-based crawl
123 |
124 | In your browser go to where the file is hosted http://localhost/crawl.php
125 |
126 | Paste the URL into the input field and press the Crawl button.
127 |
128 | ### Manual crawl
129 |
130 | At the bottom of crawl-manual.php the variable $startUrl is where to paste the URL of the website to be crawled:
131 |
132 | $startUrl = "https://thehackernews.com/";
133 |
134 | Then in your browser go to where the file is hosted http://localhost/crawl-manual.php
135 |
136 | ### Explanation
137 |
138 | The crawling process will take some time, it will completely depend on the size of the website being crawled.
139 | The page will continue to load (without output) until the `crawl.php` script finishes.
140 |
141 | Check the tables `images` and `sites` in the database to ensure they are being populated.
142 |
143 |
144 |
145 |
146 | Once the tables are populated visit the Doogle homepage and search!
147 | See preview images.
148 |
149 | # Programming Logic
150 |
151 | ## Pagination
152 |
153 | ### Logic of pagination system
154 | Inside search.php, pagination is implemented
155 |
156 |
157 |
158 | In the example above, currentPage=11.
159 | The number of pages to show is always 10.
160 |
161 | ### Results Per Page
162 |
163 | Site search will return 20 results per page and image search will return 30 results per page.
164 |
165 | The results per page can be changed inside search.php on lines {83, 88} respectively. As indicated by the $pageSize variables:
166 |
167 |
168 |
169 |
170 | ### Handling an edge case
171 |
172 | An edge case can occur when no more pages are available.
173 |
174 | So, for 331 results, **17 pages** will be available. However, without an edge case scenario consider, the UI for the pagination system will allow scrolling through pages which don't exist; which would return an empty result.
175 |
176 | To handle an edge case the following logic is implemented in the while-loop:
177 |
178 | if($currentPage + $pagesLeft > $numPages + 1)
179 | $currentPage = $numPages + 1 - $pagesLeft;
180 |
181 | while($pagesLeft != 0 && $currentPage <= $numPages)
182 | { ... }
183 |
184 |
185 | ## Image Search
186 |
187 | ### Image Captions
188 |
189 | To make image searches more informative, the 'alt' tag is part of the search term. As shown in ./classes/ImageResultsProvider.php line 34
190 |
191 |
192 |
193 |
194 | ### Loading Images with JavaScript
195 | In the 'images' table, there is a row 'broken' which tracks images which return an error.
196 |
197 | Because images are already loaded with a pure server-side solution, AJAX must be leveraged, loading images dynamically. Which is shown in ./assets/js/script.js
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 | ### Masonry
206 | Image searches are using [Masonry - Cascading grid layout library](https://masonry.desandro.com/).
207 |
208 | Masonry allows images a grid layout which is responsive due to jQuery.
209 | The image below shows an example layout:
210 |
211 |
212 |
213 |
214 |
215 | ## Site Search - Trimming Results
216 |
217 | As shown in the preview images, Doogle when performing a site search will return (title, URL and description) for each result.
218 |
219 | However, to make some results easier to read, a trimming process is performed. Inside ./classes/SiteResultsProvider.php the function trimField() is called:
220 |
221 |
222 |
223 |
224 |
225 | Title's are trimmed at 55 characters and description's are trimmed at 230 characters.
226 |
227 |
228 | ## Telemetry
229 |
230 | Both the 'images' and 'sites' tables in the database have a row containing 'clicks' for each column.
231 |
232 | The 'clicks' field is increased each time a site is visited or image is previewed.
233 |
234 | When performing a search, results returned are organised in descending order of clicks.
235 | This behaviour is shown by the $query inside ./classes/SiteResultsProvider.php function getResultsHtml(). See line 43.
236 |
237 |
238 |
239 |
240 | ## User-Agent
241 |
242 | Inside ./classes/DomDocumentParser.php the user-agent data used during crawling is located.
243 | As indicated on line 9:
244 |
245 |
246 |
247 |
248 | # Preview Images
249 | ## Doogle Homepage
250 |
251 |
252 |
253 | ## Doogle Search - Sites
254 |
255 |
256 |
257 | ## Doogle Search - Images
258 |
259 |
260 |
261 | ### Image Preview
262 |
263 | Image preview is done using Fancybox.
264 |
265 | The title, image URL and site URL are available on the bottom left corner.
266 |
267 |
268 |
269 |
270 |
271 | ## Pagination System
272 |
273 | Naturally, certain search terms may return many results like 'bbc'.
274 |
275 | To which Doogle only displays **20 sites** per page.
276 | At the bottom of the page, we can view the next 10 pages.
277 |
278 | ### Results Shown
279 |
280 |
281 |
282 | ### Bottom of Page
283 |
284 |
285 |
286 | ### Bottom of Page 13
287 |
288 |
289 |
290 | ## doogleBot Crawl Form
291 |
292 | An HTML form to submit a URL for crawling
293 |
294 |
295 |
296 | # Preview Video
297 |
298 | [Doogle Search demo - YouTube](https://youtu.be/clDt4Sg7ako)
299 |
--------------------------------------------------------------------------------
/assets/css/fancybox/3.3.5/jquery.fancybox.min.css:
--------------------------------------------------------------------------------
1 | body.compensate-for-scrollbar{overflow:hidden}.fancybox-active{height:auto}.fancybox-is-hidden{left:-9999px;margin:0;position:absolute!important;top:-9999px;visibility:hidden}.fancybox-container{-webkit-backface-visibility:hidden;backface-visibility:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;height:100%;left:0;position:fixed;-webkit-tap-highlight-color:transparent;top:0;-webkit-transform:translateZ(0);transform:translateZ(0);width:100%;z-index:99992}.fancybox-container *{box-sizing:border-box}.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-stage{bottom:0;left:0;position:absolute;right:0;top:0}.fancybox-outer{-webkit-overflow-scrolling:touch;overflow-y:auto}.fancybox-bg{background:#1e1e1e;opacity:0;transition-duration:inherit;transition-property:opacity;transition-timing-function:cubic-bezier(.47,0,.74,.71)}.fancybox-is-open .fancybox-bg{opacity:.87;transition-timing-function:cubic-bezier(.22,.61,.36,1)}.fancybox-caption,.fancybox-infobar,.fancybox-navigation .fancybox-button,.fancybox-toolbar{direction:ltr;opacity:0;position:absolute;transition:opacity .25s,visibility 0s linear .25s;visibility:hidden;z-index:99997}.fancybox-show-caption .fancybox-caption,.fancybox-show-infobar .fancybox-infobar,.fancybox-show-nav .fancybox-navigation .fancybox-button,.fancybox-show-toolbar .fancybox-toolbar{opacity:1;transition:opacity .25s,visibility 0s;visibility:visible}.fancybox-infobar{color:#ccc;font-size:13px;-webkit-font-smoothing:subpixel-antialiased;height:44px;left:0;line-height:44px;min-width:44px;mix-blend-mode:difference;padding:0 10px;pointer-events:none;text-align:center;top:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fancybox-toolbar{right:0;top:0}.fancybox-stage{direction:ltr;overflow:visible;-webkit-transform:translateZ(0);z-index:99994}.fancybox-is-open .fancybox-stage{overflow:hidden}.fancybox-slide{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:none;height:100%;left:0;outline:none;overflow:auto;-webkit-overflow-scrolling:touch;padding:44px;position:absolute;text-align:center;top:0;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;white-space:normal;width:100%;z-index:99994}.fancybox-slide:before{content:"";display:inline-block;height:100%;margin-right:-.25em;vertical-align:middle;width:0}.fancybox-is-sliding .fancybox-slide,.fancybox-slide--current,.fancybox-slide--next,.fancybox-slide--previous{display:block}.fancybox-slide--next{z-index:99995}.fancybox-slide--image{overflow:visible;padding:44px 0}.fancybox-slide--image:before{display:none}.fancybox-slide--html{padding:6px 6px 0}.fancybox-slide--iframe{padding:44px 44px 0}.fancybox-content{background:#fff;display:inline-block;margin:0 0 6px;max-width:100%;overflow:auto;padding:0;padding:24px;position:relative;text-align:left;vertical-align:middle}.fancybox-slide--image .fancybox-content{-webkit-animation-timing-function:cubic-bezier(.5,0,.14,1);animation-timing-function:cubic-bezier(.5,0,.14,1);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:transparent;background-repeat:no-repeat;background-size:100% 100%;left:0;margin:0;max-width:none;overflow:visible;padding:0;position:absolute;top:0;-webkit-transform-origin:top left;transform-origin:top left;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:99995}.fancybox-can-zoomOut .fancybox-content{cursor:zoom-out}.fancybox-can-zoomIn .fancybox-content{cursor:zoom-in}.fancybox-can-drag .fancybox-content{cursor:-webkit-grab;cursor:grab}.fancybox-is-dragging .fancybox-content{cursor:-webkit-grabbing;cursor:grabbing}.fancybox-container [data-selectable=true]{cursor:text}.fancybox-image,.fancybox-spaceball{background:transparent;border:0;height:100%;left:0;margin:0;max-height:none;max-width:none;padding:0;position:absolute;top:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%}.fancybox-spaceball{z-index:1}.fancybox-slide--html .fancybox-content{margin-bottom:6px}.fancybox-slide--iframe .fancybox-content,.fancybox-slide--map .fancybox-content,.fancybox-slide--video .fancybox-content{height:100%;margin:0;overflow:visible;padding:0;width:100%}.fancybox-slide--video .fancybox-content{background:#000}.fancybox-slide--map .fancybox-content{background:#e5e3df}.fancybox-slide--iframe .fancybox-content{background:#fff;height:calc(100% - 44px);margin-bottom:44px}.fancybox-iframe,.fancybox-video{background:transparent;border:0;height:100%;margin:0;overflow:hidden;padding:0;width:100%}.fancybox-iframe{vertical-align:top}.fancybox-error{background:#fff;cursor:default;max-width:400px;padding:40px;width:100%}.fancybox-error p{color:#444;font-size:16px;line-height:20px;margin:0;padding:0}.fancybox-button{background:rgba(30,30,30,.6);border:0;border-radius:0;cursor:pointer;display:inline-block;height:44px;margin:0;outline:none;padding:10px;transition:color .2s;vertical-align:top;width:44px}.fancybox-button,.fancybox-button:link,.fancybox-button:visited{color:#ccc}.fancybox-button:focus,.fancybox-button:hover{color:#fff}.fancybox-button.disabled,.fancybox-button.disabled:hover,.fancybox-button[disabled],.fancybox-button[disabled]:hover{color:#888;cursor:default}.fancybox-button svg{display:block;overflow:visible;position:relative;shape-rendering:geometricPrecision}.fancybox-button svg path{fill:transparent;stroke:currentColor;stroke-linejoin:round;stroke-width:3}.fancybox-button--pause svg path:nth-child(1),.fancybox-button--play svg path:nth-child(2){display:none}.fancybox-button--play svg path,.fancybox-button--share svg path,.fancybox-button--thumbs svg path{fill:currentColor}.fancybox-button--share svg path{stroke-width:1}.fancybox-navigation .fancybox-button{height:38px;opacity:0;padding:6px;position:absolute;top:50%;width:38px}.fancybox-show-nav .fancybox-navigation .fancybox-button{transition:opacity .25s,visibility 0s,color .25s}.fancybox-navigation .fancybox-button:after{content:"";left:-25px;padding:50px;position:absolute;top:-25px}.fancybox-navigation .fancybox-button--arrow_left{left:6px}.fancybox-navigation .fancybox-button--arrow_right{right:6px}.fancybox-close-small{background:transparent;border:0;border-radius:0;color:#555;cursor:pointer;height:44px;margin:0;padding:6px;position:absolute;right:0;top:0;width:44px;z-index:10}.fancybox-close-small svg{fill:transparent;opacity:.8;stroke:currentColor;stroke-width:1.5;transition:stroke .1s}.fancybox-close-small:focus{outline:none}.fancybox-close-small:hover svg{opacity:1}.fancybox-slide--iframe .fancybox-close-small,.fancybox-slide--image .fancybox-close-small,.fancybox-slide--video .fancybox-close-small{color:#ccc;padding:5px;right:-12px;top:-44px}.fancybox-slide--iframe .fancybox-close-small:hover svg,.fancybox-slide--image .fancybox-close-small:hover svg,.fancybox-slide--video .fancybox-close-small:hover svg{background:transparent;color:#fff}.fancybox-is-scaling .fancybox-close-small,.fancybox-is-zoomable.fancybox-can-drag .fancybox-close-small{display:none}.fancybox-caption{bottom:0;color:#fff;font-size:14px;font-weight:400;left:0;line-height:1.5;padding:25px 44px;right:0}.fancybox-caption:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAEtCAQAAABjBcL7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHRJREFUKM+Vk8EOgDAIQ0vj/3+xBw8qIZZueFnIKC90MCAI8DlrkHGeqqGIU6lVigrBtpCWqeRWoHDNqs0F7VNVBVxmHRlvoVqjaYkdnDIaivH2HqZ5+oZj3JUzWB+cOz4G48Bg+tsJ/tqu4dLC/4Xb+0GcF5BwBC0AA53qAAAAAElFTkSuQmCC);background-repeat:repeat-x;background-size:contain;bottom:0;content:"";display:block;left:0;pointer-events:none;position:absolute;right:0;top:-25px;z-index:-1}.fancybox-caption:after{border-bottom:1px solid hsla(0,0%,100%,.3);content:"";display:block;left:44px;position:absolute;right:44px;top:0}.fancybox-caption a,.fancybox-caption a:link,.fancybox-caption a:visited{color:#ccc;text-decoration:none}.fancybox-caption a:hover{color:#fff;text-decoration:underline}.fancybox-loading{-webkit-animation:a .8s infinite linear;animation:a .8s infinite linear;background:transparent;border:6px solid hsla(0,0%,39%,.5);border-radius:100%;border-top-color:#fff;height:60px;left:50%;margin:-30px 0 0 -30px;opacity:.6;padding:0;position:absolute;top:50%;width:60px;z-index:99999}@-webkit-keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fancybox-animated{transition-timing-function:cubic-bezier(0,0,.25,1)}.fancybox-fx-slide.fancybox-slide--previous{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.fancybox-fx-slide.fancybox-slide--next{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.fancybox-fx-slide.fancybox-slide--current{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}.fancybox-fx-fade.fancybox-slide--next,.fancybox-fx-fade.fancybox-slide--previous{opacity:0;transition-timing-function:cubic-bezier(.19,1,.22,1)}.fancybox-fx-fade.fancybox-slide--current{opacity:1}.fancybox-fx-zoom-in-out.fancybox-slide--previous{opacity:0;-webkit-transform:scale3d(1.5,1.5,1.5);transform:scale3d(1.5,1.5,1.5)}.fancybox-fx-zoom-in-out.fancybox-slide--next{opacity:0;-webkit-transform:scale3d(.5,.5,.5);transform:scale3d(.5,.5,.5)}.fancybox-fx-zoom-in-out.fancybox-slide--current{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}.fancybox-fx-rotate.fancybox-slide--previous{opacity:0;-webkit-transform:rotate(-1turn);transform:rotate(-1turn)}.fancybox-fx-rotate.fancybox-slide--next{opacity:0;-webkit-transform:rotate(1turn);transform:rotate(1turn)}.fancybox-fx-rotate.fancybox-slide--current{opacity:1;-webkit-transform:rotate(0deg);transform:rotate(0deg)}.fancybox-fx-circular.fancybox-slide--previous{opacity:0;-webkit-transform:scale3d(0,0,0) translate3d(-100%,0,0);transform:scale3d(0,0,0) translate3d(-100%,0,0)}.fancybox-fx-circular.fancybox-slide--next{opacity:0;-webkit-transform:scale3d(0,0,0) translate3d(100%,0,0);transform:scale3d(0,0,0) translate3d(100%,0,0)}.fancybox-fx-circular.fancybox-slide--current{opacity:1;-webkit-transform:scaleX(1) translateZ(0);transform:scaleX(1) translateZ(0)}.fancybox-fx-tube.fancybox-slide--previous{-webkit-transform:translate3d(-100%,0,0) scale(.1) skew(-10deg);transform:translate3d(-100%,0,0) scale(.1) skew(-10deg)}.fancybox-fx-tube.fancybox-slide--next{-webkit-transform:translate3d(100%,0,0) scale(.1) skew(10deg);transform:translate3d(100%,0,0) scale(.1) skew(10deg)}.fancybox-fx-tube.fancybox-slide--current{-webkit-transform:translateZ(0) scale(1);transform:translateZ(0) scale(1)}.fancybox-share{background:#f4f4f4;border-radius:3px;max-width:90%;padding:30px;text-align:center}.fancybox-share h1{color:#222;font-size:35px;font-weight:700;margin:0 0 20px}.fancybox-share p{margin:0;padding:0}.fancybox-share__button{border:0;border-radius:3px;display:inline-block;font-size:14px;font-weight:700;line-height:40px;margin:0 5px 10px;min-width:130px;padding:0 15px;text-decoration:none;transition:all .2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap}.fancybox-share__button:link,.fancybox-share__button:visited{color:#fff}.fancybox-share__button:hover{text-decoration:none}.fancybox-share__button--fb{background:#3b5998}.fancybox-share__button--fb:hover{background:#344e86}.fancybox-share__button--pt{background:#bd081d}.fancybox-share__button--pt:hover{background:#aa0719}.fancybox-share__button--tw{background:#1da1f2}.fancybox-share__button--tw:hover{background:#0d95e8}.fancybox-share__button svg{height:25px;margin-right:7px;position:relative;top:-1px;vertical-align:middle;width:25px}.fancybox-share__button svg path{fill:#fff}.fancybox-share__input{background:transparent;border:0;border-bottom:1px solid #d7d7d7;border-radius:0;color:#5d5b5b;font-size:14px;margin:10px 0 0;outline:none;padding:10px 15px;width:100%}.fancybox-thumbs{background:#fff;bottom:0;display:none;margin:0;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;padding:2px 2px 4px;position:absolute;right:0;-webkit-tap-highlight-color:transparent;top:0;width:212px;z-index:99995}.fancybox-thumbs-x{overflow-x:auto;overflow-y:hidden}.fancybox-show-thumbs .fancybox-thumbs{display:block}.fancybox-show-thumbs .fancybox-inner{right:212px}.fancybox-thumbs>ul{font-size:0;height:100%;list-style:none;margin:0;overflow-x:hidden;overflow-y:auto;padding:0;position:absolute;position:relative;white-space:nowrap;width:100%}.fancybox-thumbs-x>ul{overflow:hidden}.fancybox-thumbs-y>ul::-webkit-scrollbar{width:7px}.fancybox-thumbs-y>ul::-webkit-scrollbar-track{background:#fff;border-radius:10px;box-shadow:inset 0 0 6px rgba(0,0,0,.3)}.fancybox-thumbs-y>ul::-webkit-scrollbar-thumb{background:#2a2a2a;border-radius:10px}.fancybox-thumbs>ul>li{-webkit-backface-visibility:hidden;backface-visibility:hidden;cursor:pointer;float:left;height:75px;margin:2px;max-height:calc(100% - 8px);max-width:calc(50% - 4px);outline:none;overflow:hidden;padding:0;position:relative;-webkit-tap-highlight-color:transparent;width:100px}.fancybox-thumbs-loading{background:rgba(0,0,0,.1)}.fancybox-thumbs>ul>li{background-position:50%;background-repeat:no-repeat;background-size:cover}.fancybox-thumbs>ul>li:before{border:4px solid #4ea7f9;bottom:0;content:"";left:0;opacity:0;position:absolute;right:0;top:0;transition:all .2s cubic-bezier(.25,.46,.45,.94);z-index:99991}.fancybox-thumbs .fancybox-thumbs-active:before{opacity:1}@media (max-width:800px){.fancybox-thumbs{width:110px}.fancybox-show-thumbs .fancybox-inner{right:110px}.fancybox-thumbs>ul>li{max-width:calc(100% - 10px)}}
--------------------------------------------------------------------------------
/assets/js/masonry/4.2.2/masonry.pkgd.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Masonry PACKAGED v4.2.2
3 | * Cascading grid layout library
4 | * https://masonry.desandro.com
5 | * MIT License
6 | * by David DeSandro
7 | */
8 |
9 | !function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,r,a){function h(t,e,n){var o,r="$()."+i+'("'+e+'")';return t.each(function(t,h){var u=a.data(h,i);if(!u)return void s(i+" not initialized. Cannot call methods, i.e. "+r);var d=u[e];if(!d||"_"==e.charAt(0))return void s(r+" is not a valid method");var l=d.apply(u,n);o=void 0===o?l:o}),void 0!==o?o:t}function u(t,e){t.each(function(t,n){var o=a.data(n,i);o?(o.option(e),o._init()):(o=new r(n,e),a.data(n,i,o))})}a=a||e||t.jQuery,a&&(r.prototype.option||(r.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=o.call(arguments,1);return h(this,t,e)}return u(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=i)}var o=Array.prototype.slice,r=t.console,s="undefined"==typeof r?function(){}:function(t){r.error(t)};return n(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){i=i.slice(0),e=e||[];for(var n=this._onceEvents&&this._onceEvents[t],o=0;o{{ERROR}}