JSFiddle - React, Tailwind, and code Playground

MapleStory News

by MegaScience

HTML

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-sanitize.js"></script>
<input ng-model="filterText" id="search" class="form-control" placeholder="Filter text">
<select ng-model="pageSize" id="pageSize" class="form-control">
  <option value="5">5</option>
  <option value="10">10</option>
  <option value="15">15</option>
  <option value="20">20</option>
</select>
<button ng-disabled="currentPage == 0" ng-click="currentPage = currentPage - 1">Previous</button>
{{currentPage+1}}/{{numberOfPages()}}
<button ng-disabled="currentPage >= getData().length/pageSize - 1" ng-click="currentPage = currentPage + 1">Next</button>
<button ng-disabled="loading" ng-click="getNews(true)">Recent</button>
<button ng-disabled="loading" ng-click="getNews(false)">All</button>
<div id="newsContainer">
  <div ng-repeat="news in newsPosts | filter:filterText | startFrom:currentPage*pageSize | limitTo:pageSize" class="{{news.Category}}" ng-click="openPage(news.Id)">
    <div class="photo" ng-style="{'background-image' : 'url(' + (news.ImageThumbnail || news.ImageThumbnail2) + ')'}">
      <img class="shim" src="http://nxcache.nexon.net/maplestory/assets-new/img/shim_16x9.png">
      <div class="label">{{news.Category}}</div>
    </div><span>{{convertTime(news.LiveDate)}}</span>
    <h3>{{news.Title}}</h3><div>{{news.Summary}}</div></div>
</div>
<input id="toggle" name="toggle" type="checkbox" ng-model="toggleWebpage" />
<div id="webpage">
  <div id="webpageContent" ng-bind-html="webpage"></div>
  <label for="toggle">X</label>
</div>

CSS

body {
  font-family: Arial, Helvetica, sans-serif;
}

input#toggle {
  position: fixed;
  left: -9999px;
}

label[for="toggle"] {
  position: absolute;
  right: 0;
  top: 0;
  width: 1.3em;
  height: 1.3em;
  box-sizing: border-box;
  border: 1px dotted black;
  background-color: #2980B9;
  color: white;
  text-align: center;
}

input#toggle:not(:checked) + div#webpage {
  display: none;
}

div#webpage {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  height: 80%;
  width: 80%;
  background-color: white;
  border: 1px solid black;
  padding: 5px;
}

div#webpageContent {
  height: 100%;
  width: 100%;
  overflow-y: scroll;
}

div#webpageContent img {
  max-width: 100%;
}

div#newsContainer > div {
  border: 1px solid gray;
  box-sizing: border-box;
  float: left;
  clear: both;
  margin: 3px;
  padding-right: 3px;
  width: 100%;
}

div#newsContainer > div span {
  float: right;
}

div#newsContainer h3 {
  margin-top: 0;
}


/*div#newsContainer img {
  float: left;
  padding-right: 5px;
  max-width: 150px;
}*/

div#newsContainer div.photo {
  float: left;
  width: 20%;
  margin: 0 3% 0 0;
  position: relative;
  background-position: 50% 50%;
  background-repeat: no-repeat;
  background-size: cover;
}

div#newsContainer div.photo img {
  width: 100%;
}

div#newsContainer div.photo div.label {
  position: absolute;
  bottom: 0;
  left: 0;
  padding: 2px 12px;
  font-size: 14px;
  font-weight: 500;
  font-family: "Titillium Web", Arial, sans-serif;
  color: #fff;
  text-transform: uppercase;
}

div#newsContainer div.general {
  border-left: 3px solid #7519ff;
  background-color: rgba(117, 25, 255, 0.1);
}

div#newsContainer div.general div.photo div.label {
  background-color: #7519ff;
}

div#newsContainer div.events {
  border-left: 3px solid #7acca3;
  background-color: rgba(122, 204, 163, 0.1);
}

div#newsContainer div.events div.photo div.label {
  background-color: #7acca3;
}

div#newsContainer div.sale {
  border-left: 3px...

JavaScript

const newsApp = angular.module('newsApp', ['ngSanitize']);
newsApp.controller('newsController', ['$scope', '$filter', '$http', '$sce', mainController]);

function mainController($scope, $filter, $http, $sce) {
    const gameID = 10100;
    const lang = 'en-US';
    const region = '1';
    // https://gist.github.com/jesperorb/6ca596217c8dfba237744966c2b5ab1e#bypass-client-side-for-production
    const sites = {
        cors: ['https://cors.io/?', 'https://crossorigin.me/', 'https://cors-anywhere.herokuapp.com/'],
        corsIndex: 0,
        currentEndpoint: 'new',
        endpoints: {
            old: {
                recentURL: `http://nxl.nxfs.nexon.com/news/${gameID}/${lang}/recent.json`,
                newsURL: `http://nxl.nxfs.nexon.com/news/${gameID}/${lang}/list.json`,
                newsPage: 'http://nxl.nxfs.nexon.com/news/items/'
            },
            new: {
                recentURL: `https://nxl.nxfs.nexon.com/news/regions/${region}/${gameID}/${lang}/recent.json`,
                newsURL: `https://nxl.nxfs.nexon.com/news/regions/${region}/${gameID}/${lang}/list.json`,
                newsPage: 'https://nxl.nxfs.nexon.com/news/items/'
            }
        }
    };
    const getCORS = index => sites.cors[typeof index !== 'undefined' ? index : sites.corsIndex];
    const cycleCORS = () => {
        if (++sites.corsIndex >= sites.cors.length) return 'END'; //sites.corsIndex = 0;
    };
    const errorHandler = (response, cb) => {
        console.error(response);
        if (cycleCORS() !== 'END') cb();
        else {
            alert('CORS could not be resolved.');
            $scope.loading = false;
        }
    };
    const setNews = data => {
        $scope.loading = false;
        $scope.newsPosts = typeof data === 'undefined' ? [] : data;
    };
    // Pagination: https://codepen.io/khilnani/pen/qEWojX
    $scope.currentPage = 0;
    $scope.pageSize = 10;
    $scope.filterText = '';

    let lastWebpage = '';
    $scope.newsPosts = [];
  ...