<!DOCTYPE html>
<html ng-app="app">
  <head>
    <script data-require="angular.js@1.4.0-rc.1" data-semver="1.4.0-rc.1" src="https://code.angularjs.org/1.4.0-rc.1/angular.js"></script>
    <script data-require="angular-route@1.4.0-rc.1" data-semver="1.4.0-rc.1" src="https://code.angularjs.org/1.4.0-rc.1/angular-route.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>
  <body>
    <ng-include src="'nav.html'"></ng-include>
    <div ng-view></div>
  </body>
</html>

/*
 *  Define app and configure routes
 */
angular.module('app', ['ngRoute']).config(routeConfig);

function routeConfig($routeProvider){
  $routeProvider.
    when('/articles/new', {
      templateUrl: 'edit-article.html',
      controller: 'EditArticleCtrl',
      controllerAs: 'editor'
    }).
    when('/articles', {
      templateUrl: 'articles-list.html',
      controller: 'ArticlesListCtrl',
      controllerAs: 'list'
    }).
    when('/articles/:articleId', {
      templateUrl: 'edit-article.html',
      controller: 'EditArticleCtrl',
      controllerAs: 'editor'
    }).
    otherwise({
      redirectTo: '/articles'
    });
}

/*
 *  Articles list controller
 */
angular.module('app').controller('ArticlesListCtrl', ArticlesListCtrl);

ArticlesListCtrl.$inject = ['$scope', 'articlesApiSvc'];

function ArticlesListCtrl($scope, articlesApiSvc) {
  var list = this;
  
  list.articles = articlesApiSvc.articles;
  // list.articles is now bound to articlesApiSvc.articles array
  // when the api methods modify that variable, list.articles will be updated
  
  articlesApiSvc.getArticles();
  
  list.removeArticle = articlesApiSvc.deleteArticle;
  list.saveArticle = articlesApiSvc.saveArticle;
}

/*
 *  Edit article controller
 */
angular.module('app').controller('EditArticleCtrl', EditArticleCtrl);

EditArticleCtrl.$inject = ['$scope', '$routeParams', '$location', 'articlesApiSvc'];

function EditArticleCtrl($scope, $routeParams, $location, articlesApiSvc) {
  var editor = this;
  
  var id = $routeParams.articleId;
  
  if (id) {
    articlesApiSvc.selectArticle(id);
  } else {
    articlesApiSvc.newArticle();
  }
  
  editor.article = articlesApiSvc.selected;
  editor.save = save;
  
  function save() {
    editor.article.lastSaved = new Date();
    articlesApiSvc.saveArticle();
  }
}

/*
 *  Articles API service
 */
angular.module('app').factory('articlesApiSvc', articlesApiSvc);

articlesApiSvc.$inject = ['$http'];

function articlesApiSvc($http) {
  var articles = [];
  var selected = {};
  
  return {
    articles: articles,
    selected: selected,
    getArticles: getArticles,
    selectArticle: selectArticle,
    saveArticle: saveArticle,
    newArticle: newArticle,
    deleteArticle: deleteArticle
  };
  
  function getArticles() {
    if(articles.length) { return true; } // don't fetch them twice (for demo purposes)
    
    return $http.get('articles.json').then(function(response){
      // angular.copy is used so as not to break the binding
      // i.e. articles is mutated not pointed to a new array
      angular.copy(response.data, articles);
      //articles = response.data; // <-- won't work
    });
  }

  function selectArticle(id){
    angular.copy(articles[id], selected);
  }
  
  function saveArticle(){
    // an actual http POST request would be made first
    angular.copy(selected, articles[selected.id]);
  }
  
  function newArticle() {
    var newId = articles.length;
    articles.push({id: newId, title: 'New Article', body: 'Text', image: ''});
    selectArticle(newId);
  }
  
  function deleteArticle(id) {
    articles[id].status = 'deleted';
  }
}
/* Styles go here */

html, body {
  font-family: Arial;
  padding: 0; 
  margin: 0;
}

div {
  box-sizing: border-box;
}

navigation ul {
  background: black;
  margin: 0;
  padding: 1em 0;
}

navigation li {
  list-style-type: none;
  display: inline;
  padding: 0 1em;
  color: white;
}

navigation a {
  color: white;
  text-decoration: none;
}

navigation a:hover {
  text-decoration: underline;
}

header h1 {
  color: white;
  background: red;
  margin: 0;
  padding: 14px;
  font-style: italic;
  font-size: 48px;
}

.list-of-articles {
-webkit-columns: auto 2;
   -moz-columns: auto 2;
        columns: auto 2;
        
        padding: 1em;
        background: #eee;
        margin: 0;
}

.articles-filter {
  padding: 0.5em;
  background: silver;
  border-bottom: solid 1px gray;
  font-size: 12px;
}
.article--in-list {
  margin: 0;
  margin-bottom: 1em;
  list-style: none;
  -webkit-column-break-inside: avoid;
          page-break-inside: avoid;
               break-inside: avoid;
}

.list-tile, .article-preview {
  border: solid 1px gray;
  padding: 1em;
  overflow: auto;
  background: white;
}

.list-tile img, .article-preview img {
  float: left;
  width: 33%;
}

.list-tile h2, .article-preview h2 {
  margin: 0;
  padding: 0;
}

.list-tile__text, .article-preview__text {
  float: left;
  overflow: auto;
  max-width: 66%;
  padding-left: 1em;
}

.delete-link, .delete-link:visited, .delete-link:active {
  color: red;
}

.article-preview-wrapper {
  width: 66%;
  float: left;
  margin-top: 1em;
}

.editor-form {
  float: left;
  max-width: 33%;
  padding: 1em;
  margin-top: 1em;
}

p:focus, h2:focus {
  background: yellow;
  outline: solid 0px transparent;
}
[
  {
    "id": 0, 
    "title": "Protection Jacket", 
    "body": "It's a jungle out there - but you can survive if you're dressed not to be killed.", 
    "image": "http://unsplash.it/200/200?image=99", 
    "status": "published"
  },
  {
    "id": 1, 
    "title": "Plot to kill corrupt CID", 
    "body": "Four allegedly corrupt CID officers were the target of a rocket attack plot by two best friends, the Digger can reveal.", 
    "image": "http://unsplash.it/200/200?image=100", 
    "status": "published"
  },
  {
    "id": 2, 
    "title": "Police state tactics exposed", 
    "body": "Sean Clerkin says his anti-poverty group were harassed and intimidated by cops.", 
    "image": "http://unsplash.it/200/200?image=101", 
    "status": "published"
  },
  {
    "id": 3, 
    "title": "Smurphy knew too much", 
    "body": "Pals claim he was on the run from the cops but close friends and family say he was bundled into a van and dumped in the canal.", 
    "image": "http://unsplash.it/200/200?image=102", 
    "status": "published"
  },
  {
    "id" : 4,
    "title" : "The Proof",
    "body" : "The Digger exposes the lie police peddled, claiming they busted dealers in the Fox's house in the East end.",
    "image" : "http://unsplash.it/200/200?image=103",
    "status" : "published"
  },
  {
    "id" : 5,
    "title" : "Violent Attacks Linked To Suspect Killer",
    "body" : "Two assaults have been linked to cops probing a man being questioned about a murder.",
    "image" : "http://unsplash.it/200/200?image=104",
    "status" : "published"
  }
]
<div class="articles-filter">
  <label for="name">Title: </label><input name="search" type="text" ng-model="list.search.title">
  <label for="name">Body: </label><input name="search" type="text" ng-model="list.search.body">
  <select ng-init="list.search.status = 'published'" id="status" name="status" ng-model="list.search.status">
    <option value="published" selected>Published</option>
    <option value="deleted">Deleted</option>
    <option value="draft">Draft</option>
  </select>
</div>
<ul class="list-of-articles">
  <li class="article--in-list" ng-repeat="article in list.articles | filter:list.search track by article.id">
    <div class="list-tile">
      <img ng-src="{{article.image}}" />
      <div class="list-tile__text">
        <h2 ng-model="article.title" contenteditable>{{article.title}}</h2>
        <p ng-model="article.body" contenteditable>{{article.body}}</p>
        <p>
          <a href="" ng-click="list.saveArticle(article.id)" >Save</a>&nbsp;
          <a href="#/articles/{{article.id}}" >Edit</a>&nbsp;
          <a class="delete-link" href="" ng-click="list.removeArticle(article.id)">Delete</a>
        </p>
      </div>
    </div>
  </li>
</ul>
<div class="article-preview-wrapper">
  <div class="article-preview">
    <img ng-src="{{editor.article.image}}" />
    <div class="article-preview__text">
      <h2>{{editor.article.title}}</h2>
      <p>{{editor.article.body}}</p>
    </div>
  </div>
</div>
<div class="editor-form">
<form ng-submit="editor.save()">
  <table>
    <tbody>
      <tr><td><label for="title">Title</label></td>
      <td><input name="title" type="text" ng-model="editor.article.title"></td></tr>
      <tr><td><label for="body">Body</label></td>
      <td><textarea name="body" ng-model="editor.article.body"></textarea></td></tr>
      <tr><td><label for="image">Image</label></td>
      <td><input name="image" type="text" ng-model="editor.article.image" ng-model-options="{updateOn: 'blur'}"></td></tr>
      <tr>
        <td>Status: </td>
        <td>
          <select ng-init="editor.article.status = 'published'" ng-model="editor.article.status">
            <option value="published">Published</option>
            <option value="draft">Draft</option>
            <option value="deleted">Deleted</option>
          </select>
        </td>
      </tr>
    </tbody>
  </table>
  <button action="submit">Save</button>
  <h6>Last saved: {{editor.article.lastSaved | date:'yyyy-MM-dd HH:mm:ss'}}</h6>
  <p><a href="#/articles">Back to list</a></p>
</form>
</div>
<header>
  <h1>The Digger CMS!</h1>
</header>
<navigation>
  <ul>
    <li><a href="#/articles/new">New article</a></li>
    <li><a href="#/articles">Articles list</a></li>
  </ul>
</navigation>