1"use strict"; 2 3var dom = React.DOM; 4 5var IssueList = React.createClass({ 6 getInitialState: function() { 7 return { onlyPRs: false, 8 filter: '', 9 issues: [] }; 10 }, 11 12 togglePRs: function() { 13 this.setState({ onlyPRs: !this.state.onlyPRs }); 14 }, 15 16 applyFilter: function(value) { 17 this.setState({ filter: value }); 18 }, 19 20 componentDidMount: function() { 21 var issues = []; 22 23 if (window.CORDOVA_STATUS) { 24 issues = issues.concat(CORDOVA_STATUS.issues.map(function(issue) { 25 issue.created_at = new Date(issue.created_at); 26 issue.updated_at = new Date(issue.updated_at); 27 issue.origin = 'github'; 28 issue.assigneeName = issue.assignee && issue.assignee.login; 29 issue.assigneeAvatar = issue.assignee && issue.assignee.avatar_url; 30 return issue; 31 })); 32 33 } 34 35 if (window.JIRA_ISSUES) { 36 issues = issues.concat(JIRA_ISSUES.issues.map(function(issue) { 37 issue.created_at = new Date(issue.fields.created); 38 issue.updated_at = new Date(issue.fields.updated); 39 issue.title = issue.fields.summary; 40 issue.html_url = 'https://issues.apache.org/jira/browse/' + issue.key; 41 issue.origin = 'jira'; 42 issue.status = issue.fields.status.name; 43 issue.assigneeName = issue.fields.assignee && issue.fields.assignee.name; 44 issue.assigneeAvatar = issue.fields.assignee && issue.fields.assignee.avatarUrls["32x32"]; 45 return issue; 46 })); 47 } 48 49 this.setState({ issues: issues }); 50 }, 51 52 render: function() { 53 var s = this.state; 54 var issues = Array.prototype.slice.call(this.state.issues); 55 issues.sort(function(a, b) { 56 return a.updated_at < b.updated_at ? 1 : -1; 57 }); 58 59 return dom.div( 60 null, 61 Toolbar({ togglePRs: this.togglePRs, 62 onlyPRs: s.onlyPRs, 63 onFilter: this.applyFilter, 64 filter: this.state.filter.toLowerCase() }), 65 dom.div( 66 { className: 'issues list-group' }, 67 issues.map(function(issue) { 68 if (s.onlyPRs && (!issue.pull_request || !issue.pull_request.html_url)) { 69 return null; 70 } 71 72 if (s.filter && 73 issue.title.toLowerCase().indexOf(s.filter) === -1 && 74 (!issue.repo || issue.repo.toLowerCase().indexOf(s.filter) === -1)) { 75 return null; 76 } 77 78 return dom.a( 79 { 80 className: 'issue list-group-item', 81 href: issue.html_url 82 }, 83 dom.i({ className: "icon " + issue.origin }), 84 dom.span({className: "updated-at"}, moment(issue.updated_at).format('MM-DD-YYYY')), 85 dom.span(null, issue.title), 86 issue.repo ? 87 dom.span({ className: "label label-default" }, issue.repo) : 88 dom.span({ className: "label label-info" }, issue.status), 89 (issue.pull_request && issue.pull_request.html_url) ? 90 dom.span({ className: "label label-danger" }, 'PR') : null, 91 issue.assigneeName ? 92 dom.span(null, 93 dom.span(null, "Assigned to:"), 94 dom.img({className: "avatar", src: issue.assigneeAvatar}), 95 issue.assigneeName) : 96 null 97 ); 98 }.bind(this)) 99 ) 100 ); 101 } 102}); 103 104var RepoList = React.createClass({ 105 getInitialState: function() { 106 return { issues: [], 107 repos: [] }; 108 }, 109 110 componentDidMount: function() { 111 this.setState({ repos: window.CORDOVA_STATUS.repos || [] }); 112 }, 113 114 render: function() { 115 var repos = this.state.repos.sort(function(a, b) { 116 return a.repo.localeCompare(b.repo); 117 }); 118 119 return dom.div( 120 null, 121 repos.map(function(repo) { 122 var nodes = [], 123 prs = []; 124 125 nodes.push(dom.div({ className: '5u status-table-name'}, 126 dom.a({ href: "http://github.com/apache/" + repo.repo }, 127 repo.repo) 128 )); 129 130 if (repo.apachePullRequests && repo.apachePullRequests.length > 0) { 131
132 repo.apachePullRequests.forEach(function(pr) { 133 prs.push( 134 dom.div({ className: 'truncate'}, 135 dom.a({href: pr.user_url}, pr.user), 136 ': ', 137 dom.a({href: pr.url, title: pr.title}, pr.title) 138 )); 139 }); 140 } 141 142 if (repo.mentionsPullRequests && repo.mentionsPullRequests.length > 0) { 143 144 prs.push(dom.span(null, 'Firefox mentions:')); 145 146 repo.mentionsPullRequests.forEach(function(pr) { 147 prs.push( 148 dom.div({ className: 'truncate'}, 149 dom.a({href: pr.user_url}, pr.user), 150 ': ', 151 dom.a({href: pr.url, title: pr.title}, pr.title) 152 )); 153 }); 154 } 155 156 nodes.push(dom.div({ className: '7u status-table-status'}, prs.length && prs || dom.span(null, 'none'))); 157 158 return dom.div({ className: 'row flush list-group-item' }, nodes); 159 }) 160 ); 161 } 162}); 163 164var Toolbar = React.createClass({ 165 onFilter: function(e) { 166 this.props.onFilter(e.target.value); 167 }, 168 169 render: function() { 170 return dom.div( 171 { className: 'clearfix toolbar form-inline' }, 172 dom.div({ className: 'col-sm-6 form-group' }, 173 dom.label(null, 'Filter:'), 174 dom.input({ className: 'form-control', 175 value: this.props.filter, 176 onChange: this.onFilter })), 177 dom.div({ className: 'col-sm-6' }, 178 dom.button({ className: 'btn ' + (this.props.onlyPRs ? 'btn-warning' : 'btn-info'), 179 onClick: this.props.togglePRs }, 'Only PRs')) 180 ); 181 } 182}); 183 184React.renderComponent(RepoList(), document.getElementById('repos')); 185React.renderComponent(IssueList(), document.getElementById('issues'));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.